/* 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 : OpenURL.c */ /* */ /* Purpose: Functions relating to the Open URL */ /* dialogue box. */ /* */ /* Author : A.D.Hodgkinson */ /* */ /* History: 17-Apr-97: Created. */ /***************************************************/ #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 "svcprint.h" #include "Global.h" #include "FromROSLib.h" #include "MiscDefs.h" #include "Utils.h" #include "Browser.h" #include "Fetch.h" /* (For ISLINK macro) */ #include "FetchPage.h" #include "History.h" #include "Hotlist.h" #include "Menus.h" #include "URLutils.h" #include "Windows.h" #include "OpenURL.h" /* Local structures */ typedef struct openurl_contents { char url[Limits_URLBarWrit]; /* Where to open the URL to */ unsigned int in_this :1; unsigned int save_link :1; unsigned int in_new :1; unsigned int in_parent :1; } openurl_contents; /* Local statics */ // We *really* want to be mallocing this... static openurl_contents contents; /* Remember the old dialogue contents so the Cancel button can work */ static ObjectId window_id = 0; /* Remember the ID in case it needs closing 'out of the blue'. */ static ObjectId ancestor_id = 0; /* Remember the ancestor ID in case the ancestor closes. */ static HStream * open_hst = NULL; /* See openurl_to_show_from_menu */ /* Static function prototypes */ static _kernel_oserror * openurl_read_contents (ObjectId dialogue, openurl_contents * contents); static _kernel_oserror * openurl_set_contents (ObjectId dialogue, openurl_contents * contents); static int openurl_ok (int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle); static int openurl_cancel (int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle); static int openurl_radio_group_one (int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle); static int openurl_click (int eventcode, WimpPollBlock * b, IdBlock * idb, void * handle); static int openurl_key_handler (int eventcode, WimpPollBlock * b, IdBlock * idb, void * handle); /*************************************************/ /* openurl_read_contents() */ /* */ /* Reads the contents of the Open URL dialogue */ /* into an openurl_contents structure. */ /* */ /* Parameters: Object ID of the dialogue; */ /* */ /* Pointer to the structure to write */ /* to. */ /*************************************************/ static _kernel_oserror * openurl_read_contents(ObjectId dialogue, openurl_contents * contents) { _kernel_oserror * e; int state; /* Read the URL string */ *contents->url = 0; e = writablefield_get_value(0, dialogue, OpenWrit, contents->url, sizeof(contents->url), NULL); contents->url[sizeof(contents->url) - 1] = 0; /* (Ensure termination) */ if (e) return e; /* Read radio group 1 - where to open the URL */ RetError(radiobutton_get_state(0, dialogue, OpenInThis, &state, NULL)); contents->in_this = !!state; RetError(radiobutton_get_state(0, dialogue, OpenSaveLink, &state, NULL)); contents->save_link = !!state; RetError(radiobutton_get_state(0, dialogue, OpenInNew, &state, NULL)); contents->in_new = !!state; RetError(radiobutton_get_state(0, dialogue, OpenInParent, &state, NULL)); contents->in_parent = !!state; /* Finished */ return NULL; } /*************************************************/ /* openurl_set_contents() */ /* */ /* Sets the contents of the Open URL dialogue */ /* from an openurl_contents structure. */ /* */ /* Parameters: Object ID of the dialogue; */ /* */ /* Pointer to the structure to read */ /* from. */ /*************************************************/ static _kernel_oserror * openurl_set_contents(ObjectId dialogue, openurl_contents * contents) { /* The URL entry field */ RetError(writablefield_set_value(0, dialogue, OpenWrit, contents->url)); /* Radio group 1 - where to open the URL */ RetError(radiobutton_set_state(0, dialogue, OpenInThis, contents->in_this)); RetError(radiobutton_set_state(0, dialogue, OpenSaveLink, contents->save_link)); RetError(radiobutton_set_state(0, dialogue, OpenInNew, contents->in_new)); return radiobutton_set_state(0, dialogue, OpenInParent, contents->in_parent); } /*************************************************/ /* openurl_fill_in_url() */ /* */ /* If an external function wants to set the URL */ /* in the writable field of the Open URL */ /* dialogue, this is the function to use. */ /* */ /* Parameters: Pointer to the null terminated */ /* URL string. */ /*************************************************/ _kernel_oserror * openurl_fill_in_url(char * url) { if (!url || !window_id) return NULL; return writablefield_set_value(0, window_id, OpenWrit, url); } /*************************************************/ /* openurl_to_be_shown() */ /* */ /* Called when the EOpenToBeShownMisc event is */ /* generated, typically when the Open URL window */ /* is about to be shown. Handles any icon */ /* processing commands in the writable, */ /* registers event handlers, etc. */ /* */ /* Parameters are as standard for a Toolbox */ /* event handler. */ /*************************************************/ int openurl_to_be_shown(int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle) { char text[Limits_URLBarWrit]; /* In multiuser builds, must be logged in... */ #ifndef SINGLE_USER if (!logged_in) { toolbox_hide_object(0, idb->self_id); return 1; } #endif /* If the stored dialogue ID is non-zero on entry, the dialogue */ /* was reopened without closing - so get rid of the various */ /* event handlers before we reregister them. */ if (window_id) openurl_close(0, 1); /* Read the window ID and ancestor ID from the ID block */ window_id = idb->self_id; ancestor_id = idb->ancestor_id; /* Process the icon text */ ChkError(windows_process_component_text(idb->self_id, OpenWrit, text, sizeof(text), 0, 1)); /* Attach handlers for the various actions the window can perform */ ChkError(event_register_toolbox_handler(idb->self_id, EOpenOK, openurl_ok, NULL)); ChkError(event_register_toolbox_handler(idb->self_id, EOpenCancel, openurl_cancel, NULL)); ChkError(event_register_toolbox_handler(idb->self_id, EOpenRG1, openurl_radio_group_one, NULL)); ChkError(event_register_wimp_handler(idb->self_id, Wimp_EKeyPressed, openurl_key_handler, NULL)); ChkError(event_register_wimp_handler(idb->self_id, Wimp_EMouseClick, openurl_click, NULL)); /* Make sure the radios are up to date */ openurl_radio_group_one(eventcode, event, idb, handle); /* Read the existing contents into the static openurl_contents block */ ChkError(openurl_read_contents(idb->self_id, &contents)); /* Do we have a History? */ if (history_empty(NULL)) set_gadget_state(idb->self_id, OpenHistory, 1); else set_gadget_state(idb->self_id, OpenHistory, 0); /* Was the menu that generated this dialogue - if any - opened */ /* over a link? If so, write the URL to the dialogue. */ /* */ /* See openurl_to_show_from_menu for more details. */ if (open_hst) { if (ISLINK(open_hst)) { /* Don't put internal URLs in there! */ if (!urlutils_internal_extra(open_hst->anchor)) { StrNCpy0(contents.url, open_hst->anchor); ChkError(openurl_set_contents(idb->self_id, &contents)); } } else if ( open_hst->style & IMG || ( open_hst->tagno == TAG_INPUT && HtmlINPUTtype(open_hst) == inputtype_IMAGE ) ) { /* If it is an image, write the image source URL to the dialogue */ StrNCpy0(contents.url, open_hst->src); ChkError(openurl_set_contents(idb->self_id, &contents)); } else if (ISOBJECT(open_hst)) { const char * data; const char * current; browser_data * b = NULL; if (ancestor_id) toolbox_get_client_handle(0, idb->self_id, (void *) &b); if (b && !is_known_browser(b)) b = NULL; /* If it is an Object, write an appropriate URL in there, */ /* remembering to relativise it where possible. */ data = HtmlOBJECTdata(open_hst); if (!data) data = HtmlOBJECTcodebase(open_hst); if (b) { current = browser_fetch_url(b); if (!current) current = browser_current_url(b); if (current) { const char * newdata; newdata = HtmlRelativiseURL(current, data, open_hst); if (newdata) data = newdata; } } StrNCpy0(contents.url, data); ChkError(openurl_set_contents(idb->self_id, &contents)); } /* Clear the value */ open_hst = NULL; } return 1; } /*************************************************/ /* openurl_to_show_from_menu() */ /* */ /* Called when the EOpenToBeShownMenu event is */ /* generated, typically when the Open URL window */ /* is about to be shown from a menu item. */ /* */ /* To be able to show the URL of a link in the */ /* window, menu functions have to read the */ /* token the pointer is over and remember it. */ /* But to ensure this doesn't get out of date, */ /* the value is cleared when the menu is hidden. */ /* */ /* Unhelpfully, the Toolbox sends events out in */ /* an order that means the value is cleared */ /* before the EOpenToBeShownMisc event is */ /* raised. */ /* */ /* To get round that, the menu entry itself */ /* raises EOpenToBeShownMenu, so this function */ /* is called. It reads the value and stores it */ /* locally - great... Two copies hanging around. */ /* Anyway, when the EOpenToBeShownMisc event */ /* finally arrives, openurl_to_be_shown is */ /* called, reads the value set here, acts on it, */ /* and clears the value to ensure correct */ /* behaviour on future calls. */ /* */ /* All this because the events arrive in a silly */ /* order. Sigh. */ /* */ /* Parameters are as standard for a Toolbox */ /* event handler. */ /*************************************************/ int openurl_to_show_from_menu(int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle) { open_hst = menus_document_opened_over(); return 1; } /*************************************************/ /* openurl_update_popup() */ /* */ /* Ensures that the greyed/ungreyed state of the */ /* History menu popup in the Open URL dialogue */ /* is up to date. */ /*************************************************/ void openurl_update_popup(void) { if (!window_id) return; if (history_empty(NULL)) set_gadget_state(window_id, OpenHistory, 1); else set_gadget_state(window_id, OpenHistory, 0); } /*************************************************/ /* openurl_ok() */ /* */ /* Handles clicks on the 'OK' button in the */ /* Open URL dialogue. */ /* */ /* Parameters are as standard for a Toolbox */ /* event handler. */ /*************************************************/ static int openurl_ok(int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle) { openurl_contents localcontents; WimpGetPointerInfoBlock info; browser_data * b; browser_data * ancestor; /* Work out where the dialogue came from */ ChkError(wimp_get_pointer_info(&info)); if (!idb->ancestor_id) b = ancestor = NULL; else { ChkError(toolbox_get_client_handle(0, idb->ancestor_id, (void *) &b)); if (is_known_browser(b)) ancestor = b->ancestor; else ancestor = b = NULL; } /* Read the dialogue contents */ ChkError(openurl_read_contents(idb->self_id, &localcontents)); /* If Select was pressed, the dialogue will close, so */ /* remember the current contents for future reference */ /* (no button => Return was pressed) */ if ((info.button_state & Wimp_MouseButtonSelect) || !info.button_state) contents = localcontents; /* Fetch the indicated URL */ if (*localcontents.url) { /* If asked to open in a new window or saving the object, */ /* open a new window (this will be a small fetch window */ /* in the latter case). */ if (localcontents.in_new || localcontents.save_link || !b) { ChkError(windows_create_browser(localcontents.url, NULL, NULL, NULL, localcontents.save_link ? Windows_CreateBrowser_SaveToFile : Windows_CreateBrowser_Normal)); } /* Otherwise, open the URL in the browser window from which */ /* the dialogue was obtained in the first place. */ else { browser_data * fetch; /* Work out which existing window to fetch to */ if (localcontents.in_this || localcontents.save_link) fetch = b; else fetch = ancestor; /* Set that window's save_link and allow_cancel */ /* flags according to the dialogue contents */ fetch->save_link = localcontents.save_link; if (fetch->save_link) b->allow_cancel = 0; /* Initiate the fetch */ ChkError(fetchpage_new(fetch, localcontents.url, 1, 1)); } } if ((info.button_state & Wimp_MouseButtonSelect) || !info.button_state) { ChkError(openurl_close(0, 0)); } return 1; } /*************************************************/ /* openurl_cancel() */ /* */ /* Handles clicks on the 'Cancel' button in the */ /* Open URL dialogue. */ /* */ /* Parameters are as standard for a Toolbox */ /* event handler. */ /*************************************************/ static int openurl_cancel(int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle) { WimpGetPointerInfoBlock info; /* Restore the old contents */ ChkError(openurl_set_contents(window_id, &contents)); /* If Select was pressed, the dialogue should close. */ /* (No button => Escape was pressed). */ ChkError(wimp_get_pointer_info(&info)); if ((info.button_state & Wimp_MouseButtonSelect) || !info.button_state) { ChkError(openurl_close(0, 0)); } return 1; } /*************************************************/ /* openurl_close() */ /* */ /* If the Open URL dialogue is opened, this will */ /* close it, deregistering any associated event */ /* handlers. */ /* */ /* 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 * openurl_close(ObjectId ancestor, int do_not_close) { _kernel_oserror * e = NULL; if (ancestor && ancestor != ancestor_id) return NULL; if (window_id) { /* Deregister associated event handlers */ e = event_deregister_toolbox_handlers_for_object(window_id); if (e) goto openurl_close_exit; e = event_deregister_wimp_handlers_for_object(window_id); if (e) goto openurl_close_exit; /* Restore the old contents */ e = openurl_set_contents(window_id, &contents); if (e) goto openurl_close_exit; /* Close the dialogue */ if (!do_not_close) e = toolbox_hide_object(0, window_id); } openurl_close_exit: ancestor_id = window_id = 0; return e; } /*************************************************/ /* openurl_radio_group_one() */ /* */ /* Looks at radio group one and greys or ungreys */ /* items as required. Usually used as a handler */ /* for when the selection therein changes, but */ /* can be used in a more general purpose way. */ /* */ /* Parameters are as standard for a Toolbox */ /* event handler. */ /*************************************************/ static int openurl_radio_group_one(int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle) { _kernel_oserror * e; browser_data * b; browser_data * ancestor; int state; /* Note that few errors are reported here - this allows items to be */ /* removed from the window without causing errors to appear. */ if (!idb->ancestor_id) b = ancestor = NULL; else { ChkError(toolbox_get_client_handle(0, idb->ancestor_id, (void *) &b)); if (is_known_browser(b)) ancestor = b->ancestor; else ancestor = b = NULL; } /* If b is NULL, this was not opened from a browser window */ if (!b) { /* Grey out the gadget. Try to read its state, and, if this */ /* succeds and the gadget was selected, then select the */ /* 'Open in new window' gadget instead - this will never be */ /* greyed out. */ gadget_set_flags(0, idb->self_id, OpenInThis, Gadget_Faded); e = radiobutton_get_state(0, idb->self_id, OpenInThis, &state, NULL); if (!e && state) radiobutton_set_state(0, idb->self_id, OpenInNew, 1); } else gadget_set_flags(0, idb->self_id, OpenInThis, 0); /* If ancestor is NULL, this was not opened from a frame */ if (!ancestor) { gadget_set_flags(0, idb->self_id, OpenInParent, Gadget_Faded); e = radiobutton_get_state(0, idb->self_id, OpenInParent, &state, NULL); if (!e && state) radiobutton_set_state(0, idb->self_id, OpenInNew, 1); } else gadget_set_flags(0, idb->self_id, OpenInParent, 0); return 1; } /*************************************************/ /* openurl_click() */ /* */ /* Handles Wimp mouse click events in the Open */ /* URL dialogue. */ /* */ /* Parameters are as standard for a Wimp event */ /* handler. */ /*************************************************/ static int openurl_click(int eventcode, WimpPollBlock * b, IdBlock * idb, void * handle) { int used = 0; switch (idb->self_component) { case OpenHistory: { ChkError(history_menu_popup(NULL, idb->self_id, idb->self_component, 1, b->mouse_click.buttons & Wimp_MouseButtonAdjust ? !choices.show_urls : choices.show_urls)); used = 1; } break; } return used; } /*************************************************/ /* openurl_key_handler() */ /* */ /* Handles a few key pressed events in the Open */ /* URL dialogue. */ /* */ /* Parameters are as standard for a Wimp event */ /* handler. */ /*************************************************/ static int openurl_key_handler(int eventcode, WimpPollBlock * b, IdBlock * idb, void * handle) { _kernel_oserror * e; int key; key = ((WimpKeyPressedEvent *) b)->key_code; switch (key) { case akbd_TabK + akbd_Ctl: { char url[Limits_URLBarWrit]; int changed = 0; /* Read whatever is in the URL bar writable */ *url = 0; e = writablefield_get_value(0, idb->self_id, OpenWrit, url, sizeof(url), NULL); if (!e) { url[sizeof(url) - 1] = 0; /* (Ensure termination) */ /* Try and find something appropriate in the hotlist, */ /* then the history. */ #ifndef REMOTE_HOTLIST changed = hotlist_find_match(url, sizeof(url)); #endif if (!changed) changed = history_find_match(url, sizeof(url)); if (changed) { /* Update the URL writable */ writablefield_set_value(0, idb->self_id, OpenWrit, url); } } key = 0; } break; case akbd_TabK + akbd_Sh: { char url[Limits_URLBarWrit]; int changed; /* Read whatever is in the URL bar writable */ *url = 0; e = writablefield_get_value(0, idb->self_id, OpenWrit, url, sizeof(url), NULL); if (!e) { url[sizeof(url) - 1] = 0; /* (Ensure termination) */ /* Cycle the protocol specifier (adding one in if not already present) */ changed = urlutils_cycle_protocol(url, sizeof(url)); if (changed) { /* Update the URL writable */ writablefield_set_value(0, idb->self_id, OpenWrit, url); } } key = 0; } break; } if (key) wimp_process_key(key); return 1; }