/* 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 : Handlers.c */ /* */ /* Purpose: Event handlers for driving the browser */ /* front-end. */ /* */ /* Author : A.D.Hodgkinson */ /* */ /* History: 07-Feb-97: Created from Main.h. */ /***************************************************/ #include <stdlib.h> #include <stdio.h> #include <string.h> #include <ctype.h> #include "swis.h" #include "kernel.h" #include "flex.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 "quit.h" #include "proginfo.h" #include "Dialler.h" #include "svcprint.h" #include "Global.h" #include "FromROSLib.h" #include "Utils.h" #include "Browser.h" #include "ChoiceDefs.h" #include "Choices.h" #include "CSIM.h" #include "Fetch.h" /* (For ISOBJECT macro) */ #include "FetchPage.h" #include "Filetypes.h" #include "FontManage.h" #include "Forms.h" #include "Frames.h" #include "History.h" #include "Hotlist.h" #include "Images.h" #include "JavaScript.h" #include "Menus.h" #include "Meta.h" #include "MiscDefs.h" #include "Mouse.h" #include "PlugIn.h" #include "Printing.h" #include "Protocols.h" #include "Redraw.h" #include "Reformat.h" #include "Save.h" #include "SaveFile.h" #include "SetPBoxes.h" #include "TokenUtils.h" #include "Toolbars.h" #include "URLutils.h" #include "Windows.h" #include "Handlers.h" /* Local statics */ static int last_click_x; static int last_click_y; /* Static function prototypes */ static void handlers_get_call_info (browser_data ** bp, ObjectId * op, IdBlock * idb, ComponentId button); static int handlers_menu_or_toolbar (IdBlock * idb); static void handle_go_to_with_key (browser_data * b, char c, int clear); /*************************************************/ /* handlers_menu_or_toolbar() */ /* */ /* Returns 1 if an event came from a menu, else */ /* 0 if it was a toolbar (only call this where */ /* these are the only two possibilities!). */ /* */ /* Parameters: Pointer to the event ID block. */ /* */ /* Returns: 1 if the event came from a menu, */ /* else 0. */ /*************************************************/ static int handlers_menu_or_toolbar(IdBlock * idb) { _kernel_oserror * e; ObjectId tt, tb, sw; /* If we can't get toolbars on the ancestor, there are no */ /* toolbars so the event certainly isn't from a toolbar. */ /* So if the call gives an error, return 1. */ e = window_get_tool_bars(InternalTopLeft | InternalBottomLeft, idb->ancestor_id, &tb, &tt, NULL, NULL); if (e) return 1; if (controls.swap_bars) sw = tt, tt = tb, tb = sw; /* If the object ID of the event generator matches either toolbar */ /* object ID, the event came from that toolbar; else, from a menu. */ if (idb->self_id == tt || idb->self_id == tb) return 0; else return 1; } /*************************************************/ /* handlers_get_call_info() */ /* */ /* For button handlers, a standard set of calls */ /* is used for each handler - find out the */ /* browser_data struct and toolbar, debounce any */ /* keypress and if this had to be done slab the */ /* button manually. */ /* */ /* Parameters: Pointer to a pointer to a */ /* browser_data struct which will be */ /* filled with a browser_data * for */ /* the event's underlying browser */ /* window; */ /* */ /* Pointer to an ObjectId into which */ /* the object ID of the toolbar from */ /* which the event came is placed; */ /* Pointer to the event's ID block; */ /* */ /* ComponentId of the button. */ /* */ /* Assumes: Any pointer may be NULL, but the */ /* caller should check that returned */ /* values are sensible. */ /*************************************************/ static void handlers_get_call_info(browser_data ** bp, ObjectId * op, IdBlock * idb, ComponentId button) { browser_data * b; ObjectId t; int key; /* If the object has an ancestor, the event was from a toolbar, */ /* else it was from the window itself (e.g. due to a keyboard */ /* shortcut being activated). */ _swix(OS_Byte, _INR(0,1) | _OUT(1), 121, 0, &key); if (toolbox_get_client_handle(0, idb->ancestor_id, (void *) &b)) { ChkError(toolbox_get_client_handle(0, idb->self_id, (void *) &b)); t = toolbars_get_upper(b); if (key != 255) { slab_gadget_in(t, button); debounce_keypress(); slab_gadget_out(t, button); } } else { t = toolbars_get_upper(b); if (key != 255) { slab_gadget_in(t, button); debounce_keypress(); slab_gadget_out(t, button); } } if (op) *op = t; if (bp) *bp = b; } /*************************************************/ /* handle_messages() */ /* */ /* Deal with Wimp messages for reason codes 17 */ /* and 18 (type 19, acknowledges, are handled in */ /* handle_ack below). Parameters are as standard */ /* for a Wimp message handler. */ /* */ /* This function farms off a lot of its */ /* responsibilities to functions in Protocols.c. */ /*************************************************/ int handle_messages(WimpMessage * m, void * handle) { #ifndef SINGLE_USER /* Multiuser builds will refuse to handle various message types */ int complain = 0; #define ChkLogin if (!logged_in) {complain = 1; break;} else #else #define ChkLogin {} #endif switch (m->hdr.action_code) { case Wimp_MQuit: quit = 1; break; case Wimp_MMenusDeleted: menusrc = Menu_None; break; case Wimp_MHelpReply: { ChkError(protocols_ih_got_help_reply(m)); } break; case Wimp_MModeChange: { browser_data * b; modechanged = 1; ChkError(image_mode_change()); if (!printing) wimpt_read(); read_os_to_points(); /* Handles the 'printing' flag internally */ b = last_browser; while (b) { ChkError(fm_rescale_fonts(b)); b = b->previous; } ChkError(windows_initialise_tool_sizes()); ChkError(choices_mode_change()); } break; case Wimp_MAppControl: { /* AppControl message - stop all activity */ if ( m->data.app_control.reason == Wimp_MAppControl_Stop && m->hdr.sender != task_handle ) { browser_data * b; IdBlock idb; b = last_browser; while (b) { idb.ancestor_id = 0; idb.self_id = b->self_id; handle_stop(0, NULL, &idb, NULL); b = b->previous; } } } break; /* App to app transfer (Message_DataSaveAck is in the multiple source section) */ case Wimp_MDataLoadAck: ChkLogin ChkError(protocols_atats_got_data_load_ack(m)); break; case Wimp_MRAMFetch: ChkLogin ChkError(protocols_atats_got_ram_fetch(m)); break; case Wimp_MDataOpen: ChkError(protocols_atatl_got_data_open(m)); break; case Wimp_MDataLoad: { /* Allow certain browser systems to get a look at the message first */ #ifndef SINGLE_USER if (setpboxes_check_message(m)) break; #endif ChkLogin ChkError(protocols_atatl_got_data_load(m)); } break; case Wimp_MDataSave: ChkLogin ChkError(protocols_atatl_got_data_save(m)); break; case Wimp_MRAMTransmit: ChkLogin ChkError(protocols_atatl_got_ram_transmit(m)); break; /* Printing protocol (Message_DataSaveAck is in the multiple source section) */ case Browser_Message_PrintError: ChkLogin ChkError(protocols_pp_got_print_error(m)); break; case Browser_Message_PrintTypeOdd: ChkLogin ChkError(protocols_pp_got_print_type_odd(m)); break; /* Multiple source messages */ case Wimp_MDataSaveAck: ChkLogin ChkError(protocols_multi_got_data_save_ack(m)); break; /* URI handler */ case URI_MProcess: ChkError(protocols_auh_got_process(m)); break; case URI_MReturnResult: ChkError(protocols_auh_got_return_result(m)); break; case URI_MDying: ChkError(protocols_auh_got_dying(m)); break; /* ANT URL broadcast protocol */ case Message_ANTOpenURL: ChkLogin ChkError(protocols_aub_got_open_url(m)); break; /* Plug-In protocol */ case Message_PlugIn_Opening: ChkLogin ChkError(plugin_got_opening(m)); break; case Message_PlugIn_URLAccess: ChkLogin ChkError(plugin_got_url_access(m)); break; case Message_PlugIn_StreamNew: ChkLogin ChkError(plugin_got_stream_new(m)); break; case Message_PlugIn_ReshapeRequest: ChkLogin ChkError(plugin_got_reshape_request(m)); break; case Message_PlugIn_Status: ChkLogin ChkError(plugin_got_status(m)); break; case Message_PlugIn_Busy: ChkLogin ChkError(plugin_got_busy(m)); break; // For debugging. This should only ever bounce, i.e. go through handle_ack. // // case Message_PlugIn_Open: // { // MPlugIn_Open * open = (MPlugIn_Open *) &m->data; // // Printf("PlugIn_Open filename: '%s'\n", plugin_return_string(m, &open->file_name)); // // { // char combuf[4096]; // // sprintf(combuf, "Copy %s ADFS::4.$.ParamsFile ~Q~V~C~NF", plugin_return_string(m, &open->file_name)); // // _swix(OS_CLI, // _IN(0), // // combuf); // } // } // break; default: return 0; break; } #ifndef SINGLE_USER if (complain) { erb.errnum = Utils_Error_Custom_Message; StrNCpy0(erb.errmess, lookup_token("MustLogIn:The browser cannot fetch Web pages until you log in.", 0, 0)); show_error_ret(&erb); } #endif return 1; } /*************************************************/ /* handle_ack() */ /* */ /* Handles UserMessage_Acknowledge from the */ /* Wimp (message bouncing, etc.). */ /* */ /* Parameters are as standard for a Wimp event */ /* handler. */ /*************************************************/ int handle_ack(int eventcode, WimpPollBlock * block, IdBlock * idb, void * handle) { WimpMessage * m = &block->user_message_acknowledge; switch (m->hdr.action_code) { case Browser_Message_PrintSave: ChkError(protocols_pp_print_save_bounced(m)); break; case Wimp_MHelpRequest: ChkError(protocols_ih_help_request_bounced(m)); break; case Wimp_MDataLoad: ChkError(protocols_atats_data_load_bounced(m)); break; case Wimp_MDataOpen: ChkError(protocols_atats_data_open_bounced(m)); break; case Wimp_MRAMTransmit: ChkError(protocols_atats_ram_transmit_bounced(m)); break; case Wimp_MRAMFetch: ChkError(protocols_atatl_ram_fetch_bounced(m)); break; case Message_PlugIn_Open: ChkError(plugin_open_bounced(m)); break; case Message_PlugIn_StreamNew: ChkError(plugin_stream_new_bounced(m)); break; default: { return 0; } break; } return 1; } /*************************************************/ /* handle_keys() */ /* */ /* Deal with keyboard pressed events from the */ /* Wimp. Parameters are as standard for a Wimp */ /* event handler. */ /*************************************************/ int handle_keys(int eventcode, WimpPollBlock * block, IdBlock * idb, void * handle) { browser_data * b = NULL; browser_data * ancestor = NULL; browser_data * curframe = NULL; _kernel_oserror * e; int key; /* Get the browser_data structure associated with either this object's */ /* ancestor, if it has one, or this object directly, if not. If either */ /* call fails this isn't a keypress from an ancestor object obtained */ /* from a browser window and it isn't from a browser window directly. */ if (idb->ancestor_id) e = toolbox_get_client_handle(0, idb->ancestor_id, (void *) &b); else e = toolbox_get_client_handle(0, idb->self_id, (void *) &b); /* Some key presses may come from windows that have non-zero */ /* client handles which aren't browser_data struct pointers, */ /* e.g. a print dialogue with an animation in it will */ /* return an animation frame. So if b is non-zero but not a */ /* known browser_data struct pointer, set it to NULL so that */ /* later routines can quickly know there is no valid pointer */ /* available. */ if (b && !is_known_browser(b)) b = NULL; if (b) { ancestor = utils_ancestor(b); curframe = ancestor->selected_frame; if (!curframe) curframe = b; } key = ((WimpKeyPressedEvent *) block)->key_code; /* Is this from a URL bar? To find out, get the toolbar ID of */ /* this object, if possible. */ if (!e && idb->ancestor_id) { ObjectId i; i = toolbars_get_upper(b); if (!e && i == idb->self_id) { /* Make sure we have a client handle for the underlying browser */ /* window before attempting to proceed */ if (b) { switch (key) { /* Scrolling the page. Remember, keypresses trapped here */ /* are from a toolbar object (probably the URL writable) */ /* so certain key presses - such as Copy / End to go to */ /* the bottom of the page - should *not* be trapped, as */ /* they have other meanings (e.g. in the above example, */ /* delete character to the right). */ case akbd_UpK: case akbd_DownK: case akbd_PageUpK: case akbd_PageDownK: case akbd_HomeK: case akbd_UpK + akbd_Ctl: case akbd_UpK + akbd_Ctl + akbd_Sh: case akbd_DownK + akbd_Ctl: case akbd_DownK + akbd_Ctl + akbd_Sh: { if (!browser_scroll_page_by_key(curframe, key, NULL)) { key = 0; _swix(OS_Byte, _INR(0,1), 21, 0); /* Flush keyboard buffer */ } } break; case akbd_LeftK: case akbd_LeftK + akbd_Sh: case akbd_LeftK + akbd_Ctl: case akbd_LeftK + akbd_Sh + akbd_Ctl: { /* For left, only scroll when the caret is at the start of the */ /* string in the URL writable. */ if (((WimpKeyPressedEvent *) block)->caret.index == 0) { if (!browser_scroll_page_by_key(curframe, key, NULL)) { key = 0; _swix(OS_Byte, _INR(0,1), 21, 0); /* Flush keyboard buffer */ } } } break; case akbd_RightK: case akbd_RightK + akbd_Sh: case akbd_RightK + akbd_Ctl: case akbd_RightK + akbd_Sh + akbd_Ctl: { /* For right, only scroll when the caret is at the end of the */ /* string in the URL writable. */ char writable[Limits_URLBarWrit]; int len; *writable = 0; writablefield_get_value(0, idb->self_id, URLBarWrit, writable, sizeof(writable), &len); writable[sizeof(writable) - 1] = 0; /* (Ensure termination) */ if (((WimpKeyPressedEvent *) block)->caret.index >= len) { if (!browser_scroll_page_by_key(curframe, key, NULL)) { key = 0; _swix(OS_Byte, _INR(0,1), 21, 0); /* Flush keyboard buffer */ } } } break; case akbd_TabK: { if (choices.keyboard_ctrl) { wimp_set_caret_position(ancestor->window_handle, -1, 0, 0, -1, -1); /* If there's no selected token, select one */ if (!ancestor->selected || !ancestor->selected_owner) { /* Ensure *both* values are NULL (sanity check) */ ancestor->selected = NULL; ancestor->selected_owner = NULL; /* Select a token */ browser_move_selection(curframe, akbd_DownK); } else { /* If there's a selected token, does it belong to this */ /* window? */ if (ancestor->selected_owner == curframe) { WimpGetWindowStateBlock s; /* If it belongs to this window but it's not visible, */ /* find a new token that is. */ s.window_handle = curframe->window_handle; if (!wimp_get_window_state(&s) && !browser_check_visible(b, &s, ancestor->selected)) { browser_clear_selection(curframe, 0); browser_move_selection(curframe, akbd_DownK); } } // else // { // /* The selected token doesn't belong to this window, */ // /* so select a new one here. */ // // browser_move_selection(b, akbd_DownK); // } } } // This is unfinished - it only works if the form has already // had an input focus at least once, and won't scroll the page // if the focus should drop off it. else form_give_focus(curframe); key = 0; } break; case akbd_TabK + akbd_Ctl: { char url[Limits_URLBarWrit]; int changed = 0; /* Read whatever is in the URL bar writable */ *url = 0; ChkError(writablefield_get_value(0, i, URLBarWrit, url, sizeof(url), NULL)); 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, i, URLBarWrit, 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; ChkError(writablefield_get_value(0, i, URLBarWrit, url, sizeof(url), NULL)); 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, i, URLBarWrit, url); } key = 0; } break; case 0x00d: { char url[Limits_URLBarWrit]; /* Read the new URL from the URL bar writable */ *url = 0; ChkError(writablefield_get_value(0, i, URLBarWrit, url, sizeof(url), NULL)); url[sizeof(url) - 1] = 0; /* (Ensure termination) */ #ifdef ALIAS_URLS // Not implemented yet... #endif /* If we're displaying a temporary file and the URL hasn't changed, */ /* treat this as an attempt to reload the page. */ if ( b->displayed == Display_Scrap_File && !strcmp(browser_current_url(b), url) ) { erb.errnum = Utils_Error_Custom_Message; StrNCpy0(erb.errmess, lookup_token("CantReload:This page cannot be reloaded, as it was sent directly from another application.", 0, 0)); show_error_ret(&erb); } else { #ifdef HIDE_CGI /* If HIDE_CGI is defined, the URL bar may have only part of */ /* the URL in it - the CGI information could be stripped off. */ /* In that case, check if the URL matches the browser's current */ /* one with the exception of the CGI stuff, and only do the */ /* fetch if not. */ if ( browser_current_url(b) && /* If there *is* a current URL, and */ !strncmp(browser_current_url(b), url, strlen(url)) && /* the URL bar matches the start of it... */ strlen(browser_current_url(b)) > strlen(url) && /* ...but there's more of the current URL left */ browser_current_url(b)[strlen(url)] == '?' /* ...and the first extra character is a '?', */ ) ChkError(fetchpage_new(b, browser_current_url(b), 1, 1)); /* ...then fetch the current URL instead. */ else ChkError(fetchpage_new(b, url, 1, 1)); /* Otherwise do what the user asked! */ #else /* Start the new fetch */ ChkError(fetchpage_new(b, url, 1, 1)); #endif } key = 0; } break; } } } } if (key) wimp_process_key(key); return 1; } /*************************************************/ /* handle_keys_from_browser() */ /* */ /* Called when a Wimp key pressed event is */ /* generated for a specific browser window. */ /* Parameters are as standard for a Wimp event */ /* handler. */ /*************************************************/ int handle_keys_from_browser(int eventcode, WimpPollBlock * block, IdBlock * idb, browser_data * handle) { int key = ((WimpKeyPressedEvent *) block)->key_code; browser_data * ancestor; browser_data * curframe; if (!handle) return 0; ancestor = utils_ancestor(handle); curframe = ancestor->selected_frame; if (!curframe) curframe = handle; form_process_key(handle, &key); /* If 'key' is non-zero, the forms library couldn't handle it */ switch (key) { /* Scrolling the page. Here, unlike the code above, the */ /* keypress is straight off the browser page, so unless */ /* the forms library has trapped the key, things such */ /* as left/right and copy/end can be used to move the */ /* page around as well as up/down etc. */ case akbd_PageUpK: case akbd_PageDownK: case akbd_HomeK: case akbd_CopyK: /* Or 'End' */ case akbd_UpK: case akbd_UpK + akbd_Ctl: case akbd_UpK + akbd_Ctl + akbd_Sh: case akbd_DownK: case akbd_DownK + akbd_Ctl: case akbd_DownK + akbd_Ctl + akbd_Sh: case akbd_LeftK: case akbd_LeftK + akbd_Sh: case akbd_LeftK + akbd_Ctl: case akbd_LeftK + akbd_Ctl + akbd_Sh: case akbd_RightK: case akbd_RightK + akbd_Sh: case akbd_RightK + akbd_Ctl: case akbd_RightK + akbd_Ctl + akbd_Sh: { int limit; if (browser_move_selection(curframe, key)) { key = 0; _swix(OS_Byte, _INR(0,1), 21, 0); /* Flush keyboard buffer */ /* Rehighlight the frame if required */ if (controls.keep_highlight) frames_highlight_frame(curframe); } else { if (!browser_scroll_page_by_key(curframe, key, &limit)) { /* If limit is set, the window is scrolled as far as it */ /* will go - can we jump to another frame, then? (Only */ /* jumping out for certain key presses, though). */ if ( limit && ( key == akbd_UpK || key == akbd_DownK ) ) { browser_data * next = frames_find_another_frame(curframe, key == akbd_UpK ? 1 : 0); if (next) { /* Another frame has been identified */ browser_clear_selection(curframe, 0); /* Scroll the frame appropriately */ browser_scroll_page_v(next, NULL, key == akbd_UpK ? 0 : 1, 0, 0, 0x1000000, NULL); /* Make sure the ancestor(s) are updated */ ancestor->selected_frame = NULL; ancestor = utils_ancestor(next); ancestor->selected_frame = curframe = next; /* Find a selectable */ browser_move_selection(curframe, key); /* Highlight the frame */ frames_highlight_frame(curframe); } } else { /* Scrolled page - rehighlight the frame if required */ if (controls.keep_highlight) frames_highlight_frame(curframe); } } key = 0; _swix(OS_Byte, _INR(0,1), 21, 0); } } break; case akbd_TabK: if (!browser_give_general_focus(curframe)) key = 0; break; case 0x00d: { browser_data * owner; owner = ancestor->selected_owner; if (ancestor->selected && browser_check_visible(owner, NULL, ancestor->selected)) { /* If an item is selected and at least partially visible, */ /* act as if it were clicked upon with Select */ // Optimise this? Lose the check for IMG, and rely on // HTMLLib returning valid information? if ( ( ( (ancestor->selected->style & IMG) && (ancestor->selected->type & TYPE_ISMAP) ) || ( ancestor->selected->type & TYPE_ISCLIENTMAP ) ) && !ancestor->in_image_map ) { /* For image maps, if not already selected, start keyboard */ /* navigation of the map. */ BBox box; HStream * map = ancestor->selected; if (!image_get_token_image_size(owner, map, &box)) { WimpGetWindowStateBlock s; int x, y; s.window_handle = owner->window_handle; ChkError(wimp_get_window_state(&s)); if (!image_get_token_image_position(owner, map, &x, &y)) { x = coords_x_toscreen(x, (WimpRedrawWindowBlock *) &s); y = coords_y_toscreen(y, (WimpRedrawWindowBlock *) &s); box.xmin += x + 2; box.ymin += y + 2; box.xmax += x - 4; box.ymax += y - 4; // Re: Next line of code. // // Why? Can't easily force rectangle constraint, as // whenever the pointer changes shape it is unconstrained. // So the navigation function has to handle being called // with the pointer moved off the map (and it does); so // this isn't wanted, really... // // Still, left in just in case it's needed for something! // // /* Constrain the pointer to the image map rectangle */ // // mouse_rectangle(&box, 1); /* Move x and y to the middle of the image map */ x += (box.xmax - box.xmin) / 2, y += (box.ymax - box.ymin) / 2; /* Move pointer, and update internal details of what pointer is over */ mouse_to(x, y, 0); owner->pointer_over = ancestor->pointer_over = map; browser_pointer_check(0, NULL,NULL, owner); mouse_set_pointer_shape(Mouse_Shape_Map); mouse_watch_pointer_control(0); mouse_pointer_on(); owner->in_image_map = ancestor->in_image_map = 1; return 1; } } } else { /* Not an image map, or a selected map - so follow the link. */ owner->pointer_over = NULL; browser_pointer_check(0, NULL,NULL, owner); if (choices.keyboard_ctrl) { mouse_pointer_off(); mouse_watch_pointer_control(1); } handle_link_clicks(-1, NULL, NULL, owner); } } } break; default: { /* In merged status bar situations, want alphanumeric characters */ /* to pop up the URL writable with that key in it. */ if (isalnum(key)) /* Wimp_ProcessKey numbers are equal to ASCII codes for alphanumerics */ { handle_go_to_with_key(handle, (char) key, controls.clear_first); key = 0; } } } if (key) ChkError(wimp_process_key(key)); return 1; } /*************************************************/ /* handle_menus() */ /* */ /* Deal with menu selection events from the Wimp */ /* (for forms etc.). Parameters are as standard */ /* for a Wimp event handler. */ /*************************************************/ int handle_menus(int eventcode, WimpPollBlock * block, IdBlock * idb, void * handle) { switch (menusrc) { case Menu_Form: { form_select_menu_event(block); } break; case Menu_LocalHist: /* Same as for global, so no break */ case Menu_GlobalHist: { ChkError(history_menu_selection((browser_data *) menuhdl, block)); } break; default: return 0; } return 1; } /*************************************************/ /* handle_scroll_requests() */ /* */ /* Deal with Scroll Request events from the Wimp */ /* (e.g. for page up/down). Parameters are as */ /* standard for a Wimp event handler. */ /*************************************************/ int handle_scroll_requests(int eventcode, WimpPollBlock * b, IdBlock * idb, browser_data * handle) { if (b->scroll_request.yscroll) { ChkError(browser_scroll_page_v(handle, &b->scroll_request.open, b->scroll_request.yscroll > 0, b->scroll_request.yscroll == 2 || b->scroll_request.yscroll == -2, b->scroll_request.yscroll == 1 || b->scroll_request.yscroll == -1, 0, NULL)); } if (b->scroll_request.xscroll) { ChkError(browser_scroll_page_h(handle, &b->scroll_request.open, b->scroll_request.xscroll < 0, b->scroll_request.xscroll == 2 || b->scroll_request.xscroll == -2, b->scroll_request.xscroll == 1 || b->scroll_request.xscroll == -1, 0, NULL)); } return 1; } /*************************************************/ /* handle_clicks() */ /* */ /* Deal with mouse click events from the wimp, */ /* for specific object IDs. Parameters are as */ /* standard for a Wimp event handler. */ /*************************************************/ int handle_clicks(int eventcode, WimpPollBlock * b, IdBlock * idb, browser_data * handle) { int shift; /* Is Shift being pressed? */ _swix(OS_Byte, _INR(0,1) | _OUT(1), 121, 128, &shift); /* Process the event only if the browser_data structure contents */ /* match the ancestor ID of the item clicked upon - i.e. if a */ /* toolbar has been clicked upon. */ if (idb->ancestor_id == handle->self_id) { /* If the toolbox hasn't filled in the component ID, e.g. because */ /* icon flags were forced to change to give a button type the */ /* Toolbox didn't expect, fill it in now. */ if (idb->self_component == -1) { if ( window_wimp_to_toolbox(0, b->mouse_click.window_handle, b->mouse_click.icon_handle, &idb->self_id, &idb->self_component) ) return 0; } switch (idb->self_component) { case StatusBarStatusCover: /* Clicking on a covering gadget is equivalent */ case StatusBarStatus: /* to clicking on the underlying gadget instead */ { /* If the URL writable and status display are merged, want to */ /* now swap the display for the writable and put the caret in */ /* the field. */ if (handle->merged_url) { toolbars_merged_to_url(handle, idb->self_id); gadget_set_focus(0, idb->self_id, URLBarWrit); } } break; case URLBarHistoryMenuL: /* Drop through to URLBarHistoryMenuR case */ case URLBarHistoryMenuR: { ChkError(history_menu_popup(handle, idb->self_id, idb->self_component, shift, b->mouse_click.buttons & Wimp_MouseButtonAdjust ? !choices.show_urls : choices.show_urls)); } break; } /* Grey / ungrey buttons, as the state may change as */ /* a result of being selected. */ toolbars_set_button_states(handle); return 1; } return 0; } /*************************************************/ /* handle_link_clicks() */ /* */ /* Deal with mouse click events from the wimp, */ /* on gadgets in the browser window. Parameters */ /* are as standard for a Wimp event handler. */ /*************************************************/ int handle_link_clicks(int eventcode, WimpPollBlock * b, IdBlock * idb, browser_data * handle) { HStream * p = NULL; int ox, oy, adj, used = 0; int shift, ctrl; WimpGetPointerInfoBlock i; browser_data * ancestor = utils_ancestor(handle); browser_data * owner; owner = ancestor->selected_owner; /* Is Shift held down? (Do this very early to minimise the */ /* changes of missing the keypress). */ _swix(OS_Byte, _INR(0,1) | _OUT(1), 121, 128, &shift); /* Similarly, for Control */ _swix(OS_Byte, _INR(0,1) | _OUT(1), 121, 129, &ctrl); /* Remember where the click was so other functions can find this */ /* without calling Wimp_GetPointerInfo, by which time the pointer */ /* may have moved a bit. */ last_click_x = b->mouse_click.mouse_x; last_click_y = b->mouse_click.mouse_y; /* There are circumstances under which pointer watching, to see if */ /* the mouse pointer should be turned off, is disabled but should */ /* be enabled at this stage. If this is so, turn it back on. */ if (choices.keyboard_ctrl && ancestor->selected) { owner->pointer_over = NULL; browser_pointer_check(0, NULL, NULL, owner); mouse_watch_pointer_control(1); } /* If this is entered with an event code of -1, this signals that */ /* the keyboard handler is calling the function. In that case, */ /* the routine will treat the item in handle->selected as if it */ /* were clicked on with Select. */ /* */ /* Otherwise, the token that was clicked on is discovered */ /* from the pointer position, and the mouse button used from the */ /* actual mouse button state. */ if (eventcode >= 0 && b->mouse_click.buttons & Wimp_MouseButtonMenu) return 0; /* Use the 'adjust' function as this may return special information if running Full Screen. */ adj = (controls.ignore_adjust || eventcode < 0) ? 0 : adjust(); /* This may be a drag, not a click; also, if the browser has */ /* children, this must be a frame border resize event. */ if ( b->mouse_click.buttons == 16 || /* (Adjust-drag) */ b->mouse_click.buttons == 64 /* (Select-drag) */ ) { /* For now, we have no defined action for drags */ return 1; } /* For dragging borders, don't do this on the drag event (i.e. keep */ /* this code in a position to execute after the check for a drag). */ /* Otherwise, when the routine calls Wimp_GetPointerInfo, it'll get */ /* the original starting coordinates just after the drag begins. */ /* This makes the frames 'twitch' briefly back to their starting */ /* position. */ if (handle->nchildren) { drag_in_progress = 1; handle->dragging = 1; register_null_claimant(Wimp_ENull, (WimpEventHandler *) handle_drags, handle); /* Call it now to minimise the time during which the pointer */ /* could wander when it's meant to be constrained */ handle_drags(0, NULL, NULL, handle); return 1; } /* Get the token that was clicked upon, if any. */ ChkError(wimp_get_pointer_info(&i)); if (eventcode >= 0) p = browser_get_pointer_token(handle, &i, &ox, &oy); else p = ancestor->selected, handle = owner; if (p) { /* First - forms. */ if ( (p->style & FORM) && ( (p->tagno == TAG_INPUT) || (p->tagno == TAG_TEXTAREA) || (p->tagno == TAG_SELECT) ) ) { int x = 0, y = 0; /* Get the offset into an IMAGE button type */ if (p->tagno == TAG_INPUT && HtmlINPUTtype(p) == inputtype_IMAGE && eventcode >= 0) { ChkError(image_return_click_offset(handle, p, &i, &x, &y)); } ChkError(form_click_field(handle, p, 0, x, y)); used = 1; } else { /* If the token is an anchor, flash it briefly. This isn't done */ /* for images unless they're turned off and the image has ALT */ /* text, which would show the highlight. */ if ( p->anchor && ( !(p->style & IMG) || ( (p->style & IMG) && p->text && handle->displayed != Display_External_Image && !image_token_fetched(handle, p) ) ) ) browser_flash_token(handle, p); /* If shift and control are not held down, and we have a link, follow the link */ if (!shift && !ctrl) { if (p->anchor || (p->type & TYPE_ISCLIENTMAP)) /* An anchor or client-side map */ { int ignore = 0; /* First, do we have JavaScript code to deal with? */ if (p->onclick && *p->onclick) ChkError(javascript_href_onclick(handle, p, &ignore)); /* If ignore is zero, we're supposed to deal with the HREF attribute */ /* on the link - otherwise, ignore it. */ if (!ignore) { /* Client-side image maps */ if (p->type & TYPE_ISCLIENTMAP) { char * url; char * target; char * alt; /* Find out which pixel we clicked on */ ChkError(image_return_click_offset(handle, p, &i, &ox, &oy)); csim_return_info(handle, p, ox, oy, &url, &target, &alt); if (url && *url) { ChkError(fetchpage_fetch_targetted(handle, url, target, NULL, adj)); used = 1; } } /* Server-side image maps */ if ( !used && (p->style & IMG) && (p->type & TYPE_ISMAP) ) { char coords[64]; if (eventcode < 0) { ChkError(wimp_get_pointer_info(&i)); p = browser_get_pointer_token(handle, &i, NULL, NULL); if (!p) return 0; } /* Find out which pixel we clicked on */ ChkError(image_return_click_offset(handle, p, &i, &ox, &oy)); if (ox >= 0 && oy >= 0) { /* Build an appropriate CGI string including this information. */ sprintf(coords, "?%d,%d", ox, oy); ChkError(fetchpage_fetch_targetted(handle, p->anchor, p->target, coords, adj)); } used = 1; } /* Otherwise, a simple link */ if (!used) { /* Note that running full screen will cause non-targetted links */ ChkError(fetchpage_fetch_targetted(handle, p->anchor, p->target, NULL, adj)); used = 1; } } else { /* The JavaScript routines said that the HREF contents of the link */ /* should be ignored; so just flag that we've dealt with this, but */ /* do nothing else. */ used = 1; } } } else { /* If Shift is held down, open the object into a save dialogue. */ /* Always do this through a new window, if the Choices say so; */ /* this will be a 'small fetch' window rather than a full, */ /* large browser. */ if (shift) { if (p->anchor) { if (!adj && !controls.use_small) { if (!handle->save_oldstore && handle->source) { /* If there is store and its size hasn't already been remembered, */ /* keep a record of the old source store size before the new file */ /* save clears the data away. */ handle->save_oldstore = flex_size((flex_ptr) &handle->source); } /* Set the save_link flag and start the fetch */ handle->save_link = 1; /* If control is held down, set the reloading flag to bypass the cache */ if (ctrl) handle->reloading = 1; ChkError(fetchpage_new(handle, p->anchor, 0, 1)); } else { /* Open the link in a new window */ ChkError(windows_create_browser(p->anchor, NULL, NULL, NULL, Windows_CreateBrowser_SaveToFile)); /* Again, if control is held down, set the reloading flag to bypass the cache */ if (ctrl) last_browser->reloading = 1; } used = 1; } } else { /* For Control, if it's an image, show it. This overrides */ /* any 'don't show images' flags and will load the image */ /* if it isn't already fetched. */ if ((p->style & IMG) || (ISOBJECT(p))) { image_reload(handle, p); /* (Slightly misleading function name) */ used = 1; } } } } } /* For mouse clicks, if nothing has flagged that the click was used */ /* in some way, place the input focus generally into the ancestor */ /* window and mark the frame as selected. */ if (eventcode >= 0 && !used) { wimp_set_caret_position(ancestor->window_handle, -1, 0, 0, -1, -1); ancestor->selected_frame = handle; frames_highlight_frame(handle); /* If there's an object selected in another frame, must move */ /* the selection to this one - otherwise, keyboard movement */ /* would jump back to the other one. */ if (ancestor->selected && ancestor->selected_owner != handle) { if (ancestor->selected_owner) browser_clear_selection(ancestor->selected_owner, 0); ancestor->selected_owner = NULL; /* Make sure these */ ancestor->selected = NULL; /* are cleared... */ browser_move_selection(handle, akbd_DownK); } } return 1; } /*************************************************/ /* handle_drags() */ /* */ /* A null event handler to handle dragging on */ /* the page. Parameters are as standard for a */ /* Wimp event handler, though only the last */ /* parameter, a pointer to the browser_data */ /* struct for which the drag applies, is used. */ /*************************************************/ int handle_drags(int eventcode, WimpPollBlock * b, IdBlock * idb, browser_data * handle) { static browser_data * browser_resizing = NULL; static int resizing_row = -1; static int resizing_col = -1; static int resizing_ofsleft = 0; static int resizing_ofstop = 0; WimpGetPointerInfoBlock info; WimpGetWindowStateBlock state; int wx, wy; /* If handle is NULL, this was called for a Wimp_EUserDrag */ /* event from the Wimp. In that case, cancel the global */ /* drag handling flag and allow various event handlers */ /* elsewhere to now progress normally but wait until the */ /* null event handler calls this function to deregister */ /* any browser-specific stuff. */ if (!handle) { drag_in_progress = 0; return 0; } /* Get the pointer position and browser window state */ if (wimp_get_pointer_info(&info)) return 0; state.window_handle = handle->window_handle; if (wimp_get_window_state(&state)) return 0; if (!info.button_state) { /* No button being pressed - the drag has ended */ if (handle->dragging) { drag_in_progress = 0; handle->dragging = 0; deregister_null_claimant(Wimp_ENull, (WimpEventHandler *) handle_drags, handle); if (browser_resizing) { /* If we were resizing a frameset, lock the new positions in place */ frames_lock_frameset(browser_resizing); /* Reset the various state variables ready for the next drag */ browser_resizing = NULL; resizing_row = -1; resizing_col = -1; } return 0; } } /* Work out where the pointer is relative to the window work area */ wx = coords_x_toworkarea(info.x, (WimpRedrawWindowBlock *) &state); wy = coords_y_toworkarea(info.y, (WimpRedrawWindowBlock *) &state); /* The mouse button is still held down. So what are we doing? */ if (handle->nchildren) { /* If the frame has children, then the pointer must be over a blank bit */ /* - i.e. a border between frames. In that case, this is a frame resize. */ browser_data * child; WimpGetWindowOutlineBlock co; int delta_x = 0, delta_y = 0; int num_rows, num_cols; int i, c; BBox span; num_rows = handle->frameset->rows; num_cols = handle->frameset->cols; if (!num_rows) num_rows = 1; if (!num_cols) num_cols = 1; /* A parent frame has got the click, so it must be in the border between */ /* other frames in its defined frameset. */ if (!browser_resizing) { _kernel_oserror * e; e = frames_find_pointer_in_frameset(handle, last_click_x, last_click_y, &resizing_row, &resizing_col, &resizing_ofsleft, &resizing_ofstop, 1); if (e || (resizing_row < 0 && resizing_col < 0)) { browser_resizing = NULL; resizing_row = -1; resizing_col = -1; return 0; } browser_resizing = handle; /* Closure of 'if (!browser_resizing)' */ } /* Whether or not we had to work out the initial drag */ /* conditions above, deal with moving the frames now. */ c = (resizing_row > 0 ? resizing_row : 0) * num_cols + (resizing_col > 0 ? resizing_col : 0); if (c >= handle->nchildren) return 0; child = handle->children[c]; co.window_handle = child->window_handle; if (wimp_get_window_outline(&co)) return 0; if (resizing_row > 0) delta_y = (info.y - co.outline.ymax) - resizing_ofstop; if (resizing_col > 0) delta_x = resizing_ofsleft - (co.outline.xmin - info.x); /* Deal with resizing rows first. */ span.xmin = span.ymin = 0x1000000; /* Very big to start with */ span.xmax = span.ymax = -1; /* Small to start with */ if (delta_y) { /* Resize the row above the pointer */ for (i = 0; i < num_cols; i++) { /* What child number are we on? */ c = i + (resizing_row - 1) * num_cols; if (c < handle->nchildren) { /* Get this child frame's outline coordinates */ child = handle->children[c]; co.window_handle = child->window_handle; if (!wimp_get_window_outline(&co)) { /* Alter its visible area appropriately */ co.outline.ymin += delta_y; /* Keep a record of the region over which */ /* things have moved for redraw purposes */ if (co.outline.xmin < span.xmin) span.xmin = co.outline.xmin; if (co.outline.ymin < span.ymin) span.ymin = co.outline.ymin; if (co.outline.xmax > span.xmax) span.xmax = co.outline.xmax; if (co.outline.ymax > span.ymax) span.ymax = co.outline.ymax; /* Resize any child frames */ ChkError(frames_resize_frame(child, &co.outline)); } } } /* Same again for the row below */ for (i = 0; i < num_cols; i++) { c = i + resizing_row * num_cols; if (c < handle->nchildren) { child = handle->children[c]; co.window_handle = child->window_handle; if (!wimp_get_window_outline(&co)) { co.outline.ymax += delta_y; if (co.outline.xmin < span.xmin) span.xmin = co.outline.xmin; if (co.outline.ymin < span.ymin) span.ymin = co.outline.ymin; if (co.outline.xmax > span.xmax) span.xmax = co.outline.xmax; if (co.outline.ymax > span.ymax) span.ymax = co.outline.ymax; ChkError(frames_resize_frame(child, &co.outline)); } } } /* Closure of 'if (delta_y)' */ } /* Now do columns; first, to the left of the pointer */ if (delta_x) { for (i = 0; i < num_rows; i++) { c = i * num_cols + resizing_col - 1; if (c < handle->nchildren) { child = handle->children[c]; co.window_handle = child->window_handle; if (!wimp_get_window_outline(&co)) { co.outline.xmax += delta_x; if (co.outline.xmin < span.xmin) span.xmin = co.outline.xmin; if (co.outline.ymin < span.ymin) span.ymin = co.outline.ymin; if (co.outline.xmax > span.xmax) span.xmax = co.outline.xmax; if (co.outline.ymax > span.ymax) span.ymax = co.outline.ymax; ChkError(frames_resize_frame(child, &co.outline)); } } } /* Next, columns to the right of the pointer */ for (i = 0; i < num_rows; i++) { c = i * num_cols + resizing_col; if (c < handle->nchildren) { child = handle->children[c]; co.window_handle = child->window_handle; if (!wimp_get_window_outline(&co)) { co.outline.xmin += delta_x; if (co.outline.xmin < span.xmin) span.xmin = co.outline.xmin; if (co.outline.ymin < span.ymin) span.ymin = co.outline.ymin; if (co.outline.xmax > span.xmax) span.xmax = co.outline.xmax; if (co.outline.ymax > span.ymax) span.ymax = co.outline.ymax; ChkError(frames_resize_frame(child, &co.outline)); } } } /* Closure of 'if (delta_x)' */ } /* Now redraw the parent to ensure borders are correctly plotted */ if ( span.xmin >= 0 && span.ymin >= 0 && span.xmax >= 0 && span.ymax >= 0 ) { coords_box_toworkarea(&span, (WimpRedrawWindowBlock *) &state); ChkError(wimp_force_redraw(state.window_handle, span.xmin, span.ymin, span.xmax, span.ymax)); } /* Closure of 'if (handle->nchildren)' */ } return 0; } /*************************************************/ /* handle_close_browser() */ /* */ /* Close a browser window, and frames within it. */ /* Parameters are as standard for a Wimp event */ /* handler. */ /*************************************************/ int handle_close_browser(int eventcode, WimpPollBlock * b, IdBlock * idb, browser_data * handle) { frames_collapse_set(handle); windows_close_browser(handle); return 1; } /*************************************************/ /* handle_home() */ /* */ /* Handles clicks on the Home button. Parameters */ /* are as standard for a Toolbox event handler. */ /*************************************************/ int handle_home(int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle) { int from_menu = 0; browser_data * b; char home[Limits_URL]; handlers_get_call_info(&b, NULL, idb, ButtonBarHome); from_menu = handlers_menu_or_toolbar(idb); urlutils_create_home_url(home, sizeof(home)); if (from_menu || controls.ignore_adjust || !adjust()) ChkError(fetchpage_new(b, home, 1, 1)); else { ChkError(windows_create_browser(home, NULL, NULL, NULL, Windows_CreateBrowser_Normal)); ChkError(browser_inherit(b, last_browser)); } /* Grey / ungrey buttons, as the state may change as */ /* a result of being selected. */ ChkError(toolbars_set_button_states(b)); return 1; } /*************************************************/ /* handle_back() */ /* */ /* Handles clicks on the Back button. Parameters */ /* are as standard for a Toolbox event handler. */ /*************************************************/ int handle_back(int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle) { int from_menu = 0; browser_data * b; handlers_get_call_info(&b, NULL, idb, ButtonBarBack); from_menu = handlers_menu_or_toolbar(idb); ChkError(history_fetch_backwards(b, (from_menu || controls.ignore_adjust) ? 0 : adjust())); /* Grey / ungrey buttons, as the state may change as */ /* a result of being selected. */ ChkError(toolbars_set_button_states(b)); return 1; } /*************************************************/ /* handle_forward() */ /* */ /* Handles clicks on the Forward button. */ /* Parameters are as standard for a Toolbox */ /* event handler. */ /*************************************************/ int handle_forwards(int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle) { int from_menu = 0; browser_data * b; handlers_get_call_info(&b, NULL, idb, ButtonBarForward); from_menu = handlers_menu_or_toolbar(idb); ChkError(history_fetch_forwards(b, (from_menu || controls.ignore_adjust) ? 0 : adjust())); /* Grey / ungrey buttons, as the state may change as */ /* a result of being selected. */ ChkError(toolbars_set_button_states(b)); return 1; } /*************************************************/ /* handle_stop() */ /* */ /* Handles clicks on the Stop button. Parameters */ /* are as standard for a Toolbox event handler. */ /*************************************************/ int handle_stop(int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle) { browser_data * b; #ifdef SINGLE_USER int shift; /* Is Shift being pressed? */ _swix(OS_Byte, _INR(0,1) | _OUT(1), 121, 128, &shift); #endif /* Find out where we were called from */ handlers_get_call_info(&b, NULL, idb, ButtonBarStop); /* If using the tristate TriState_Go_GoTo_Stop, */ /* then stop kills everything first time. Else, */ /* it stops everything except image fetches, */ /* then stops image fetches too. */ if (b->tristate != TriState_Go_GoTo_Stop) { if (frames_fetching(b)) frames_abort_fetching(utils_ancestor(b), 0, 1); else frames_abort_fetching(utils_ancestor(b), 1, 1); } else frames_abort_fetching(utils_ancestor(b), 1, 1); /* Clear the flag saying this is a History based fetch */ b->from_history = 0; /* Cancel local client pull */ meta_cancel_refresh(b); /* Grey / ungrey buttons, as the state may change as */ /* a result of being selected. */ ChkError(toolbars_set_button_states(b)); /* Ensure everything is up to date in the toolbars */ toolbars_update_progress(b); #ifdef SINGLE_USER /* Broadcast an AppControl message to stop any further */ /* activity in WebServe - this will eventually be directed */ /* straight at WebServe rather than broadcast... */ // if (controls.stop_proxy && !b->ancestor) ChkError(utils_stop_proxy()); if ( ( controls.stop_proxy && !shift ) || ( !controls.stop_proxy && shift ) ) ChkError(utils_stop_proxy()); #endif return 1; } /*************************************************/ /* handle_reload() */ /* */ /* Handles clicks on the Reload button. */ /* Parameters are as standard for a Toolbox */ /* event handler. */ /*************************************************/ int handle_reload(int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle) { _kernel_oserror * e; browser_data * b; ObjectId t; int new_view; int from_menu = 0; handlers_get_call_info(&b, &t, idb, ButtonBarReload); /* If the browser is displaying a temporary file, can't reload it */ if (b->displayed == Display_Scrap_File) { erb.errnum = Utils_Error_Custom_Message; StrNCpy0(erb.errmess, lookup_token("CantReload:This page cannot be reloaded, as it was sent directly from another application.", 0, 0)); show_error_ret(&erb); return 0; } from_menu = handlers_menu_or_toolbar(idb); new_view = (from_menu || controls.ignore_adjust) ? 0 : adjust(); /* If there's a displayed URL already, reload it */ if (b->urlddata) { /* If not going to a new view, set the reloading flag */ /* and get the URL. */ if (!new_view) { b->reloading = 1; b->reload_lock = 1; e = fetchpage_new(b, b->urlddata, 0, 1); if (e) show_error_ret(e); } /* Otherwise, just fetch the URL in a new window. */ else { ChkError(windows_create_browser(b->urlddata, NULL, NULL, NULL, Windows_CreateBrowser_Normal)); ChkError(browser_inherit(b, last_browser)); } } else { /* Otherwise, get a URL string from the URL bar and do */ /* a fresh load of that. */ char url[Limits_URLBarWrit]; *url = 0; writablefield_get_value(0, t, URLBarWrit, url, sizeof(url), NULL); url[sizeof(url) - 1] = 0; /* (Ensure termination) */ if (*url) { if (!new_view) ChkError(fetchpage_new(b, url, 1, 1)); else { ChkError(windows_create_browser(url, NULL, NULL, NULL, Windows_CreateBrowser_Normal)); ChkError(browser_inherit(b, last_browser)); } } } /* Grey / ungrey buttons, as the state may change as */ /* a result of being selected. */ ChkError(toolbars_set_button_states(b)); return 1; } /*************************************************/ /* handle_view_hotlist() */ /* */ /* Handles clicks on the View Hotlist button. */ /* Parameters are as standard for a Toolbox */ /* event handler. */ /*************************************************/ int handle_view_hotlist(int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle) { #ifndef REMOTE_HOTLIST int from_menu = handlers_menu_or_toolbar(idb); int adjust = 0; #else browser_data * b; char path[Limits_URL]; int from_menu = 0; #endif #ifndef SINGLE_USER /* In multiuser builds, if not logged in, claim the event */ /* but don't do anything with it. */ if (!logged_in) return 1; #endif #ifndef REMOTE_HOTLIST /* If not from a menu, allow Adjust to toggle the display type */ /* at open time to the opposite of that specified by the */ /* HotlistShow option in the Choices file. */ if (!from_menu) { WimpGetPointerInfoBlock info; ChkError(wimp_get_pointer_info(&info)); if (info.button_state & Wimp_MouseButtonAdjust) adjust = 1; } /* Open the hotlist */ ChkError(hotlist_open(Toolbox_ShowObject_Centre, NULL, adjust ? choices.hotlist_show == Choices_HotlistType_Descriptions : choices.hotlist_show == Choices_HotlistType_URLs)); #else handlers_get_call_info(&b, NULL, idb, ButtonBarViewHotlist); from_menu = handlers_menu_or_toolbar(idb); /* Create the hotlist URL */ urlutils_create_hotlist_url(path, sizeof(path)); /* Deal with appending the current URL, if necessary */ // For future implementation: // // if (controls.append_urls) // { // char url[Limits_URL]; // // if (browser_current_url(b)) // { // StrNCpy0(url, browser_current_url(b)); // history_record_local(b, url); // } // // history_pull_local_last(b, url, sizeof(url)); // // Then do the rest on url, instead of browser_current_url. if (controls.append_urls && browser_current_url(b)) { int len; lookup_control("AppendWith:?url=",0,0); /* Need to translate some chars, so working out the length of the final */ /* string is a little complex. */ len = strlen(path) + strlen(tokens); if (len + 1 < sizeof(path)) { char * p = browser_current_url(b); strcat(path, tokens); while (*p && len + 4 < sizeof(path)) /* +4 = +1 for terminator, +3 for maximum step size within loop */ { if (isalnum(*p)) path[len] = *p, len++; else { sprintf(path + len, "%%%02X", *p); len += 3; } p++; } if (len >= sizeof(path)) len = sizeof(path) - 1; path[len] = 0; } } /* Finally, fetch the required hotlist */ if (adjust() && !from_menu && !controls.ignore_adjust) { ChkError(windows_create_browser(path, NULL, NULL, NULL, Windows_CreateBrowser_Normal)); ChkError(browser_inherit(b, last_browser)); } else ChkError(fetchpage_new(b, path, 1, 1)); #endif return 1; } /*************************************************/ /* handle_add_hotlist() */ /* */ /* Handles clicks on the Add To Hotlist button. */ /* Parameters are as standard for a Toolbox */ /* event handler. */ /*************************************************/ int handle_add_hotlist(int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle) { #ifndef REMOTE_HOTLIST browser_data * b; int from_menu = 0; int adjust = 0; handlers_get_call_info(&b, NULL, idb, ButtonBarAddToHotlist); from_menu = handlers_menu_or_toolbar(idb); /* If not from a menu, allow Adjust to add to the other end of the */ /* hotlist compared to that specified in the AddHotlist Choices */ /* file item. */ if (!from_menu) { WimpGetPointerInfoBlock info; ChkError(wimp_get_pointer_info(&info)); if (info.button_state & Wimp_MouseButtonAdjust) adjust = 1; } /* Can't proceed if we don't have a URL */ if (b && browser_current_url(b) && *browser_current_url) { char title[Limits_Title]; ChkError(window_get_title(0, b->self_id, title, sizeof(title), NULL)); title[sizeof(title) - 1] = 0; ChkError(hotlist_add(title, browser_current_url(b), adjust ? choices.add_hotlist == Choices_AddHotlist_Top : choices.add_hotlist == Choices_AddHotlist_Bottom)); } #else #ifdef TRACE erb.errnum = Utils_Error_Custom_Normal; strcpy(erb.errmess, "There is no defined action for Add To Hotlist in REMOTE_HOTLIST builds"); show_error_ret(&erb); #endif #endif return 0; } /*************************************************/ /* handle_view_resources() */ /* */ /* Handles clicks on the View Resources button. */ /* Parameters are as standard for a Toolbox */ /* event handler. */ /*************************************************/ int handle_view_resources(int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle) { return 0; } /*************************************************/ /* handle_load_images() */ /* */ /* Handles clicks on the Load Images button. */ /* Parameters are as standard for a Toolbox */ /* event handler. */ /*************************************************/ int handle_load_images(int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle) { browser_data * b; ObjectId t; handlers_get_call_info(&b, &t, idb, ButtonBarLoadImages); ChkError(browser_set_look(b, 0, -1, -1, 1, 1)); return 1; } /*************************************************/ /* handle_view_source() */ /* */ /* Handles clicks on the View Source button. */ /* Parameters are as standard for a Toolbox */ /* event handler. */ /*************************************************/ int handle_view_source(int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle) { char path[Limits_URL]; /* Generated from a URL, but can't be any longer than one */ browser_data * b; ObjectId t; FILE * file; int len, bytes; handlers_get_call_info(&b, &t, idb, ButtonBarViewSource); if (!is_known_browser(b) || !b->source) return 1; /* If the item is loaded from a file (not a scrap file, */ /* or image, obviously), then load from there. Else, go */ /* via. Scrap. */ if ( browser_current_url(b) && b->displayed == Display_Fetched_Page && !strncmp(browser_current_url(b), FileMethod, strlen(FileMethod)) ) { StrNCpy0(path, browser_current_url(b)); urlutils_url_to_pathname(path, sizeof(path)); } else { /* Save the source to a scrap file */ file = fopen(Save_ScrapFile, "wb"); if (!file) goto handle_view_source_exit; len = flex_size((flex_ptr) &b->source); bytes = fwrite(b->source, 1, len, file); fclose(file); if (len < bytes) goto handle_view_source_exit; /* Set the filetype */ ChkError(_swix(OS_File, _INR(0,2), 18, Save_ScrapFile, FileType_HTML)); /* Record that we've used a scrap file */ StrNCpy0(path, Save_ScrapFile); } /* Broadcast a Message_DataOpen for the file, claiming the */ /* filetype is text (which is what the Filer does...). */ ChkError(protocols_atats_send_data_open(FileType_TEXT, path)); return 1; /* Exit for errors whilst writing the scrap file */ handle_view_source_exit: erb.errnum = Utils_Error_Custom_Message; StrNCpy0(erb.errmess, lookup_token("CannotShow:Can't show the source this way (problem with the scrap file).", 0,0)); show_error_ret(&erb); return 1; } /*************************************************/ /* handle_go_to() */ /* */ /* Handles clicks on the Go To button. */ /* Parameters are as standard for a Toolbox */ /* event handler. */ /*************************************************/ int handle_go_to(int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle) { browser_data * b; ObjectId t; handlers_get_call_info(&b, &t, idb, ButtonBarGoTo); /* Ensure the URL bar is up to date, if the URL writable and */ /* status displays are merged then change to the URL display */ /* and finally give the window the input focus. */ toolbars_update_url(b); if (b->merged_url) toolbars_merged_to_url(b, t); if (t) gadget_set_focus(0, t, URLBarWrit); /* Update the buttons and exit */ ChkError(toolbars_set_button_states(b)); return 1; } /*************************************************/ /* handle_go_to_with_key() */ /* */ /* Handles going to a 'go to' state (merged */ /* toolbar showing the URL writable instead of */ /* the status display) with a given character */ /* entered into that writable. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the toolbar; */ /* */ /* The character to append to the */ /* writable field's contents; */ /* */ /* 1 to in fact clear the writable */ /* before putting the character in, */ /* else 0 to append to existing */ /* contents. */ /*************************************************/ static void handle_go_to_with_key(browser_data * b, char c, int clear) { ObjectId t; char url[Limits_URLBarWrit]; char cat[2]; t = toolbars_get_upper(b); /* Ensure the URL bar is up to date, if the URL writable and */ /* status displays are merged then change to the URL display */ /* and finally give the window the input focus. */ toolbars_update_url(b); /* (Append the given character) */ if (clear) { url[0] = c; url[1] = 0; } else { *url = 0; writablefield_get_value(0, t, URLBarWrit, url, sizeof(url), NULL); url[sizeof(url) - 1] = 0; if (strlen(url) < sizeof(url)) { cat[0] = c; cat[1] = 0; strcat(url, cat); } } writablefield_set_value(0, t, URLBarWrit, url); /* (Show the writable / give the input focus) */ if (b->merged_url) toolbars_merged_to_url(b, t); if (t) gadget_set_focus(0, t, URLBarWrit); /* Update the buttons and exit. */ ChkError(toolbars_set_button_states(b)); return; } /*************************************************/ /* handle_go() */ /* */ /* Handles clicks on the Go button. Parameters */ /* are as standard for a Toolbox event handler. */ /*************************************************/ int handle_go(int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle) { browser_data * b; WimpKeyPressedEvent keys; IdBlock id; handlers_get_call_info(&b, NULL, idb, ButtonBarGo); // Nasty... But then robust, as quite a lot goes on in the key handler // and duplicating the code would lead to problems if it gets out of // sync. /* Fake a keyboard pressed event from the URL bar for the key handler */ keys.key_code = 0x00d; id.ancestor_id = b->self_id; id.self_id = toolbars_get_upper(b); id.self_component = URLBarWrit; return handle_keys(eventcode, (WimpPollBlock *) &keys, &id, NULL); } /*************************************************/ /* handle_cancel() */ /* */ /* Handles clicks on the Cancel button. */ /* Parameters are as standard for a Toolbox */ /* event handler. */ /*************************************************/ int handle_cancel(int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle) { browser_data * b; handlers_get_call_info(&b, NULL, idb, ButtonBarCancel); /* If the URL writable and status display are merged, switch to */ /* status display. Update the URL bar with the original text, */ /* cancelling any changes made by the user, and set the caret */ /* position by the general method (so focus may go to a form, */ /* the URL writable, or the page generally). */ if (b->merged_url) toolbars_merged_to_status(b, toolbars_get_upper(b)); toolbars_update_url(b); browser_give_general_focus(b); /* Update the buttons and exit */ ChkError(toolbars_set_button_states(b)); return 1; } /*************************************************/ /* handle_bistate() */ /* */ /* Handles the EButtonBarBistate event, which */ /* says that the bistate button should be */ /* activated. */ /* */ /* Parameters are as standard for a Toolbox */ /* event handler. */ /*************************************************/ int handle_bistate(int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle) { browser_data * b; handlers_get_call_info(&b, NULL, idb, ButtonBarBistate); /* Deal with each button type individually */ switch (b->bistate) { case BiState_Cancel_Back: { if (!b->bistate_state) return handle_cancel(eventcode, event, idb, handle); else return handle_back(eventcode, event, idb, handle); } break; } return 0; } /*************************************************/ /* handle_tristate() */ /* */ /* Handles the EButtonBarTristate event, which */ /* says that the tristate button should be */ /* activated. */ /* */ /* Parameters are as standard for a Toolbox */ /* event handler. */ /*************************************************/ int handle_tristate(int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle) { browser_data * b; handlers_get_call_info(&b, NULL, idb, ButtonBarTristate); /* Deal with each button type individually */ switch (b->bistate) { case TriState_Go_GoTo_Stop: { switch (b->tristate_state) { case 0: return handle_go(eventcode, event, idb, handle); break; case 1: return handle_go_to(eventcode, event, idb, handle); break; case 2: return handle_stop(eventcode, event, idb, handle); break; } } break; } return 0; } /*************************************************/ /* handle_save_source() */ /* */ /* Handles clicks on the Save button. Parameters */ /* are as standard for a Toolbox event handler. */ /*************************************************/ int handle_save_source(int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle) { browser_data * b; handlers_get_call_info(&b, NULL, idb, ButtonBarSaveSource); if (is_known_browser(b) && b->source) ChkError(savefile_open_for(b, save_as_html)); return 1; } /*************************************************/ /* handle_print() */ /* */ /* Handles clicks on the Print button in the */ /* toolbar of browser windows. Parameters are as */ /* standard for a Toolbox event handler. */ /*************************************************/ int handle_print(int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle) { browser_data * b; handlers_get_call_info(&b, NULL, idb, ButtonBarPrint); if (is_known_browser(b) && b->cell && b->cell->nlines) ChkError(print_open_for(b, idb->self_id)); return 1; } /*************************************************/ /* handle_save_as_text() */ /* */ /* Handles clicks on the Save As Text button. */ /* Parameters are as standard for a Toolbox */ /* event handler. */ /*************************************************/ int handle_save_as_text(int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle) { browser_data * b; handlers_get_call_info(&b, NULL, idb, ButtonBarSaveAsText); if (is_known_browser(b) && b->cell && b->cell->nlines) ChkError(savefile_open_for(b, save_as_text)); return 1; } /*************************************************/ /* handle_save_as_draw() */ /* */ /* Handles clicks on the Save As Draw button. */ /* Parameters are as standard for a Toolbox */ /* event handler. */ /*************************************************/ int handle_save_as_draw(int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle) { browser_data * b; handlers_get_call_info(&b, NULL, idb, ButtonBarSaveAsDraw); if (is_known_browser(b) && b->cell && b->cell->nlines) ChkError(savefile_open_for(b, save_as_draw)); return 1; } /*************************************************/ /* handle_clear_url() */ /* */ /* Clears the URL writable (like Ctrl+U), */ /* assuming the input focus is in the relevant */ /* place... */ /* */ /* Parameters are as standard for a Toolbox */ /* event handler. */ /*************************************************/ int handle_clear_url(int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle) { WimpGetCaretPositionBlock caret; browser_data * b; ObjectId t; char url[2]; ChkError(wimp_get_caret_position(&caret)); if (caret.icon_handle < 0 || caret.height < 0 || caret.index < 0) return 0; ChkError(toolbox_get_client_handle(0, idb->ancestor_id, (void *) &b)); if (!b) return 0; t = toolbars_get_upper(b); /* Ensure the URL bar is up to date, if the URL writable and */ /* status displays are merged then change to the URL display */ /* and finally give the window the input focus. */ toolbars_update_url(b); /* (Append the given character) */ url[0] = 0; url[1] = 0; writablefield_set_value(0, t, URLBarWrit, url); /* (Show the writable / give the input focus) */ if (b->merged_url) toolbars_merged_to_url(b, t); if (t) gadget_set_focus(0, t, URLBarWrit); /* Update the buttons and exit. */ ChkError(toolbars_set_button_states(b)); return 1; } /*************************************************/ /* handle_show_history_menu() */ /* */ /* Opens the history menu near the menu popup */ /* gadget (acts as if that were clicked upon). */ /* If the gadget doesn't exist, the opening */ /* position is undefined. */ /* */ /* Parameters are as standard for a Toolbox */ /* event handler. */ /*************************************************/ int handle_show_history_menu(int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle) { browser_data * b; ObjectId t; BBox test; int which; int shift; /* Is Shift being pressed? */ _swix(OS_Byte, _INR(0,1) | _OUT(1), 121, 128, &shift); /* Which browser is this for? */ ChkError(toolbox_get_client_handle(0, idb->ancestor_id ? idb->ancestor_id : idb->self_id, (void *) &b)); if (!b) return 0; t = toolbars_get_upper(b); /* Is the popup present (either the 'to the left' or */ /* the 'to the right' version)? */ if (!gadget_get_bbox(0, t, URLBarHistoryMenuR, &test)) which = 1; else if (!gadget_get_bbox(0, t, URLBarHistoryMenuL, &test)) which = 2; else which = 0; if (which) { /* Show the menu */ ChkError(history_menu_popup(b, t, which == 1 ? URLBarHistoryMenuR : URLBarHistoryMenuL, shift, choices.show_urls)); return 1; } /* Gadget isn't present, so can't open the menu */ return 0; } /*************************************************/ /* handle_show_info() */ /* */ /* Put version number in program info window; */ /* called on a ProgInfo_AboutToBeShown event. */ /* Parameters are as standard for a Toolbox */ /* event handler. */ /*************************************************/ int handle_show_info(int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle) { #ifndef SINGLE_USER /* Make sure that the tokens buffer is invalidated, */ /* or we could end up appending multiple '(M)'s to */ /* the string if lookup_token thinks it has no */ /* reason to look up the token again. */ *lasttokn = 0; #endif lookup_token("Version:(Unknown!)",1,0); #ifndef SINGLE_USER strcat(tokens, " (M)"); #endif ChkError(proginfo_set_version(0, idb->self_id, tokens)); return 1; } /*************************************************/ /* handle_quit() */ /* */ /* Deal with a Quit_Quit event from any source; */ /* parameters are as standard for a Toolbox */ /* event handler. */ /*************************************************/ int handle_quit(int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle) { browser_data * b; /* Ensure the pointer is visible */ mouse_pointer_on(); /* Close all open windows, thereby freeing memory and terminating */ /* any open connections. This may take some time, so put up an */ /* hourglass. */ _swix(Hourglass_Start, _IN(0), 10); while (last_browser) { b = last_browser; while (b && b->ancestor) b = b->previous; if (b && !b->ancestor) handle_close_browser(0, NULL, NULL, b); } _swix(Hourglass_Off, 0); /* Signal that the application should exit through setting 'quit'. */ quit = 1; return 1; } /*************************************************/ /* handle_lose_caret() */ /* */ /* Call to drag the input focus back to the */ /* window in case it is lost. */ /* */ /* Parameters are as standard for a Wimp event */ /* handler. */ /*************************************************/ int handle_lose_caret(int eventcode, WimpPollBlock * block, IdBlock * idb, void * handle) { if (controls.keep_caret) { WimpGetPointerInfoBlock i; int handle; /* Only grab the caret if this task owns the window */ /* the pointer is currently over. */ ChkError(wimp_get_pointer_info(&i)); handle = task_from_window(i.window_handle); if (handle == task_handle) { WimpGetCaretPositionBlock caret; /* The pointer is over a browser-owned window, but if the */ /* caret is also in a browser-owned window, don't want to */ /* move it right now. */ if (wimp_get_caret_position(&caret)) return 0; handle = task_from_window(caret.window_handle); /* If we don't own the window the caret has moved to, */ /* grab it back into the browser window. */ if (handle != task_handle) browser_give_general_focus(last_browser); return 1; } } return 0; } /*************************************************/ /* handle_dialler_display() */ /* */ /* A null event handler to update the dialler */ /* status display field, if present. */ /* */ /* Parameters are as standard for a Wimp event */ /* handler. */ /*************************************************/ int handle_dialler_display(int eventcode, WimpPollBlock * b, IdBlock * idb, browser_data * handle) { if (handle->url_bar) ChkError(toolbars_update_dialler_time(handle)); return 0; } /*************************************************/ /* handle_dialler_service() */ /* */ /* Handles Message_Service messages from the */ /* TaskModule module, which may be (for example) */ /* watching out for 'status changed' service */ /* calls from the Dialler. */ /* */ /* Parameters are as standard for a Wimp message */ /* handler. */ /*************************************************/ int handle_dialler_service(WimpMessage * m, void * handle) { browser_data * b = (browser_data *) handle; _kernel_swi_regs * r; if (!is_known_browser(b)) return 0; if (m->hdr.action_code != Message_Service) return 0; r = (_kernel_swi_regs *) &m->data; if (r->r[1] == Service_DiallerStatus) { if (b->url_bar) ChkError(toolbars_update_dialler_status(b)); return 1; } return 0; } /*************************************************/ /* handle_miscellaneous_event() */ /* */ /* Handle any other user event not already */ /* covered by other functions, passing it on if */ /* we don't know what to do with it. */ /* */ /* Parameters are as standard for a Toolbox */ /* event handler. */ /*************************************************/ int handle_miscellaneous_event(int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle) { if (eventcode >= EHelpGenericBase && eventcode <= EHelpGenericTop) { browser_data * b; if (toolbox_get_client_handle(0, idb->ancestor_id, (void *) &b)) { ChkError(toolbox_get_client_handle(0, idb->self_id, (void *) &b)); } if (!is_known_browser(b)) b = NULL; menus_help_miscellaneous(b, eventcode - EHelpGenericBase); return 1; } return 0; }