/* Copyright 1997 Acorn Computers Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /***************************************************/ /* File : SaveFile.c */ /* */ /* Purpose: Handle the Save File dialogue (actual */ /* file saving is done in Save.c). Relies */ /* on there being only one Save File */ /* dialogue open at a time (it is a */ /* shared object). */ /* */ /* Author : A.D.Hodgkinson */ /* */ /* History: 03-Sep-97: Created. */ /* */ /* 08-Dec-97: Added code for option or */ /* radio buttons and auto */ /* widthing of the dialogue. */ /***************************************************/ #include <stdlib.h> #include <string.h> #include "swis.h" #include "HTMLLib.h" /* HTML library API, Which will include html2_ext.h, tags.h and struct.h */ #include "wimp.h" #include "wimplib.h" #include "event.h" #include "toolbox.h" #include "window.h" #include "gadgets.h" #include "svcprint.h" #include "Global.h" #include "MiscDefs.h" #include "Utils.h" #include "Browser.h" #include "Fetch.h" /* (For ISLINK macro) */ #include "Filetypes.h" #include "History.h" #include "Hotlist.h" #include "Images.h" #include "Menus.h" #include "MimeMap.h" #include "Object.h" #include "Protocols.h" #include "Save.h" #include "SaveDraw.h" #include "SaveText.h" #include "Toolbars.h" #include "URLutils.h" #include "Windows.h" #include "SaveFile.h" /* Local statics */ static browser_data * savefile_browser = NULL; static HStream * savefile_token = NULL; static int savefile_type = 0x000; static savefile_open_reason savefile_reason = save_as_html; static ObjectId window_id = 0; static ComponentId parent_component = -1; static ObjectId ancestor_id = 0; /* These are to remember the state of the 'alternative' */ /* selector (option button or radio buttons) for when */ /* the Save File dialogue is opened for a particular */ /* parent component. The 'mhso' etc. represent the first */ /* letters of the component name from Menus.h (so in the */ /* above example, mhso = MiscHotlistSaveObject). Where */ /* several components lead to the same section of code */ /* in a 'switch' statement, the name of the first */ /* as appearing in savefile_to_be_shown is used. */ #ifndef REMOTE_HOTLIST static int alt_mhso = 0; /* MiscHotlistSaveObject */ #endif static int alt_ead = 0; /* ExportAsDraw */ static int alt_fsfl = 0; /* FrameSaveFrameLocation */ static int alt_el = 0; /* ExportLink */ static int alt_ep = 0; /* ExportPicture */ static int alt_eb = 0; /* ExportBackground */ /* Static function prototypes */ static int savefile_drag_ended (int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle); static int savefile_ok (int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle); static int savefile_cancel (int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle); static _kernel_oserror * savefile_switch_to_normal (ObjectId window); static _kernel_oserror * savefile_switch_to_option (ObjectId window); static _kernel_oserror * savefile_switch_to_radios (ObjectId window); static _kernel_oserror * savefile_set_items (ObjectId window, int selected); static int savefile_option_changed (int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle); static int savefile_radio_changed (int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle); static int savefile_item_changed (IdBlock * idb); static _kernel_oserror * savefile_text_uri_or_url (ObjectId window); static _kernel_oserror * savefile_toggle_uri_or_url (ObjectId window); static _kernel_oserror * savefile_text_sprite_or_original (ObjectId window); static _kernel_oserror * savefile_toggle_sprite_or_original (ObjectId window, const char * src); static _kernel_oserror * savefile_text_without_backgrounds_or_with (ObjectId window); static _kernel_oserror * savefile_toggle_without_backgrounds_or_with (ObjectId window); static _kernel_oserror * savefile_set_correct_extent (ObjectId window, BBox * ra1); static _kernel_oserror * savefile_auto_width (ObjectId window); /*************************************************/ /* savefile_open_for() */ /* */ /* Creates and opens a Save File dialogue for a */ /* given browser, opening near the pointer. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* which is the ancestor of the */ /* dialogue; */ /* */ /* A savefile_open_reason describing */ /* why the dialogue is being opened */ /* (see SaveFile.h). */ /*************************************************/ _kernel_oserror * savefile_open_for(browser_data * b, savefile_open_reason reason) { ObjectId id; ComponentId pc; /* Create the dialogue - as this is shared object it */ /* will not be recreated if it already exists, but */ /* it's more efficient to not bother even trying! */ if (!window_id) { RetError(toolbox_create_object(0, "SaveFile", &id)); } else id = window_id; /* Remember the reason we're being opened here */ savefile_reason = reason; /* Work out which parent object and component IDs */ /* we want to set */ switch (savefile_reason) { default: case save_as_html: pc = FileSaveFrame; break; case save_as_draw: pc = ExportAsDraw; break; case save_as_text: pc = ExportAsText; break; } /* Show the dialogue */ RetError(toolbox_show_object(Toolbox_ShowObject_AsMenu, id, Toolbox_ShowObject_AtPointer, NULL, b->self_id, pc)); return NULL; } /*************************************************/ /* savefile_to_be_shown() */ /* */ /* Fills in the Save File dialogue prior to */ /* being shown, on the basis of the parent */ /* component ID and ancestor object ID in the */ /* event. */ /* */ /* Parameters are as standard for a Toolbox */ /* event handler. */ /*************************************************/ int savefile_to_be_shown(int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle) { char text[Limits_OS_Pathname]; browser_data * b; int first_time = !window_id; int ok = 0; ObjectId pc = idb->parent_component; /* If this is the first time we've been opened, do a few */ /* initialisation bits and pieces */ if (first_time) { /* Process the writable icon text */ ChkError(windows_process_component_text(idb->self_id, SaveFileWrit, text, sizeof(text), 0, 1)); /* The following ensures that we increment the usage */ /* count for the dialogue so it is never deleted. */ /* Otherwise, we have to start keeping track of when */ /* it goes and deal with event handlers etc. as */ /* appropriate. Creating the object like this only */ /* gives back the existing ID of what is already */ /* there, so this works out as taking up less time */ /* and less code than an alternative approach. */ ChkError(toolbox_create_object(0, "SaveFile", NULL)); } /* Where did we come from? */ ChkError(toolbox_get_client_handle(0, idb->ancestor_id, (void *) &b)); /* Deal with each parent menu item case */ switch (pc) { case -1: { /* If the dialogue was raised directly from a keyboard shortcut, */ /* (hmm, preferable to go through an event...) want to save the */ /* current page source. */ pc = FileSaveFrame; /* Slight complication is that only an ancestor browser ever */ /* has the input focus, so we need to find the actual selected */ /* frame that the user is going to think the dialogue is for. */ if (b->selected_frame) b = b->selected_frame; /* Can't rely on any earlier sanity checks in this case, as this will */ /* be the first and only function called before the dialogue opens. */ if (!b || !b->source) return 0; /* No 'break' - let this drop through... */ } /* Save a page's source */ case FileSaveFrame: case FileSaveParent: case FileSaveAncestor: { browser_data * alt = b; /* For saving a frameset, work out the required browser_data struct */ if (pc == FileSaveParent) { alt = utils_parent(b); if (!alt) alt = utils_ancestor(b); } else if (pc == FileSaveAncestor) alt = utils_ancestor(b); b = alt; ChkError(savefile_switch_to_normal(idb->self_id)); /* Reset the transferred data counter */ b->save_transferred = 0; ChkError(savefile_set_leafname_from_url(idb->self_id, SaveFileWrit, browser_current_url(b))); /* Set the draggable sprite */ ChkError(savefile_set_filetype(idb->self_id, SaveFileDrag, b->page_is_text ? FileType_TEXT : FileType_HTML, 0)); /* Ensure the width is correct */ ChkError(savefile_auto_width(idb->self_id)); /* Remember various details about the dialogue's source */ savefile_browser = b; savefile_token = NULL; ok = 1; } break; #ifndef REMOTE_HOTLIST case HotlistSaveHotlist: { ChkError(savefile_switch_to_normal(idb->self_id)); ChkError(savefile_set_leafname(idb->self_id, SaveFileWrit, lookup_token("HotlistLeafname:Hotlist",0,0))); ChkError(savefile_set_filetype(idb->self_id, SaveFileDrag, FileType_HTML, 0)); ChkError(savefile_auto_width(idb->self_id)); ok = 1; } break; /* Save an object from the hotlist - a URL, directory, */ /* or general selection */ case MiscHotlistSaveObject: { hotlist_item * source = hotlist_find_selected_item(); unsigned int items = hotlist_count_selected_items(); if (items && source) { if (items == 1 && source->type == hl_url) { ChkError(savefile_switch_to_option(idb->self_id)); ChkError(savefile_set_items(idb->self_id, alt_mhso)); ChkError(savefile_set_leafname_from_url(idb->self_id, SaveFileWrit, source->data.url)); ChkError(savefile_text_uri_or_url(idb->self_id)); ChkError(savefile_toggle_uri_or_url(idb->self_id)); ChkError(savefile_auto_width(idb->self_id)); } else { ChkError(savefile_switch_to_normal(idb->self_id)); ChkError(savefile_set_leafname(idb->self_id, SaveFileWrit, lookup_token("HotlistLeafname:Hotlist",0,0))); ChkError(savefile_set_filetype(idb->self_id, SaveFileDrag, FileType_HTML, 0)); ChkError(savefile_auto_width(idb->self_id)); } ok = 1; } #ifdef TRACE else { erb.errnum = Utils_Error_Custom_Normal; sprintf(erb.errmess, "Should have more than a selected hotlist item %p and item count %d in savefile_to_be_shown", source, items); ChkError(&erb); } #endif } break; #endif case HistorySaveLocal: case HistorySaveGlobal: { ChkError(savefile_switch_to_normal(idb->self_id)); ChkError(savefile_set_leafname(idb->self_id, SaveFileWrit, lookup_token("HistoryLeafname:History",0,0))); ChkError(savefile_set_filetype(idb->self_id, SaveFileDrag, FileType_HTML, 0)); savefile_browser = b; ok = 1; } break; case ExportAsDraw: { ChkError(savefile_switch_to_option(idb->self_id)); ChkError(savefile_set_items(idb->self_id, alt_ead)); ChkError(savefile_set_leafname_from_url(idb->self_id, SaveFileWrit, browser_current_url(b))); ChkError(savefile_text_without_backgrounds_or_with(idb->self_id)); ChkError(savefile_toggle_without_backgrounds_or_with(idb->self_id)); ChkError(savefile_auto_width(idb->self_id)); savefile_browser = b; savefile_token = NULL; ok = 1; } break; case ExportAsText: { ChkError(savefile_switch_to_normal(idb->self_id)); ChkError(savefile_set_leafname_from_url(idb->self_id, SaveFileWrit, browser_current_url(b))); ChkError(savefile_set_filetype(idb->self_id, SaveFileDrag, FileType_TEXT, 0)); ChkError(savefile_auto_width(idb->self_id)); savefile_browser = b; savefile_token = NULL; ok = 1; } break; /* Save a location as a URI file */ case FileSaveFrameLocation: case FileSaveParentLocation: case FileSaveAncestorLocation: { browser_data * alt = b; char * url; /* For saving a frameset, work out the required browser_data struct */ if (pc == FileSaveParentLocation) { alt = utils_parent(b); if (!alt) alt = utils_ancestor(b); } else if (pc == FileSaveAncestorLocation) alt = utils_ancestor(b); b = alt; url = browser_current_url(b); if (!url) url = " "; ChkError(savefile_switch_to_option(idb->self_id)); ChkError(savefile_set_items(idb->self_id, alt_fsfl)); ChkError(savefile_set_leafname_from_url(idb->self_id, SaveFileWrit, url)); ChkError(savefile_text_uri_or_url(idb->self_id)); ChkError(savefile_toggle_uri_or_url(idb->self_id)); ChkError(savefile_auto_width(idb->self_id)); savefile_browser = b; savefile_token = NULL; ok = 1; } break; /* Export the link the pointer was over when the menu opened */ case ExportLink: { HStream * link = menus_document_opened_over(); if (!link || !ISLINK(link)) { #ifndef TRACE return 0; #else erb.errnum = Utils_Error_Custom_Normal; sprintf(erb.errmess, "Menu token %p is not a link or has no anchor text in savefile_to_be_shown", link); show_error_ret(&erb); return 0; #endif } /* Set the leafname and filetype */ ChkError(savefile_switch_to_option(idb->self_id)); ChkError(savefile_set_items(idb->self_id, alt_el)); ChkError(savefile_set_leafname_from_url(idb->self_id, SaveFileWrit, link->anchor)); ChkError(savefile_text_uri_or_url(idb->self_id)); ChkError(savefile_toggle_uri_or_url(idb->self_id)); ChkError(savefile_auto_width(idb->self_id)); /* Remember various details about the dialogue's source */ savefile_browser = b; savefile_token = link; ok = 1; } break; case ExportPicture: { HStream * image = menus_document_opened_over(); const char * src = NULL; ChkError(savefile_switch_to_radios(idb->self_id)); ChkError(savefile_set_items(idb->self_id, alt_ep)); if ( !image || ! ( (image->style & IMG) || ( image->tagno == TAG_INPUT && HtmlINPUTtype(image) == inputtype_IMAGE ) || ( ISOBJECT(image) && object_token_is_image(b, image) ) ) ) { #ifndef TRACE return 0; #else erb.errnum = Utils_Error_Custom_Normal; sprintf(erb.errmess, "Menu token %p is not an image in savefile_to_be_shown", image); show_error_ret(&erb); return 0; #endif } if (image->style & IMG) src = image->src; else if (image->tagno == TAG_INPUT) src = HtmlINPUTsrc(image); else src = HtmlOBJECTdata(image); ChkError(savefile_set_leafname_from_url(idb->self_id, SaveFileWrit, (char *) src)); ChkError(savefile_text_sprite_or_original(idb->self_id)); ChkError(savefile_toggle_sprite_or_original(idb->self_id, src)); ChkError(savefile_auto_width(idb->self_id)); /* Remember various details about the dialogue's source */ savefile_browser = b; savefile_token = image; ok = 1; } break; case ExportBackground: { char src[Limits_URL]; ChkError(savefile_switch_to_radios(idb->self_id)); ChkError(savefile_set_items(idb->self_id, alt_eb)); if (b->background_image == -1) { #ifndef TRACE return 0; #else erb.errnum = Utils_Error_Custom_Normal; StrNCpy0(erb.errmess, "There is no background image on this page in savefile_to_be_shown"); show_error_ret(&erb); return 0; #endif } *src = 0; image_get_background_image_url(b, src, sizeof(src)); if (!*src) ChkError(savefile_set_leafname (idb->self_id, SaveFileWrit, lookup_token("BackName:Background",0,0))); else ChkError(savefile_set_leafname_from_url(idb->self_id, SaveFileWrit, src)); ChkError(savefile_text_sprite_or_original(idb->self_id)); ChkError(savefile_toggle_sprite_or_original(idb->self_id, src)); ChkError(savefile_auto_width(idb->self_id)); savefile_browser = b; savefile_token = NULL; ok = 1; } break; #ifdef TRACE default: { erb.errnum = 0; StrNCpy0(erb.errmess, "Save dialogue origin not understood in savefile_to_be_shown"); show_error_ret(&erb); return 0; } break; #endif } /* If we were successful, install relevant event handlers etc., assuming */ /* this is the first time the dialogue was opened. */ if (ok) { parent_component = pc; ancestor_id = b->self_id; if (first_time) { window_id = idb->self_id; ChkError(event_register_toolbox_handler(window_id, Draggable_DragEnded, savefile_drag_ended, NULL)); ChkError(event_register_toolbox_handler(window_id, ESaveFileOK, savefile_ok, NULL)); ChkError(event_register_toolbox_handler(window_id, ESaveFileCancel, savefile_cancel, NULL)); ChkError(event_register_toolbox_handler(window_id, ESaveFileOption, savefile_option_changed, NULL)); ChkError(event_register_toolbox_handler(window_id, ESaveFileRadio, savefile_radio_changed, NULL)); } } return 1; } /*************************************************/ /* savefile_set_leafname() */ /* */ /* Sets the leafname in the Save File dialogue */ /* to a given value, preserving whatever path */ /* component may already have been present. */ /* Any last save path recorded by the save */ /* routines in Save.c will take precedence over */ /* an existing dialogue path, though. */ /* */ /* Parameters: Object ID of the dialogue; */ /* */ /* Component ID of the writable */ /* gadget; */ /* */ /* Pointer to a null terminated leaf */ /* to work with. */ /*************************************************/ _kernel_oserror * savefile_set_leafname(ObjectId object, ComponentId component, char * leaf) { char * dot; char path[Limits_OS_Pathname]; const char * last; if (!leaf) return NULL; /* Is there an existing last pathname we should use? */ last = save_return_last_path(); if (last && *last) { StrNCpy0(path, last); } else { /* See what filename is already in the dialogue */ path[0] = 0; /* Important so that when we strcat the leafname later, */ /* things will work even if the writable holds no text. */ RetError(writablefield_get_value(0, object, component, path, sizeof(path), NULL)); path[sizeof(path) - 1] = 0; } /* If there's a leafname here, force a terminator in place of the '.' */ /* - otherwise, want to only have the leaf, so clear the string. */ dot = strrchr(path, '.'); if (dot) *(++dot) = 0; else *path = 0; /* Can we fit the leaf in? */ if (strlen(path) + strlen(leaf) + 1 > sizeof(path)) { /* No, can we just fit in the leaf? If not, leave */ /* everything alone... */ if (strlen(leaf) + 1 <= sizeof(path)) strcpy(path, leaf); } else strcat(path, leaf); /* Set the value */ return writablefield_set_value(0, object, component, path); } /*************************************************/ /* savefile_set_leafname_from_url() */ /* */ /* As savefile_set_leafname, but works out the */ /* leaf from a given URL. */ /* */ /* Parameters: Object ID of the dialogue; */ /* */ /* Component ID of the writable */ /* gadget; */ /* */ /* Pointer to a null terminated URL */ /* to work with. */ /*************************************************/ _kernel_oserror * savefile_set_leafname_from_url(ObjectId object, ComponentId component, char * url) { char leaf[Limits_OS_Pathname]; /* Get a leafname */ urlutils_leafname_from_url(url, leaf, sizeof(leaf)); /* Use it */ return savefile_set_leafname(object, component, leaf); } /*************************************************/ /* savefile_set_filetype() */ /* */ /* Sets the sprite of the draggable object in */ /* the Save File dialogue according to the given */ /* filetype, and records that filetype in */ /* savefile_type (unless asked not to). */ /* */ /* If the sprite cannot be found in the Wimp */ /* sprite pool, 'file_xxx' is used instead. */ /* */ /* Parameters: Object ID of the dialogue; */ /* */ /* Component ID of the draggable */ /* gadget; */ /* */ /* The filetype; */ /* */ /* 1 to *not* set savefile_type to */ /* the filetype - this would be used */ /* if (for example) this function */ /* was being called for a dialogue */ /* similar to, but not the same as */ /* the Save File dialogue (e.g. Save */ /* Object, where several can exist */ /* at once); if for a 'genuine' Save */ /* File dialogue, you *must* pass */ /* zero here. */ /*************************************************/ _kernel_oserror * savefile_set_filetype(ObjectId object, ComponentId component, int type, int dont_record) { char sprite[Limits_OS_SpriteName]; int len; if (dont_record || savefile_type != type) { if (!dont_record) savefile_type = type; /* Will it fit in the buffer? */ len = utils_len_printf("file_%03x", type); /* If so, build the sprite name and tell the gadget to use that sprite */ if (len < sizeof(sprite)) { sprintf(sprite, "file_%03x", type); if ( !_swix(Wimp_SpriteOp, _IN(0) | _IN(2), 0x18, /* Select sprite */ sprite) ) { /* If it has been found, use this sprite */ return draggable_set_sprite(0, object, component, sprite); } else { /* Otherwise use the generic 'file_xxx' instead */ return draggable_set_sprite(0, object, component, "file_xxx"); } } } return NULL; } /*************************************************/ /* savefile_drag_ended() */ /* */ /* Handle Draggable_DragEnded events from the */ /* Save File dialogue draggable sprite gadget. */ /* */ /* Parameters are as standard for a Toolbox */ /* event handler. */ /*************************************************/ static int savefile_drag_ended(int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle) { DraggableDragEndedEvent * drag = (DraggableDragEndedEvent *) event; protocols_saving saving = protocols_saving_nothing; int size = 4096; /* More or less arbitrary */ int alt = savefile_alternative_selected(); WimpGetPointerInfoBlock info; int window_handle; char path[Limits_OS_Pathname]; char * leaf; void * extra = savefile_token; /* If the user dragged back to the save dialogue, do nothing */ ChkError(window_get_wimp_handle(0, window_id, &window_handle)); if (window_handle == drag->window_handle) return 1; /* Get the pathname from the Save File dialogue. */ ChkError(writablefield_get_value(0, window_id, SaveFileWrit, path, sizeof(path), NULL)); path[sizeof(path) - 1] = 0; /* Point to the leafname component */ leaf = strrchr(path, '.'); if (!leaf) leaf = path; else leaf ++; /* Work out the estimated data size */ switch (parent_component) { /* Size of an HTML file */ case FileSaveFrame: case FileSaveParent: case FileSaveAncestor: { saving = protocols_saving_document_source; if (is_known_browser(savefile_browser)) size = save_source_size(savefile_browser); else size = 0; } break; /* Size of a URI file for a location */ case FileSaveFrameLocation: case FileSaveParentLocation: case FileSaveAncestorLocation: { char * url = browser_current_url (savefile_browser); char * title = browser_current_title(savefile_browser); if (!url) url = " "; saving = protocols_saving_frame_location; size = save_uri_size(url, title, alt); } break; /* Size of a text file */ case ExportAsText: { saving = protocols_saving_document_as_text; if (is_known_browser(savefile_browser)) size = savetext_text_size(savefile_browser); else size = 0; } break; /* Size of a Draw file */ case ExportAsDraw: { saving = protocols_saving_document_as_draw; if (is_known_browser(savefile_browser)) size = savedraw_draw_size(savefile_browser, alt); else size = 0; } break; /* Size of a URI file */ case ExportLink: { saving = protocols_saving_link; if (savefile_token && savefile_token->anchor) size = save_uri_size(savefile_token->anchor, NULL, alt); else size = 0; } break; /* Size of an image to export */ case ExportPicture: { saving = protocols_saving_image_sprite; if (savefile_token && is_known_browser(savefile_browser)) { if (!alt) size = image_sprite_size(savefile_browser, savefile_token); } else size = 0; } break; /* Save a background image */ case ExportBackground: { saving = protocols_saving_image_sprite; if (is_known_browser(savefile_browser)) { if (!alt) size = image_sprite_size(savefile_browser, NULL); } else size = 0; } break; #ifndef REMOTE_HOTLIST /* File size of the entire hotlist */ case HotlistSaveHotlist: { saving = protocols_saving_entire_hotlist; } break; /* Size of an object from the hotlist - a URL, directory, */ /* or general selection */ case MiscHotlistSaveObject: { hotlist_item * source = hotlist_find_selected_item(); unsigned int items = hotlist_count_selected_items(); if (items && source) { if (items == 1 && source->type == hl_url) { saving = protocols_saving_hotlist_entry; extra = (void *) source; size = save_uri_size(source->data.url, source->name, alt); } else saving = protocols_saving_hotlist_selection; } } break; #endif case HistorySaveLocal: { saving = protocols_saving_local_history; size = 0; } break; case HistorySaveGlobal: { saving = protocols_saving_global_history; size = 0; } break; /* For others, could leave size as 4096 - *but must set 'saving' appropriately* */ } /* Send out the DataSave message */ info.x = drag->x; info.y = drag->y; info.window_handle = drag->window_handle; info.icon_handle = drag->icon_handle; ChkError(protocols_atats_send_data_save(savefile_browser, extra, leaf, size, savefile_type, saving, &info)); return 1; } /*************************************************/ /* savefile_ok() */ /* */ /* Handles clicks on the 'OK' button in the */ /* Save File dialogue. */ /* */ /* Parameters are as standard for a Toolbox */ /* event handler. */ /*************************************************/ static int savefile_ok(int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle) { _kernel_oserror * e = NULL; int ok = 0; int alt = savefile_alternative_selected(); char * leaf; char path[Limits_OS_Pathname]; /* Get the pathname from the Save File dialogue. */ ChkError(writablefield_get_value(0, window_id, SaveFileWrit, path, sizeof(path), NULL)); path[sizeof(path) - 1] = 0; /* Is this fully specified? */ leaf = strrchr(path, '.'); if (!leaf) { StrNCpy0(erb.errmess, lookup_token("GivePath:To save, drag the file icon to a directory viewer", 0, 0)); erb.errnum = Utils_Error_Custom_Message; ChkError(&erb); } /* Save the file */ switch (parent_component) { /* Save as HTML */ case FileSaveFrame: case FileSaveParent: case FileSaveAncestor: { if (is_known_browser(savefile_browser)) { e = save_save_source(path, savefile_browser); if (!e) ok = 1; } } break; /* Save as text */ case ExportAsText: { if (is_known_browser(savefile_browser)) { e = savetext_save_text(savefile_browser, path); if (!e) ok = 1; } } break; /* Save as Draw */ case ExportAsDraw: { if (is_known_browser(savefile_browser)) { e = savedraw_save_draw(savefile_browser, path, alt); if (!e) ok = 1; } } break; /* Save the current location or a link as a URI or URL file */ case ExportLink: case FileSaveFrameLocation: case FileSaveParentLocation: case FileSaveAncestorLocation: { if (is_known_browser(savefile_browser)) { if (savefile_token) { char * title = browser_current_title(savefile_browser); /* Save a link as a URI file */ e = save_save_uri(path, savefile_token->anchor, title, alt); if (!e) ok = 1; } else { /* Save the current location as a URI file */ char * url = browser_current_url (savefile_browser); char * title = browser_current_title(savefile_browser); if (!url) url = " "; e = save_save_uri(path, url, title, alt); if (!e) ok = 1; } } } break; /* Export a foreground image */ case ExportPicture: { if (is_known_browser(savefile_browser)) { if (!alt) e = image_export_sprite (path, savefile_browser, savefile_token); else e = image_export_original(path, savefile_browser, savefile_token); if (!e) ok = 1; } } break; /* Export a background image */ case ExportBackground: { if (is_known_browser(savefile_browser)) { if (!alt) e = image_export_sprite (path, savefile_browser, NULL); else e = image_export_original(path, savefile_browser, NULL); if (!e) ok = 1; } } break; #ifndef REMOTE_HOTLIST /* Save the hotlist */ case HotlistSaveHotlist: { e = hotlist_save_hotlist(path, NULL, 0); if (!e) ok = 1; } break; /* Save an object from the hotlist - a URL, directory, */ /* or general selection */ case MiscHotlistSaveObject: { hotlist_item * source = hotlist_find_selected_item(); unsigned int items = hotlist_count_selected_items(); if (items && source) { if (items == 1 && source->type == hl_url) { e = save_save_uri(path, source->data.url, source->name, alt); if (!e) { ok = 1; e = hotlist_clear_selection(); } } else { e = hotlist_save_hotlist(path, NULL, 1); if (!e) { ok = 1; e = hotlist_clear_selection(); } } } } break; #endif case HistorySaveLocal: { browser_data * local_browser = savefile_browser; if (!is_known_browser(savefile_browser)) local_browser = NULL; e = history_save_as_html(path, local_browser); if (!e) ok = 1; } break; case HistorySaveGlobal: { e = history_save_as_html(path, NULL); if (!e) ok = 1; } break; } /* If everything is OK, close the menu */ if (!e) { ChkError(savefile_close(0,1)); /* Make sure we've tidied up */ _swix(Wimp_CreateMenu, _IN(1), -1); } /* Finished */ if (e) ChkError(e); return 1; } /*************************************************/ /* savefile_cancel() */ /* */ /* Handles clicks on the 'Cancel' button in the */ /* Save File dialogue */ /* */ /* Parameters are as standard for a Toolbox */ /* event handler. */ /*************************************************/ static int savefile_cancel(int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle) { /* If we were fetching, stop the fetch */ if (savefile_browser && is_known_browser(savefile_browser)) { if (savefile_browser->save_link) fetch_stop(savefile_browser, 0); } /* We don't do anything sophisticated, like */ /* restoring previous options here, as the */ /* dialogue is too simple for it to be */ /* worthwhile. Therefore, just close it. */ ChkError(savefile_close(0,0)); return 1; } /*************************************************/ /* savefile_close() */ /* */ /* If the Save File dialogue is opened, this */ /* will close it. */ /* */ /* Parameters: An object ID, or 0. If not zero, */ /* the ID must match the ancestor */ /* recorded when the dialogue was */ /* opened or no action is taken. */ /* */ /* 0 to close the dialogue, 1 to do */ /* everything except that. */ /*************************************************/ _kernel_oserror * savefile_close(ObjectId ancestor, int do_not_close) { _kernel_oserror * e = NULL; if (!window_id) return NULL; if (ancestor && ancestor != ancestor_id) return NULL; /* If required, close the dialogue */ if (!do_not_close && !e) { /* If the dialogue came from a menu tree, collapse the tree */ if (parent_component != -1) e = _swix(Wimp_CreateMenu, _IN(1), -1); /* Close the dialogue */ if (!e) e = toolbox_hide_object(0, window_id); } parent_component = -1; ancestor_id = 0; savefile_browser = NULL; savefile_token = NULL; savefile_type = 0x000; return e; } /*************************************************/ /* savefile_return_dialogue_info() */ /* */ /* Returns information on the Save File dialogue */ /* and its ancestor. */ /* */ /* Parameters: Pointer to an ObjectId, in which */ /* the ID of the dialogue is placed; */ /* */ /* Pointer to an ObjectId, in which */ /* the ID of the ancestor window is */ /* placed. */ /* */ /* Returns: See parameters list, and note */ /* that the returned values will be */ /* 0 and 0 if the Save File dialogue */ /* is closed. */ /* */ /* Assumes: Either pointer may be NULL. */ /*************************************************/ void savefile_return_dialogue_info(ObjectId * window, ObjectId * ancestor) { if (window) *window = window_id; if (ancestor) *ancestor = ancestor_id; } /*************************************************/ /* savefile_switch_to_normal() */ /* */ /* Move the radio buttons or option buttons from */ /* view in the Save File dialogue, if either is */ /* present. */ /* */ /* Parameters: Object ID of the dialogue. */ /*************************************************/ static _kernel_oserror * savefile_switch_to_normal(ObjectId window) { BBox opt, ra1, ra2; int showing = 0; int noopt = 0; int norad = 0; int moveby; /* Find the option button and/or radio gadgets */ if (gadget_get_bbox(0, window, SaveFileOption, &opt)) noopt = 1; if (gadget_get_bbox(0, window, SaveFileRadio1, &ra1)) norad = 1; if (gadget_get_bbox(0, window, SaveFileRadio2, &ra2)) norad = 1; if (!noopt && opt.xmin < opt.xmax - opt.xmin) showing = 1; else if (!norad && ra1.xmin < ra1.xmax - ra1.xmin) showing = 2; /* If 'showing' is zero, we're on the normal display already */ if (!showing) goto savefile_switch_to_normal_set_extent; /* Otherwise, need to move things about a bit */ if (showing == 1) { /* We're on the option button display */ moveby = (opt.xmax - opt.xmin) * 2; opt.xmin += moveby; opt.xmax += moveby; RetError(gadget_move_gadget(0, window, SaveFileOption, &opt)); } else { /* We're on the radio buttons display */ moveby = (ra1.xmax - ra1.xmin) * 2; ra1.xmin += moveby; ra1.xmax += moveby; ra2.xmin += moveby; ra2.xmax += moveby; RetError(gadget_move_gadget(0, window, SaveFileRadio1, &ra1)); RetError(gadget_move_gadget(0, window, SaveFileRadio2, &ra2)); } /* Move the Y coordinates of the Cancel and Save buttons back */ /* to the normal position. First, Cancel. */ RetError(gadget_get_bbox(0, window, SaveFileCancel, &ra1)); RetError(gadget_get_bbox(0, window, SaveFileCancelMarkNormal, &ra2)); ra1.ymin = ra2.ymin; ra1.ymax = ra2.ymax; RetError(gadget_move_gadget(0, window, SaveFileCancel, &ra1)); savefile_switch_to_normal_set_extent: /* Now, OK (or 'Save' or whatever) */ RetError(gadget_get_bbox(0, window, SaveFileOK, &ra1)); RetError(gadget_get_bbox(0, window, SaveFileOKMarkNormal, &ra2)); ra1.ymin = ra2.ymin; ra1.ymax = ra2.ymax; RetError(gadget_move_gadget(0, window, SaveFileOK, &ra1)); /* Set the extent */ RetError(savefile_set_correct_extent(window, &ra1)); /* Finished */ return NULL; } /*************************************************/ /* savefile_switch_to_option() */ /* */ /* Move the option button into the Save File */ /* dialogue, moving out the radio buttons if */ /* present. */ /* */ /* Parameters: Object ID of the dialogue. */ /*************************************************/ static _kernel_oserror * savefile_switch_to_option(ObjectId window) { BBox opt, ra1, ra2, wri; int showing = 0; int noopt = 0; int norad = 0; int moveby; /* Find the writable gadget */ RetError(gadget_get_bbox(0, window, SaveFileWrit, &wri)); /* Find the option button and/or radio gadgets */ if (gadget_get_bbox(0, window, SaveFileOption, &opt)) noopt = 1; if (gadget_get_bbox(0, window, SaveFileRadio1, &ra1)) norad = 1; if (gadget_get_bbox(0, window, SaveFileRadio2, &ra2)) norad = 1; /* If there are no option buttons, try switching to the */ /* radios instead. */ if (noopt) { if (norad) return NULL; else return savefile_switch_to_radios(window); } if (!noopt && opt.xmin < opt.xmax - opt.xmin) showing = 1; else if (!norad && ra1.xmin < ra1.xmax - ra1.xmin) showing = 2; /* If 'showing' is 1, we're on the option display already */ if (showing == 1) goto savefile_switch_to_option_set_extent; /* Move back to the normal display as a starting point */ RetError(savefile_switch_to_normal(window)); /* Move the option button to the same xmin as the writable */ moveby = opt.xmin - wri.xmin; opt.xmin -= moveby; opt.xmax -= moveby; RetError(gadget_move_gadget(0, window, SaveFileOption, &opt)); /* Move the Y coordinates of the Cancel and Save buttons */ /* to the option position. First, Cancel. */ RetError(gadget_get_bbox(0, window, SaveFileCancel, &ra1)); RetError(gadget_get_bbox(0, window, SaveFileCancelMarkOption, &ra2)); ra1.ymin = ra2.ymin; ra1.ymax = ra2.ymax; RetError(gadget_move_gadget(0, window, SaveFileCancel, &ra1)); savefile_switch_to_option_set_extent: /* Now, OK (or 'Save' or whatever) */ RetError(gadget_get_bbox(0, window, SaveFileOK, &ra1)); RetError(gadget_get_bbox(0, window, SaveFileOKMarkOption, &ra2)); ra1.ymin = ra2.ymin; ra1.ymax = ra2.ymax; RetError(gadget_move_gadget(0, window, SaveFileOK, &ra1)); /* Set the extent */ RetError(savefile_set_correct_extent(window, &ra1)); /* Finished */ return NULL; } /*************************************************/ /* savefile_switch_to_radios() */ /* */ /* Move the radio buttons into the Save File */ /* dialogue, moving out the option button if */ /* present. */ /* */ /* Parameters: Object ID of the dialogue. */ /*************************************************/ static _kernel_oserror * savefile_switch_to_radios(ObjectId window) { BBox opt, ra1, ra2, wri; int showing = 0; int noopt = 0; int norad = 0; int moveby; /* Find the writable gadget */ RetError(gadget_get_bbox(0, window, SaveFileWrit, &wri)); /* Find the option button and/or radio gadgets */ if (gadget_get_bbox(0, window, SaveFileOption, &opt)) noopt = 1; if (gadget_get_bbox(0, window, SaveFileRadio1, &ra1)) norad = 1; if (gadget_get_bbox(0, window, SaveFileRadio2, &ra2)) norad = 1; /* If there are no radio buttons, try switching to the option */ /* button instead. */ if (norad) { if (noopt) return NULL; else return savefile_switch_to_option(window); } if (!noopt && opt.xmin < opt.xmax - opt.xmin) showing = 1; else if (!norad && ra1.xmin < ra1.xmax - ra1.xmin) showing = 2; /* If 'showing' is 2, we're on the radios display already */ if (showing == 2) goto savefile_switch_to_radios_set_extent; /* Move back to the normal display as a starting point */ RetError(savefile_switch_to_normal(window)); /* Move the radio buttons to the same xmin as the writable */ moveby = ra1.xmin - wri.xmin; ra1.xmin -= moveby; ra1.xmax -= moveby; moveby = ra2.xmin - wri.xmin; ra2.xmin -= moveby; ra2.xmax -= moveby; RetError(gadget_move_gadget(0, window, SaveFileRadio1, &ra1)); RetError(gadget_move_gadget(0, window, SaveFileRadio2, &ra2)); /* Move the Y coordinates of the Cancel and Save buttons */ /* to the radios position. First, Cancel. */ RetError(gadget_get_bbox(0, window, SaveFileCancel, &ra1)); RetError(gadget_get_bbox(0, window, SaveFileCancelMarkRadios, &ra2)); ra1.ymin = ra2.ymin; ra1.ymax = ra2.ymax; RetError(gadget_move_gadget(0, window, SaveFileCancel, &ra1)); savefile_switch_to_radios_set_extent: /* Now, OK (or 'Save' or whatever) */ RetError(gadget_get_bbox(0, window, SaveFileOK, &ra1)); RetError(gadget_get_bbox(0, window, SaveFileOKMarkRadios, &ra2)); ra1.ymin = ra2.ymin; ra1.ymax = ra2.ymax; RetError(gadget_move_gadget(0, window, SaveFileOK, &ra1)); /* Set the extent */ RetError(savefile_set_correct_extent(window, &ra1)); /* Finished */ return NULL; } /*************************************************/ /* savefile_set_items() */ /* */ /* Set the radio buttons or option button in a */ /* Save File dialogue, if present, to the given */ /* state. */ /* */ /* Parameters: Object ID of the dialogue; */ /* */ /* 1 to tick the option box or */ /* select the second of the two */ /* radios - 0 to untick the option */ /* box or select the first of the */ /* two radios. */ /*************************************************/ static _kernel_oserror * savefile_set_items(ObjectId window, int selected) { optionbutton_set_state(0, window, SaveFileOption, selected); radiobutton_set_state (0, window, SaveFileRadio1, !selected); radiobutton_set_state (0, window, SaveFileRadio2, selected); return NULL; } /*************************************************/ /* savefile_option_changed() */ /* */ /* Called when the option button in a Save File */ /* dialogue (if any) is toggled. */ /* */ /* Parameters are as standard for a Toolbox */ /* event handler. */ /*************************************************/ static int savefile_option_changed(int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle) { return savefile_item_changed(idb); } /*************************************************/ /* savefile_radio_changed() */ /* */ /* Called when the radio buttons in a Save File */ /* dialogue (if any) are toggled. */ /* */ /* Parameters are as standard for a Toolbox */ /* event handler. */ /*************************************************/ static int savefile_radio_changed(int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle) { return savefile_item_changed(idb); } /*************************************************/ /* savefile_item_changed() */ /* */ /* Called when either one of the two radios that */ /* can appear in a Save File dialogue, or the */ /* option button that can appear, is used. */ /* */ /* Parameters: Pointer an IdBlock from the */ /* Toolbox event that was raised by */ /* the button(s). */ /*************************************************/ static int savefile_item_changed(IdBlock * idb) { browser_data * b; ComponentId pc = idb->parent_component; int alt = savefile_alternative_selected(); /* Where did we come from? */ ChkError(toolbox_get_client_handle(0, idb->ancestor_id, (void *) &b)); // /* If the parent ID is the same as a toolbar, this was */ // /* from a toolbar button - in which case, use a parent */ // /* ID of FileSaveParent, as that is what we actually */ // /* want to do. */ // // if ( // idb->parent_id == toolbars_get_upper(b) || // idb->parent_id == toolbars_get_lower(b) // ) // pc = FileSaveParent; /* Actions will vary according to the parent component */ switch (pc) { case FileSaveFrame: case FileSaveParent: case FileSaveAncestor: case HotlistSaveHotlist: case HistorySaveLocal: case HistorySaveGlobal: case ExportAsText: { /* Nothing to do at the moment, shouldn't be able to access */ /* the option button gadget anyway... */ #ifndef REMOTE_HOTLIST savefile_item_changed_gripe: #endif #ifdef TRACE erb.errnum = Utils_Error_Custom_Normal; sprintf(erb.errmess, "Shouldn't be using the option button in SaveFile dialogue for parent component 0x%x in savefile_item_changed!", pc); show_error_ret(&erb); #endif return 1; } break; #ifndef REMOTE_HOTLIST case MiscHotlistSaveObject: { hotlist_item * source = hotlist_find_selected_item(); unsigned int items = hotlist_count_selected_items(); alt_mhso = alt; if (items && source) { if (items == 1 && source->type == hl_url) { /* Saving a single hotlist item, so can do this as */ /* a URI or ANT URL file. */ ChkError(savefile_toggle_uri_or_url(idb->self_id)); } else { /* Saving multiple items as HTML - no defined action */ /* for the option button. */ goto savefile_item_changed_gripe; } } } break; #endif case ExportAsDraw: { alt_ead = alt; ChkError(savefile_toggle_without_backgrounds_or_with(idb->self_id)); } break; case FileSaveFrameLocation: case FileSaveParentLocation: case FileSaveAncestorLocation: case ExportLink: { if (pc == ExportLink) alt_el = alt; else alt_fsfl = alt; ChkError(savefile_toggle_uri_or_url(idb->self_id)); } break; case ExportPicture: { HStream * image = menus_document_opened_over(); const char * src = NULL; alt_ep = alt; if (image->style & IMG) src = image->src; else if (image->tagno == TAG_INPUT) src = HtmlINPUTsrc(image); else src = HtmlOBJECTdata(image); ChkError(savefile_toggle_sprite_or_original(idb->self_id, src)); } break; case ExportBackground: { char src[Limits_URL]; alt_eb = alt; if (b->background_image == -1) { #ifndef TRACE return 0; #else erb.errnum = Utils_Error_Custom_Normal; StrNCpy0(erb.errmess, "There is no background image on this page in savefile_item_changed."); show_error_ret(&erb); return 0; #endif } *src = 0; image_get_background_image_url(b, src, sizeof(src)); ChkError(savefile_toggle_sprite_or_original(idb->self_id, src)); } break; #ifdef TRACE default: { erb.errnum = 0; StrNCpy0(erb.errmess, "Save dialogue origin not understood in savefile_item_changed."); show_error_ret(&erb); return 0; } break; #endif } return 1; } /*************************************************/ /* savefile_alternative_selected() */ /* */ /* If the second of the two radios in a Save */ /* File dialogue is selected, or if there is */ /* an option button present instead and it is */ /* itself selected, returns 1. Otherwise returns */ /* 0. */ /* */ /* Returns: See above. */ /* */ /* Assumes: window_id holds the Object ID of */ /* a relevant Save file dialogue. */ /*************************************************/ int savefile_alternative_selected(void) { int alt_set = 0; int try_radios = 0; BBox box; if (!window_id) return 0; /* Need to work out whether the option button or radio buttons */ /* are present, and if so, which is visible at the moment and */ /* use that. */ if (!gadget_get_bbox(0, window_id, SaveFileOption, &box)) { /* If we can get the BBox, is it visible right now? */ if (box.xmin >= box.xmax - box.xmin) try_radios = 1; /* No, so signal we should try the radios */ else { /* Get the option button state */ if (optionbutton_get_state(0, window_id, SaveFileOption, &alt_set)) alt_set = 0; } } /* Couldn't get the option button BBox, so try the radios */ else try_radios = 1; /* If required, do similar things to the above with the radio buttons */ if (try_radios) { if (!gadget_get_bbox(0, window_id, SaveFileRadio2, &box)) { if (box.xmin < box.xmax - box.xmin) { if (radiobutton_get_state(0, window_id, SaveFileRadio2, &alt_set, NULL)) alt_set = 0; } } } return alt_set; } /*************************************************/ /* savefile_text_uri_or_url() */ /* */ /* Set the text in the radio buttons or option */ /* button of a Save File dialogue giving the */ /* choice of saving a URL as a URI or ANT URL */ /* file. */ /* */ /* Parameters: Object ID of the relevant Save */ /* File dialogue. */ /*************************************************/ static _kernel_oserror * savefile_text_uri_or_url(ObjectId window) { /* These can all fail silently if the gadget isn't present */ optionbutton_set_label(0, window, SaveFileOption, lookup_token("SaveFileOpURL:Save URL file",0,0)); radiobutton_set_label (0, window, SaveFileRadio1, lookup_token("SaveFileRaURI:URI format",0,0)); radiobutton_set_label (0, window, SaveFileRadio2, lookup_token("SaveFileRaURL:URL format",0,0)); return 0; } /*************************************************/ /* savefile_toggle_uri_or_url() */ /* */ /* Called when the option to save a URL as a URI */ /* or ANT URL file is changed. */ /* */ /* Parameters: Object ID of the relevant Save */ /* File dialogue. */ /*************************************************/ static _kernel_oserror * savefile_toggle_uri_or_url(ObjectId window) { int set_url = savefile_alternative_selected(); if (set_url) return savefile_set_filetype(window, SaveFileDrag, FileType_URL, 0); else return savefile_set_filetype(window, SaveFileDrag, FileType_URI, 0); } /*************************************************/ /* savefile_text_sprite_or_original() */ /* */ /* Set the text in the radio buttons or option */ /* button of a Save File dialogue giving the */ /* choice of saving an image as a sprite or in */ /* its original format. */ /* */ /* Parameters: Object ID of the relevant Save */ /* File dialogue. */ /*************************************************/ static _kernel_oserror * savefile_text_sprite_or_original(ObjectId window) { /* These can all fail silently if the gadget isn't present */ optionbutton_set_label(0, window, SaveFileOption, lookup_token("SaveFileOpOriginal:Save original image",0,0)); radiobutton_set_label (0, window, SaveFileRadio1, lookup_token("SaveFileRaSprite:Sprite format",0,0)); radiobutton_set_label (0, window, SaveFileRadio2, lookup_token("SaveFileRaOriginal:Original format",0,0)); return 0; } /*************************************************/ /* savefile_toggle_sprite_or_original() */ /* */ /* Called when the option to save an image as a */ /* sprite or in its original format is changed. */ /* */ /* Parameters: Object ID of the relevant Save */ /* File dialogue. */ /*************************************************/ static _kernel_oserror * savefile_toggle_sprite_or_original(ObjectId window, const char * src) { int set_original = savefile_alternative_selected(); if (!set_original) return savefile_set_filetype(window, SaveFileDrag, FileType_SPR, 0); else { const char * dot = NULL; int filetype; /* Try and determine the filetype from the URL of the image */ if (src) dot = strrchr(src, '.'); if (dot) { if (mimemap_extension_to_riscos(dot, &filetype)) filetype = FileType_DATA; } else filetype = FileType_DATA; return savefile_set_filetype(window, SaveFileDrag, filetype, 0); } } /*************************************************/ /* savefile_text_without_backgrounds_or_with() */ /* */ /* Set the text in the radio buttons or option */ /* button of a Save File dialogue giving the */ /* choice of saving a Draw file with or without */ /* a background image. */ /* */ /* Parameters: Object ID of the relevant Save */ /* File dialogue. */ /*************************************************/ static _kernel_oserror * savefile_text_without_backgrounds_or_with(ObjectId window) { /* These can all fail silently if the gadget isn't present */ optionbutton_set_label(0, window, SaveFileOption, lookup_token("SaveFileOpBack:With background",0,0)); radiobutton_set_label (0, window, SaveFileRadio1, lookup_token("SaveFileRaNoBack:No background",0,0)); radiobutton_set_label (0, window, SaveFileRadio2, lookup_token("SaveFileRaBack:With background",0,0)); return 0; } /*************************************************/ /* savefile_toggle_without_backgrounds_or_with() */ /* */ /* Called when the option to save as a Draw file */ /* with or without a background image is */ /* changed. */ /* */ /* Parameters: Object ID of the relevant Save */ /* File dialogue. */ /*************************************************/ static _kernel_oserror * savefile_toggle_without_backgrounds_or_with(ObjectId window) { /* There's very little to do here presently */ return savefile_set_filetype(window, SaveFileDrag, FileType_DRAW, 0); } /*************************************************/ /* savefile_set_correct_extent() */ /* */ /* Ensure the width and height (in terms of the */ /* visible area and actual extent) is correct */ /* according to the presence of: */ /* */ /* SaveFileRightGapMarker */ /* SaveFileBottomGapMarker */ /* */ /* Parameters: Object ID of the dialogue to */ /* alter; */ /* */ /* Pointer to a BBox describing the */ /* new size (xmax - xmin, ymax - */ /* ymin). */ /*************************************************/ static _kernel_oserror * savefile_set_correct_extent(ObjectId window, BBox * ra1) { WimpGetWindowStateBlock state; int open; BBox opt, ra2; /* Find out what the window extent should be */ RetError(window_get_extent(0, window, &opt)); /* Set the window extent ymin to the extent ymax minus the */ /* height; the height being the ymin of the OK button minus */ /* the height of the SaveFileBottomGapMarker gadget. Set */ /* the width on a similar basis. */ RetError(gadget_get_bbox(0, window, SaveFileBottomGapMarker, &ra2)); opt.ymin = opt.ymax + ra1->ymin - (ra2.ymax - ra2.ymin); RetError(gadget_get_bbox(0, window, SaveFileRightGapMarker, &ra2)); opt.xmax = opt.xmin + ra1->xmax + (ra2.xmax - ra2.xmin); RetError(window_set_extent(0, window, &opt)); /* We need to ensure the window is opened to full size, but it may be */ /* already open - so get the state first. */ RetError(window_get_wimp_handle(0, window, &state.window_handle)); RetError(wimp_get_window_state(&state)); /* Remember if it is open or not */ open = state.flags & WimpWindow_Open; /* Set the visible area to match the extent, extending */ /* it to the right and downwards. */ state.visible_area.xmax = state.visible_area.xmin + opt.xmax - opt.xmin; state.visible_area.ymin = state.visible_area.ymax - opt.ymax + opt.ymin; /* Show the window with these coordinates */ RetError(wimp_open_window((WimpOpenWindowBlock *) &state)); /* If it wasn't already open, close the window */ if (!open) RetError(wimp_close_window(&state.window_handle)); /* Finished. */ return NULL; } /*************************************************/ /* savefile_auto_width() */ /* */ /* If the SaveFileAutoWidthMarker gadget */ /* (SaveFile.h) is present, check the width of */ /* various text items in the Save File dialogue */ /* and auto-width other components to make the */ /* dialogue fit the widest. The following */ /* components are assumed to be present: */ /* */ /* SaveFileRightGapMarker */ /* SaveFileBottomGapMarker */ /* */ /* Parameters: Object ID of the dialogue to */ /* auto-width. */ /*************************************************/ static _kernel_oserror * savefile_auto_width(ObjectId window) { BBox min, max; BBox optbox, ra1box, ra2box, miscbox; char opt[Limits_SaveFile_Option]; char ra1[Limits_SaveFile_Radios]; char ra2[Limits_SaveFile_Radios]; int showing = 0; int noopt = 0; int norad = 0; int width; int margin; /* If we haven't got the auto width marker, we shouldn't resize */ if (gadget_get_bbox(0, window, SaveFileAutoWidthMarker, &min)) return NULL; /* Use the right hand gap marker as a margin indicator */ RetError(gadget_get_bbox(0, window, SaveFileRightGapMarker, &max)); margin = (max.xmax - max.xmin) * 2; /* Use the bottom gap marker as a maximum width indicator */ RetError(gadget_get_bbox(0, window, SaveFileBottomGapMarker, &max)); /* Get the option and radio button label texts */ if ( optionbutton_get_label(0, window, SaveFileOption, opt, sizeof(opt), NULL) ) *opt = '\0'; else opt[sizeof(opt) - 1] = '\0'; if ( radiobutton_get_label (0, window, SaveFileRadio1, ra1, sizeof(ra1), NULL) ) *ra1 = '\0'; else ra1[sizeof(ra1) - 1] = '\0'; if ( radiobutton_get_label (0, window, SaveFileRadio2, ra2, sizeof(ra2), NULL) ) *ra2 = '\0'; else ra2[sizeof(ra2) - 1] = '\0'; /* Find the bounding boxes of the option and radio */ /* button gadgets to see what is visible right now */ if (gadget_get_bbox(0, window, SaveFileOption, &optbox)) noopt = 1; if (gadget_get_bbox(0, window, SaveFileRadio1, &ra1box)) norad = 1; if (gadget_get_bbox(0, window, SaveFileRadio2, &ra2box)) norad = 1; if (!noopt && optbox.xmin < optbox.xmax - optbox.xmin) showing = 1; else if (!norad && ra1box.xmin < ra1box.xmax - ra1box.xmin) showing = 2; /* Minimum width comes from the auto width marker, */ /* but this includes the margin width which we want */ /* to exclude for the moment. */ width = min.xmax - min.xmin - margin; if (showing == 1) { int text; /* Showing the option button, so work out width of it */ RetError(utils_text_width(opt, &text, 0)); text += 64; /* Hack :-) - overestimate of the option button size itself plus gap between button and label */ if (text > width) width = text; } else if (showing == 2) { int text1, text2; /* Showing the radio buttons, so work out width of them */ RetError(utils_text_width(ra1, &text1, 0)); RetError(utils_text_width(ra2, &text2, 0)); text1 += 64; /* Magic number as for 'showing == 1' case above */ text2 += 64; if (text1 > width) width = text1; if (text2 > width) width = text2; } if (width > max.xmax - max.xmin - margin) width = max.xmax - max.xmin - margin; /* We now know how wide things are to be. The writable gadget */ /* and option or radio buttons should be 'width', the draggable */ /* icon should just move to stay centred, to start with. */ if (!noopt) { optbox.xmax = optbox.xmin + width; RetError(gadget_move_gadget(0, window, SaveFileOption, &optbox)); } if (!norad) { ra1box.xmax = ra1box.xmin + width; ra2box.xmax = ra2box.xmin + width; RetError(gadget_move_gadget(0, window, SaveFileRadio1, &ra1box)); RetError(gadget_move_gadget(0, window, SaveFileRadio2, &ra2box)); } RetError(gadget_get_bbox(0, window, SaveFileWrit, &miscbox)); miscbox.xmax = miscbox.xmin + width; RetError(gadget_move_gadget(0, window, SaveFileWrit, &miscbox)); { int drag; RetError(gadget_get_bbox(0, window, SaveFileDrag, &miscbox)); drag = miscbox.xmax - miscbox.xmin; miscbox.xmin = (width + margin - drag) / 2; miscbox.xmax = miscbox.xmin + drag; RetError(gadget_move_gadget(0, window, SaveFileDrag, &miscbox)); } /* The OK and Cancel buttons - these have a 'margin' gap between */ /* them, and the OK button should be 8 wider than Cancel (it's */ /* the default action button, and has a thicker border). 16 is */ /* the technically correct value, but it doesn't look good! */ { int button; button = (width - margin - 8) / 2; RetError(gadget_get_bbox(0, window, SaveFileCancel, &ra1box)); RetError(gadget_get_bbox(0, window, SaveFileOK, &ra2box)); ra1box.xmax = ra1box.xmin + button; ra2box.xmin = ra1box.xmax + margin; ra2box.xmax = ra2box.xmin + button + 8; RetError(gadget_move_gadget(0, window, SaveFileCancel, &ra1box)); RetError(gadget_move_gadget(0, window, SaveFileOK, &ra2box)); } /* Finally, set the window width */ { WimpGetWindowStateBlock state; int open; /* Find out what the window vertical extent should be */ RetError(window_get_extent(0, window, &miscbox)); /* Set the width */ miscbox.xmax = miscbox.xmin + width + margin; RetError(window_set_extent(0, window, &miscbox)); /* We need to ensure the window is opened to full size, but it may be */ /* already open - so get the state first. */ RetError(window_get_wimp_handle(0, window, &state.window_handle)); RetError(wimp_get_window_state(&state)); /* Remember if it is open or not */ open = state.flags & WimpWindow_Open; /* Set the visible area to match the extent, extending */ /* it to the right and downwards. */ state.visible_area.xmax = state.visible_area.xmin + miscbox.xmax - miscbox.xmin; state.visible_area.ymin = state.visible_area.ymax - miscbox.ymax + miscbox.ymin; /* Show the window with these coordinates */ RetError(wimp_open_window((WimpOpenWindowBlock *) &state)); /* If it wasn't already open, close the window */ if (!open) RetError(wimp_close_window(&state.window_handle)); } /* Finished. */ return NULL; }