/* 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 "URI.h" /* URI handler API, in URILib: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 "TBEvents.h" #include "Utils.h" #include "Authorise.h" #include "Browser.h" #include "FetchPage.h" #include "FontManage.h" #include "Forms.h" #include "Frames.h" #include "History.h" #include "Images.h" #include "JavaScript.h" #include "MiscDefs.h" #include "Mouse.h" #include "Printing.h" #include "Redraw.h" #include "Reformat.h" #include "Save.h" #include "TokenUtils.h" #include "Toolbars.h" #include "URLutils.h" #include "Windows.h" #include "Handlers.h" /* Local statics */ static char last_help[Limits_DisplayStats]; 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 _kernel_oserror * handle_history_menu_popup (browser_data * b, ObjectId toolbar, ComponentId left_or_right, int show_urls); 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 (fixed.swapbars) 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. */ /*************************************************/ int handle_messages(WimpMessage * m, void * handle) { switch (m->hdr.action_code) { case Wimp_MQuit:quit=1; break; case Wimp_MMenusDeleted:menusrc = Menu_None; break; case Wimp_MHelpReply: { if (fixed.claimhelp) { ObjectId o, a = 0; browser_data * b = NULL; WimpGetPointerInfoBlock i; wimp_get_pointer_info(&i); if (window_wimp_to_toolbox(0, i.window_handle, i.icon_handle, &o, NULL)) break; /* If we can get an ancestor, the pointer is over e.g. a toolbar */ /* - otherwise, assume it is over a browser window. */ toolbox_get_ancestor(0, o, &a, NULL); if (a) { toolbox_get_client_handle(0, a, (void *) &b); } else toolbox_get_client_handle(0, o, (void *) &b); /* If we haven't got a valid client handle, exit. */ if (!is_known_browser(b)) break; /* If the text is empty, there was no help for that item, */ /* so restore the old status display, if there was a */ /* help display already there. */ if (!*m->data.help_reply.text) { if (b->status_help != NULL) { b->status_help = NULL; toolbars_cancel_status(b, Toolbars_Status_Help); } } else { /* Otherwise update the status bar with the help text, */ /* if the text has changed. */ if ( !b->status_help || ( b->status_help && strcmp(b->status_help, m->data.help_reply.text) ) ) { StrNCpy0(last_help, m->data.help_reply.text); b->status_help = last_help; toolbars_update_status(b, Toolbars_Status_Help); } } } } 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()); } 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; case Browser_Message_PrintError: { if (m->hdr.size == 20) { /* RISC OS 2 printer manager's PrintBusy response */ erb.errnum = Utils_Error_Custom_Message; StrNCpy0(erb.errmess, lookup_token("PrintBusy:The printer is currently busy.", 0, 0)); show_error_ret(&erb); } /* RISC OS 3 !Printers general error response */ else show_error_ret((_kernel_oserror *) &m->data); } break; case Browser_Message_PrintTypeOdd: { WimpMessage ptk; if (m->hdr.your_ref && m->hdr.your_ref == printer_message_ref) { /* The printer manager sent PrintTypeOdd as a reply to this */ /* task (not a broadcast), so go ahead and print. */ printer_message_ref = 0; /* Send PrintTypeKnown */ ptk.hdr.size = 20; ptk.hdr.your_ref = m->hdr.my_ref; ptk.hdr.action_code = Browser_Message_PrintTypeKnown; ChkError(wimp_send_message(Wimp_EUserMessage, &ptk, m->hdr.sender, 0, NULL)); print_print(NULL); } // Commented out as the Alias$@PrintType_FF4 system variable does this job // anyway, and if we don't claim this message then anything else which may // have a better idea of what to do at least gets a chance to try. // // This currently doesn't work, incidentally; the conditions on the 'if' // are wrong (printer_message_ref has probably been set to 0, but I never // got the chance to properly debug this before removing it due to time // constraints...). // // else if (printer_message_ref && m->data.data_save.file_type == FileType_POUT) // { // /* If the printer doesn't understand PrintOut files, then */ // /* it may be broken (!) / PostScript. So reply, and copy */ // /* the file to the printer device directly. */ // // printer_message_ref = 0; // // ptk.hdr.size = 20; // ptk.hdr.your_ref = m->hdr.my_ref; // ptk.hdr.action_code = Browser_Message_PrintTypeKnown; // // ChkError(wimp_send_message(Wimp_EUserMessage, &ptk, m->hdr.sender, 0, NULL)); // // _swix(OS_FSControl, // _INR(0,3), // // 26, // m->data.data_save.leaf_name, // "printer:", // 2); /* Flags - 'Force' set, but no others. */ // } } break; case Wimp_MDataSaveAck: { if (m->hdr.your_ref == printer_message_ref) { WimpMessage dl; int file_size; /* Print to a file in Printer$Temp, then send a */ /* DataLoad to the printer manager. */ printer_message_ref = 0; print_print(m->data.data_save.leaf_name); _swix(OS_File, _INR(0,1) | _OUT(4), 23, m->data.data_save.leaf_name, &file_size); dl.hdr.size = 64; dl.hdr.your_ref = m->hdr.my_ref; dl.hdr.action_code = Wimp_MDataLoad; dl.data.data_load.destination_window = m->data.data_save.destination_window; dl.data.data_load.destination_icon = m->data.data_save.destination_icon; dl.data.data_load.estimated_size = file_size; dl.data.data_load.file_type = FileType_POUT; _swix(OS_File, _INR(0,2), 18, m->data.data_save.leaf_name, FileType_POUT); strcpy(dl.data.data_load.leaf_name, m->data.data_save.leaf_name); ChkError(wimp_send_message(Wimp_EUserMessage, &dl, m->hdr.sender, 0, NULL)); } } break; case Wimp_MDataOpen: { /* Don't want to load a text file from double-clicking, */ /* only by dragging to a window or the icon bar icon. */ /* Similarly, ANT's URL files should only be loaded if */ /* dragged on, and the same is true of URI files if we */ /* are using the URI handler. */ if ( m->data.data_open.file_type == FileType_TEXT || m->data.data_open.file_type == FileType_URL || ( m->data.data_open.file_type == FileType_URI && uri_module_present ) ) break; /* Now treat as a DataLoad message ready to drop */ /* through to the Wimp_MDataLoad case statement. */ /* This avoids duplicating the loader code. */ m->data.data_load.destination_window = 0; /* Force a new window to open */ m->data.data_load.destination_icon = -1; m->data.data_load.estimated_size = 0; } /* So let the above fall through to Wimp_MDataLoad... */ case Wimp_MDataLoad: { /* Proceed only if it's a filetype we can handle */ if ( m->data.data_load.file_type == FileType_HTML || m->data.data_load.file_type == FileType_TEXT || m->data.data_load.file_type == FileType_GIF || m->data.data_load.file_type == FileType_JPEG || m->data.data_load.file_type == FileType_PNG || m->data.data_load.file_type == FileType_TIFF || m->data.data_load.file_type == FileType_URL || m->data.data_load.file_type == FileType_URI ) { if (m->hdr.action_code == Wimp_MDataOpen) { /* If we've fallen into the DataLoad code from the DataOpen */ /* code above, need to send a DataLoadAck message now. */ WimpMessage dla = *m; dla.hdr.sender = task_handle; dla.hdr.your_ref = m->hdr.my_ref; dla.hdr.action_code = Wimp_MDataLoadAck; ChkError(wimp_send_message(Wimp_EUserMessage, &dla, m->hdr.sender, 0, NULL)); } if (m->data.data_load.destination_window <= 0) { /* Load file to icon bar - i.e. open a new window. */ char url[Limits_URL]; if ( m->data.data_load.file_type != FileType_URI && m->data.data_load.file_type != FileType_URL ) { StrNCpy0(url, m->data.data_load.leaf_name); urlutils_pathname_to_url(url, sizeof(url)); } else urlutils_load_uri_file(url, sizeof(url), m->data.data_load.leaf_name); ChkError(windows_create_browser(url, NULL, NULL, NULL, 0)); } else { /* Load file to a browser window. Need to find it's */ /* browser_data structure. */ char url[Limits_URL]; ObjectId o; browser_data * b; ChkError(window_wimp_to_toolbox(0, m->data.data_load.destination_window, m->data.data_load.destination_icon, &o, NULL)); ChkError(toolbox_get_client_handle(0, o, (void *) &b)); /* Is the client handle a known browser_data structure? */ if (is_known_browser(b)) { /* It is, so deal with the file */ if ( m->data.data_load.file_type != FileType_URI && m->data.data_load.file_type != FileType_URL ) { StrNCpy0(url, m->data.data_load.leaf_name); urlutils_pathname_to_url(url, sizeof(url)); } else urlutils_load_uri_file(url, sizeof(url), m->data.data_load.leaf_name); ChkError(fetchpage_new(b, url, 1, 0)); } } } } break; case URI_MProcess: { URIProcessMessage * uri = (URIProcessMessage *) &m->data; int ok; unsigned int sender = m->hdr.sender; /* Can we handle this URI? */ ok = urlutils_check_protocols(uri->uri); #ifdef TRACE if (tl & (1u<<21)) Printf("handle_messages: URI_MProcess '%s', ok = %d\n",uri->uri,ok); #endif /* If so, reply to the message and possibly start a fetch */ if (ok) { /* Only fetch if the flags bits don't say we're to just */ /* check the URI could be handled. */ if (!uri->flags.bits.check) { uri_queue * entry = urlutils_find_queue_entry(uri->uri_handle); if (entry) { ChkError(fetchpage_postprocess_uri(entry->b, uri ->uri, entry->flags & URIQueue_RecordInHistory ? 1 : 0)); /* Don't remove it from the queue of uri_queue structures yet - */ /* wait for the ReturnResult message for that. */ } else ChkError(windows_create_browser(uri->uri, NULL, NULL, NULL, 0)); } /* Now reply, saying that we've handled the message */ m->hdr.sender = task_handle; m->hdr.your_ref = m->hdr.my_ref; m->hdr.action_code = URI_MProcessAck; ChkError(wimp_send_message(Wimp_EUserMessage, m, sender, 0, NULL)); } } break; case URI_MReturnResult: { URIReturnResultMessage * uri = (URIReturnResultMessage *) &m->data; #ifdef TRACE if (tl & (1u<<21)) Printf("handle_messages: URI_MReturnResult, not_claimed = %d\n",uri->flags.bits.not_claimed); #endif /* Remove the entry from the queue */ ChkError(urlutils_remove_from_queue(uri->uri_handle)); /* If the URI was not claimed by anyone, give an appropriate error */ if (uri->flags.bits.not_claimed) { erb.errnum = Utils_Error_Custom_Message; StrNCpy0(erb.errmess, lookup_token("CannotFetch:The browser does not have a method of fetching the requested site.", 0,0)); show_error_ret(&erb); } } break; case URI_MDying: { /* If the URI handler is dying, don't try and use it anymore... */ uri_module_present = 0; } break; default: return 0; } 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) { switch (block->user_message_acknowledge.hdr.action_code) { case Browser_Message_PrintSave: { /* The PrintSave bounced, so the printer driver must not be loaded. */ /* Since we're not printing text, the PRMs say 'go for it'... */ print_print(NULL); } break; case Wimp_MHelpRequest: { /* If a HelpRequest bounces, there's no help on the item the pointer */ /* is over so allow the status display to go back to status again. */ if (fixed.claimhelp) { ObjectId o, a = -1; browser_data * b = NULL; WimpGetPointerInfoBlock i; wimp_get_pointer_info(&i); if (window_wimp_to_toolbox(0, i.window_handle, i.icon_handle, &o, NULL)) break; /* If we can get an ancestor, the pointer is over e.g. a toolbar */ /* - otherwise, assume it is over a browser window. */ toolbox_get_ancestor(0, o, &a, NULL); if (a) { toolbox_get_client_handle(0, a, (void *) &b); } else toolbox_get_client_handle(0, o, (void *) &b); /* If we haven't got a valid client handle, exit */ if (!is_known_browser(b)) break; /* Update the status line */ if (b->status_help != NULL) { b->status_help = NULL; toolbars_cancel_status(b, Toolbars_Status_Help); } } } default: return 0; } return 1; } /*************************************************/ /* handle_send_helpreq() */ /* */ /* Sends out HelpRequest messages for the item */ /* the mouse pointer is currently over, every */ /* 20 centiseconds or so. */ /* */ /* Parameters are as standard for a Wimp event */ /* handler. */ /*************************************************/ int handle_send_helpreq(int eventcode, WimpPollBlock * block, IdBlock * idb, void * handle) { int time_now; static int last_time = 0; static int last_window = 0; static int last_icon = 0; /* Only proceed if the fixed choices say to do so */ if (fixed.claimhelp) { /* Don't sent out requests too often */ _swix(OS_ReadMonotonicTime, _OUT(0), &time_now); if (time_now - last_time > 20) { WimpGetPointerInfoBlock i; WimpMessage m; last_time = time_now; wimp_get_pointer_info(&i); /* Don't send a request if the pointer isn't over a */ /* browser-owned window. */ if (task_handle == task_from_window(i.window_handle)) { /* Don't send out multiple requests for the same window/icon. */ if (i.icon_handle != last_icon || i.window_handle != last_window) { last_icon = i.icon_handle; last_window = i.window_handle; } else return 0; /* Build the message block and send the request */ m.hdr.size = 40; m.hdr.sender = task_handle; m.hdr.my_ref = 0; m.hdr.your_ref = 0; m.hdr.action_code = Wimp_MHelpRequest; m.data.help_request.mouse_x = i.x; m.data.help_request.mouse_y = i.y; m.data.help_request.buttons = i.button_state; m.data.help_request.window_handle = i.window_handle; m.data.help_request.icon_handle = i.icon_handle; ChkError(wimp_send_message(Wimp_EUserMessageRecorded, &m, i.window_handle, i.icon_handle, NULL)); } } } return 0; } /*************************************************/ /* 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_DisplayURL]; int len; *writable = 0; writablefield_get_value(0, idb->self_id, DisplayURL, 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.keyboardctl) { 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 0x00d: { char url[Limits_DisplayURL]; /* Read the new URL from the URL bar writable */ *url = 0; ChkError(writablefield_get_value(0, i, DisplayURL, url, sizeof(url), NULL)); url[sizeof(url) - 1] = 0; /* (Ensure termination) */ #ifdef ALIAS_URLS // Not implemented yet... #endif #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 return 1; } break; } } } /* Keypress wasn't from a URL bar, but was from something obtained */ /* within a browser window since we could get that window's tool */ /* bars from an ancestor object, which a main window doesn't have. */ else { /* Proceed if the keypress wasn't from an authorisation dialogue */ /* or a save dialogue */ if ( idb && idb->self_id != authorise_return_dialogue_id() && idb->self_id != save_return_dialogue_id() ) { if (key == 0x00d && b) { // Assume it was from an Open URL dialogue for now... char c[Limits_DisplayURL]; *c = 0; writablefield_get_value(0, idb->self_id, OpenWrit, c, sizeof(c), NULL); c[sizeof(c) - 1] = 0; /* (Ensure termination) */ ChkError(fetchpage_new(b, c, 1, 1)); key = 0; } } } } /* Keypress was not from an object obtained from a browser window, */ /* and if we couldn't find a client handle isn't from a browser */ /* window either. */ else { if (!b && key == 0x00d) { /* Not a browser window; must be a URL dialogue from the icon bar */ char c[Limits_DisplayURL]; *c = 0; writablefield_get_value(0, idb->self_id, OpenWrit, c, sizeof(c), NULL); c[sizeof(c) - 1] = 0; /* (Ensure termination) */ ChkError(windows_create_browser(c, NULL, NULL, NULL, 0)); key = 0; } } 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 (choices.keephighlight) 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 (choices.keephighlight) 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 */ if ( (ancestor->selected->style & IMG) && (ancestor->selected->type & TYPE_ISMAP) && !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.keyboardctl) { 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, choices.clearfirst); 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_History: 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_history_menu_popup() */ /* */ /* Handles clicks on a history menu popup item. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the history to show; */ /* */ /* Object ID of the toolbar holding */ /* the popup; */ /* */ /* Component ID of the item that was */ /* clicked on (DisplayMLeft or */ /* DisplayMenu - see TBEvents.h); */ /* */ /* 1 to show the URLs, 0 to show */ /* page titles where available. */ /*************************************************/ static _kernel_oserror * handle_history_menu_popup(browser_data * b, ObjectId toolbar, ComponentId left_or_right, int show_urls) { _kernel_oserror * e; WimpGetWindowStateBlock state; BBox menu; /* If there's already a menu open, close it */ /* (so the action is to toggle the menu). */ if (menusrc == Menu_History && menuhdl == b) { menusrc = Menu_None; menuhdl = NULL; return wimp_create_menu((void *) -1, 0, 0); } /* Get the Wimp handle for the tool bar and get the window state */ e = window_get_wimp_handle(0, toolbar, &state.window_handle); if (e) return e; e = wimp_get_window_state(&state); if (e) return e; /* Get the bounding box of the popup icon that was used */ e = gadget_get_bbox(0, toolbar, left_or_right, &menu); if (e) return e; /* Convert that to screen coords ready for opening the menu */ /* next to it. */ coords_box_toscreen(&menu, (WimpRedrawWindowBlock *) &state); /* The menu about to be shown is a History list, from the browser */ /* window tool bar. Ask the History code to build the menu. */ if (left_or_right == DisplayMenu) { /* Build and show menu to right of menu icon for DisplayMenu object */ e = (history_build_menu(b, menu.xmax - 2, menu.ymax, show_urls, 0)); if (e) return e; } else { /* Otherwise, show it to the left of the icon */ e = history_build_menu(b, menu.xmin - 4, menu.ymin + 4, show_urls, 1); if (e) return e; } return NULL; } /*************************************************/ /* 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) { /* 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 StatsCover: /* Clicking on a covering gadget is equivalent */ case DisplayStats: /* 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, DisplayURL); } } break; case DisplayMLeft: /* Drop through to DisplayMenu case */ case DisplayMenu: { ChkError(handle_history_menu_popup(handle, idb->self_id, idb->self_component, b->mouse_click.buttons & Wimp_MouseButtonMenu ? !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 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.keyboardctl && 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 = (fixed.ignoreadjust || 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. */ if (eventcode >= 0) { ChkError(wimp_get_pointer_info(&i)); 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) ) ) ) { history_record_global(p->anchor); browser_flash_token(handle, p); used = 1; } /* If shift and control are not held down, and we have a link, follow the link */ if (!shift && !ctrl) { if (p->anchor) { 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) { /* Image maps */ if ((p->style & IMG) && (p->type & TYPE_ISMAP)) { char coords[64]; browser_data * targetted; 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); history_record_global(p->anchor); targetted = frames_find_target(handle, p); if (targetted || choices.full_screen) { /* If a named target was found, open in that. Otherwise we must */ /* be running full screen, so can't open a new window; in this */ /* case, open in the ancestor. */ ChkError(fetchpage_new_add(targetted ? targetted : ancestor, p->anchor, 1, 1, coords, adj)); } else { /* If we've reached here, a named target wasn't found but the */ /* browser isn't running full screen either, so open a new */ /* window with the name specified in the link. */ ChkError(windows_create_browser(p->anchor, NULL, NULL, p->target, 0)); } } used = 1; } /* Otherwise, a simple link */ else { if (!adj) { browser_data * targetted; history_record_global(p->anchor); targetted = frames_find_target(handle, p); /* Don't want to ever open a new window if configured */ /* to run full screen. */ if (targetted || choices.full_screen) { /* If a named target was found, open in that. Otherwise we must */ /* be running full screen, so can't open a new window; in this */ /* case, open in the ancestor. */ ChkError(fetchpage_new(targetted ? targetted : ancestor, p->anchor, 1, 1)); } else { /* If we've reached here, a named target wasn't found but the */ /* browser isn't running full screen either, so open a new */ /* window with the name specified in the link. */ ChkError(windows_create_browser(p->anchor, NULL, NULL, p->target, 0)); } } /* Yes, this 'else' would mean that even if running */ /* full screen, an Adjust click would open a new */ /* window - but note the fixed.ignoreadjust choices */ /* option, which disables the use of adjust and can */ /* be used in conjunction with the full screen */ /* option. */ else ChkError(windows_create_browser(p->anchor, NULL, NULL, NULL, 0)); 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 // { // WimpDragBox box; // // box.wimp_window = handle->window_handle; // box.drag_type = 12; /* Horizontal and vertical drag to scroll */ // // box.dragging_box.xmin = b->mouse_click.mouse_x; // box.dragging_box.ymin = b->mouse_click.mouse_y; // box.dragging_box.xmax = b->mouse_click.mouse_x; // box.dragging_box.ymax = b->mouse_click.mouse_y; // // wimp_drag_box(&box); // } } else { /* If Shift is held down, open the object into a save dialogue. */ if (shift) { if (p->anchor) { if (!adj) { handle->savelink = 1; ChkError(fetchpage_new(handle, p->anchor, 1, 1)); } else ChkError(windows_create_browser(p->anchor, NULL, NULL, NULL, 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) { 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, ButtonHome); from_menu = handlers_menu_or_toolbar(idb); urlutils_create_home_url(home, sizeof(home)); if (from_menu || fixed.ignoreadjust || !adjust()) ChkError(fetchpage_new(b, home, 1, 1)); else ChkError(windows_create_browser(home, NULL, NULL, NULL, 0)); /* 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, ButtonBack); from_menu = handlers_menu_or_toolbar(idb); ChkError(history_fetch_backwards(b, (from_menu || fixed.ignoreadjust) ? 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, ButtonForward); from_menu = handlers_menu_or_toolbar(idb); ChkError(history_fetch_forwards(b, (from_menu || fixed.ignoreadjust) ? 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; handlers_get_call_info(&b, NULL, idb, ButtonStop); /* 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); else frames_abort_fetching(utils_ancestor(b), 1); } else frames_abort_fetching(utils_ancestor(b), 1); /* Grey / ungrey buttons, as the state may change as */ /* a result of being selected. */ ChkError(toolbars_set_button_states(b)); /* Broadcast an AppControl message to stop any further */ /* activity in WebServ - this will eventually be directed */ /* straight at WebServ rather than broadcast... */ if (fixed.stopwebserv && !b->ancestor) ChkError(utils_stop_webserv()); 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, ButtonReload); from_menu = handlers_menu_or_toolbar(idb); new_view = (from_menu || fixed.ignoreadjust) ? 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 this is fails for whatever reason, */ /* don't forget to clear the reloadng flag before */ /* reporting the error. */ if (!new_view) { b->reloading = 1; e = fetchpage_new(b, b->urlddata, 0, 1); if (e) { b->reloading = 0; show_error_ret(e); } } /* Otherwise, just fetch the URL in a new window. */ else ChkError(windows_create_browser(b->urlddata, NULL, NULL, NULL, 0)); } else { /* Otherwise, get a URL string from the URL bar and do */ /* a fresh load of that. */ char url[Limits_DisplayURL]; *url = 0; writablefield_get_value(0, t, DisplayURL, 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, 0)); } } /* 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) { browser_data * b; char path[Limits_URL]; int from_menu = 0; handlers_get_call_info(&b, NULL, idb, ButtonViewHot); 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 (fixed.appendurls) // { // 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 (fixed.appendurls && browser_current_url(b)) { int len; lookup_token("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 && !fixed.ignoreadjust) ChkError(windows_create_browser(path, NULL, NULL, NULL, 0)); else ChkError(fetchpage_new(b, path, 1, 1)); 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) { 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) { return 0; } /*************************************************/ /* 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) { return 0; } /*************************************************/ /* 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, ButtonGoTo); /* 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, DisplayURL); /* 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_DisplayURL]; 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, DisplayURL, 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, DisplayURL, 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, DisplayURL); /* 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, ButtonGo); // 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 = DisplayURL; 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, ButtonCancel); /* 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 EBiStateKeyed event, which says */ /* that the bistate button should be actioned. */ /* 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, ButtonBi); /* 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 ETriStateKeyed event, which says */ /* that the tristate button should be actioned. */ /* 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, ButtonTri); /* 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_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, DisplayURL, 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, DisplayURL); /* 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), */ /* presuming that gadget exists. */ /* */ /* 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; 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, DisplayMenu, &test)) which = 1; else if (!gadget_get_bbox(0, t, DisplayMLeft, &test)) which = 2; else which = 0; if (which) { /* Show the menu */ ChkError(handle_history_menu_popup(b, t, which == 1 ? DisplayMenu : DisplayMLeft, 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) { ChkError(proginfo_set_version(0, idb->self_id, lookup_token("Version:Unknown!",1,0))); 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 (fixed.keepcaret) { 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; }