/* 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 : Toolbars.c */ /* Purpose: Toolbar-related functions for the */ /* browser */ /* Author : A.D.Hodgkinson */ /* History: 21-Nov-96: Created */ /***************************************************/ #include <stdlib.h> #include <stdio.h> #include <string.h> #include "swis.h" #include "flex.h" #include "wimp.h" #include "wimplib.h" #include "event.h" #include "toolbox.h" #include "window.h" #include "gadgets.h" #include "Dialler.h" #include "svcprint.h" #include "Global.h" #include "TBEvents.h" #include "Utils.h" #include "Browser.h" #include "Fetch.h" #include "Handlers.h" #include "Images.h" #include "Memory.h" #include "Mouse.h" #include "Reformat.h" #include "URLUtils.h" #include "Toolbars.h" /* Locals */ static int bar_overlap = -1; /* Local types */ /* Used to record the requirement of a particular browser to */ /* display a certain message in the status bar. For a given */ /* page which may include frames, an array of these is built */ /* as the frameset is defined and starts to fetch pages. */ /* The actual status message is derived from examining the */ /* priorities of all of the messages. */ typedef struct status_content { browser_data * b; status_type type; unsigned int start; unsigned int end; } status_content; /* Static function prototypes */ static void toolbars_set_size (ObjectId o, int height, int yscroll, int flags); static int toolbars_find_overlap (ObjectId o); static int toolbars_bar_overlap (ObjectId o); static _kernel_oserror * toolbars_update_specific_status (browser_data * b, browser_data * ancestor, int entry, status_type type); static int toolbars_write_status (browser_data * ancestor); static status_type toolbars_return_inferred (browser_data * b); static _kernel_oserror * toolbars_infer_status (browser_data * b, browser_data * ancestor, int entry); static _kernel_oserror * toolbars_add_status_item (browser_data * ancestor); static status_type toolbars_return_act_status (browser_data * b); static int toolbars_calculate_progress (browser_data * b); static void toolbars_create_progress (browser_data * b, char * buffer); /*************************************************/ /* toolbars_get_upper() */ /* */ /* Returns the object ID of the upper toolbar. */ /* This may well not be the internal top left */ /* toolbar of the window. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the toolbar. */ /* */ /* Returns: Object ID of the toolbar, or NULL */ /* for an error / no upper toolbar. */ /*************************************************/ ObjectId toolbars_get_upper(browser_data * b) { ObjectId t; if (b->all_in_bottom || fixed.swapbars) { if ( window_get_tool_bars(InternalBottomLeft, b->self_id, &t, NULL, NULL, NULL) ) return NULL; else return t; } if ( window_get_tool_bars(InternalTopLeft, b->self_id, NULL, &t, NULL, NULL) ) return NULL; else return t; } /*************************************************/ /* toolbars_get_lower() */ /* */ /* Returns the object ID of the lower toolbar. */ /* This may well not be the internal bottom */ /* left toolbar of the window. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the toolbar. */ /* */ /* Returns: Object ID of the toolbar, or NULL */ /* for an error / no lower toolbar. */ /*************************************************/ ObjectId toolbars_get_lower(browser_data * b) { ObjectId t; if (b->all_in_top || fixed.swapbars) { if ( window_get_tool_bars(InternalTopLeft, b->self_id, NULL, &t, NULL, NULL) ) return NULL; else return t; } if ( window_get_tool_bars(InternalBottomLeft, b->self_id, &t, NULL, NULL, NULL) ) return NULL; else return t; } /*************************************************/ /* toolbars_set_size() */ /* */ /* Sets the vertical visible height and scroll */ /* position of a given toolbar. */ /* */ /* Parameters: The object ID of the toolbar; */ /* The visible height to set it to; */ /* The y scroll offset to give it; */ /* Flags - alter InternalTopLeft, or */ /* InternalBottomLeft toolbar. */ /*************************************************/ static void toolbars_set_size(ObjectId o, int height, int yscroll, int flags) { WimpGetWindowStateBlock s; ObjectId p; /* Get the toolbar's parent object (the window) */ #ifdef TRACE if (tl & (1u<<1)) Printf("\ntoolbars_set_size: Called with object ID %p\n",(void *) o); #endif toolbox_get_parent(0,o,&p,NULL); #ifdef TRACE if (tl & (1u<<1)) Printf("toolbars_set_size: Parent ID is %p\n",(void *) p); #endif ChkError(toolbox_show_object(0,o,0,0,p,-1)); /* Get the toolbar's wimp handle and size through Wimp_GetWindowState */ ChkError(window_get_wimp_handle(0,o,&s.window_handle)); ChkError(wimp_get_window_state(&s)); /* Set the vertical size */ s.visible_area.ymin = s.visible_area.ymax - height; ChkError(toolbox_show_object(0,o,1,&s.visible_area,p,-1)); /* This call is needed for the toolbox to recognise the new size */ if (flags == 2) ChkError(window_set_tool_bars(2, p, 0, o, 0, 0)); /* Top left */ else ChkError(window_set_tool_bars(1, p, o, 0, 0, 0)); /* Bottom left */ /* Set the scroll position */ s.yscroll = yscroll; ChkError(toolbox_show_object(0,o,1,&s.visible_area,p,-1)); #ifdef TRACE if (tl & (1u<<1)) Printf("toolbars_set_size: Successful\n"); #endif } /*************************************************/ /* toolbars_find_overlap() */ /* */ /* Finds the overlap of the height gadgets 0xd */ /* and 0xe in the upper toolbar, recording this */ /* in the local bar_overlap int to make future */ /* references faster. */ /* */ /* Parameters: The object ID of the toolbar. */ /*************************************************/ static int toolbars_find_overlap(ObjectId o) { BBox url, but; int overlap; if (gadget_get_bbox(0, o, URLSpacer, &url)) return 0; if (gadget_get_bbox(0, o, ButtonSpacer, &but)) return 0; overlap = but.ymax - url.ymin; if (overlap >= 0) return overlap; return 0; } /*************************************************/ /* toolbars_bar_overlap() */ /* */ /* Returns the overlap of the height gadgets 0xd */ /* and 0xe in the upper toolbar, using a local */ /* cached value for speed. */ /* */ /* Parameters: The object ID of the toolbar. */ /*************************************************/ static int toolbars_bar_overlap(ObjectId o) { if (bar_overlap < 0) bar_overlap = toolbars_find_overlap(o); return bar_overlap; } /*************************************************/ /* toolbars_set_presence() */ /* */ /* Reads the browser_data structure associated */ /* with a given browser window object ID, and */ /* ensures the toolbars associated with that */ /* window match the flags set inside the data */ /* structure. */ /* */ /* Parameters: The browser window's object ID; */ /* */ /* A flags word holding the toolbars */ /* to alter - InternalTopLeft, or */ /* InternalBottomLeft. */ /* */ /* Assumes: That the object ID really is a */ /* valid browser window. */ /*************************************************/ void toolbars_set_presence(ObjectId o, unsigned int flags) { browser_data * b; ObjectId t; #ifdef TRACE if (tl & (1u<<1)) Printf("\ntoolbars_set_presence: Called with object ID %p\n",(void *) o); #endif ChkError(toolbox_get_client_handle(0, o, (void *) &b)); /* If not a valid browser_data structure, exit */ if (!is_known_browser(b)) return; /* Toolbars can only exist on ancestor browser windows, not */ /* in any child frames. */ if (b->ancestor) b = b->ancestor; #ifdef TRACE if (tl & (1u<<1)) Printf("toolbars_set_presence: Browser data at %p\n",(void *) b); #endif /* At present, if toolbars are merged to the top or bottom of the */ /* window, presence cannot be set through this function. */ #ifdef TRACE if (tl & (1u<<1)) { if (b->all_in_top || b->all_in_bottom) Printf("toolbars_set_presence: Toolbars are fixed, exitting with no action\n",(void *) b); } #endif if (b->all_in_top || b->all_in_bottom) return; /* Otherwise, proceed */ if (flags & InternalTopLeft) { t = toolbars_get_upper(b); if (t) { #ifdef TRACE if (tl & (1u<<1)) Printf("toolbars_set_presence: Top left toolbar ID is %p\n",(void *) t); #endif /* URL and button bars both present */ if ((b->url_bar) && (b->button_bar)) toolbars_set_size(t, toolbars_button_height(b) + toolbars_url_height(b), 0, InternalTopLeft); /* URL bar only present */ else if ((b->url_bar) && !(b->button_bar)) toolbars_set_size(t, toolbars_url_height(b), 0, InternalTopLeft); /* Button bar only present */ else if (!(b->url_bar) && (b->button_bar)) { /* Find what would be the URL bar height if it was present on its own */ BBox w; gadget_get_bbox(0,t,URLSpacer,&w); /* Now set the toolbar size and Y scroll offset to show only the buttons */ toolbars_set_size(t, toolbars_button_height(b), - (w.ymax - w.ymin) + toolbars_bar_overlap(t), InternalTopLeft); } /* URL bar and button bar both absent */ else toolbox_hide_object(0,t); } } if (flags & InternalBottomLeft) { t = toolbars_get_lower(b); if (t) { #ifdef TRACE if (tl & (1u<<1)) Printf("toolbars_set_presence: Bottom left toolbar ID is %p\n",(void *) t); #endif /* Status bar present */ if (b->status_bar) ChkError(toolbox_show_object(0,t,0,NULL,o,-1)); /* Status bar absent */ else ChkError(toolbox_hide_object(0,t)); } } /* Make sure that the toolbars have the correct gadget positions */ if (choices.move_gadgets > 0) toolbars_move_gadgets(b); /* // */ /* Make sure that the window is completely redrawn */ /* // // { // WimpGetWindowStateBlock s; // s.window_handle = b->window_handle; // ChkError(wimp_get_window_state(&s)); // ChkError(wimp_force_redraw(-1, // s.visible_area.xmin, // s.visible_area.ymin, // s.visible_area.xmax, // s.visible_area.ymax)); // } */ #ifdef TRACE if (tl & (1u<<1)) Printf("\ntoolbars_set_presence: Successful\n"); #endif } /*************************************************/ /* toolbars_move_gadgets() */ /* */ /* Generally called as part of some window */ /* resize event, this function moves and/or */ /* resizes various gadgets in various toolbars, */ /* if the toolbars are present in the window. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the toolbars. */ /*************************************************/ void toolbars_move_gadgets(browser_data * p) { BBox b, g1, g2; int right, gap, width; ObjectId t; WimpGetWindowStateBlock s; _kernel_oserror * e; #ifdef TRACE if (tl & (1u<<1)) Printf("\ntoolbars_move_gadgets: Called\n"); #endif s.window_handle = p->window_handle; ChkError(wimp_get_window_state(&s)); b = s.visible_area; if ((b.xmax - b.xmin) < fixed.minimum_convergence) b.xmax = b.xmin + fixed.minimum_convergence; /* If the URL bar is present... */ if (p->url_bar) { t = toolbars_get_upper(p); if (t) { /* If the pop-up menu gadget for the History list needs to move, move it */ /* and resize the URL writable to maintain a fixed distance between the */ /* two gadgets. */ e = gadget_get_bbox(0, t, DisplayMenu, &g1); /* Get the popup's bounding box */ ChkError(gadget_get_bbox(0, t, DisplayURL, &g2)); /* Get the writable's bounding box */ /* Where should the right hand edge of the popup be? */ right = b.xmax - b.xmin - 4; /* If the popup doesn't exist, want to set up a BBox as if a zero */ /* width popup was present. */ if (e) g1.xmin = g2.xmax, g1.ymin = 0, g1.xmax = !right, g1.ymax = 0; /* Gap is the distance between the right hand edge of the URL writable */ /* and the left hand edge of the popup. */ gap = g1.xmin - g2.xmax; /* Is this the same as it is already? If not, move / resize the gadgets. */ if (right != g1.xmax) { if (!e) { /* Move the popup */ width = g1.xmax - g1.xmin; g1.xmax = right; g1.xmin = right - width; ChkError(gadget_move_gadget(0, t, DisplayMenu, &g1)); } /* Resize the URL writable */ g2.xmax = g1.xmin - gap; ChkError(gadget_move_gadget(0, t, DisplayURL, &g2)); } } } /* If the status bar is present... */ if (p->status_bar) { t = toolbars_get_lower(p); if (t) { /* As above, move the byte counter display field if need be, */ /* and if it does move, resize the status display field. */ ChkError(gadget_get_bbox(0, t, DisplayBytes, &g1)); ChkError(gadget_get_bbox(0, t, DisplayStats, &g2)); gap = g1.xmin - g2.xmax; right = b.xmax - b.xmin - 20; if (right != g1.xmax) { width = g1.xmax - g1.xmin; g1.xmax = right; g1.xmin = right - width; ChkError(gadget_move_gadget(0, t, DisplayBytes, &g1)); g2.xmax = g1.xmin - gap; ChkError(gadget_move_gadget(0, t, DisplayStats, &g2)); } } } #ifdef TRACE if (tl & (1u<<1)) Printf("toolbars_move_gadgets: Successful\n"); #endif } /*************************************************/ /* toolbars_animation() */ /* */ /* Advances the browser animation in the status */ /* bar by one frame. */ /* */ /* Parameters are as for a standard Wimp event */ /* handler (this is called on null events). */ /*************************************************/ int toolbars_animation(int eventcode, WimpPollBlock * b, IdBlock * idb, browser_data * handle) { if ((handle->status_bar) && (animation_frames > 0)) { ObjectId t; char v[8]; /* Find the status bar object Id */ t = toolbars_get_lower(handle); if (!t) return 0; /* Advance the frame counter and put a validation string */ /* that would give a button gadget the relevant animation */ /* sprite into v. */ handle->current_frame ++; if (handle->current_frame >= animation_frames) handle->current_frame = 0; sprintf(v,"sa%d",handle->current_frame); /* Set the validation string on the status bar animation */ /* button icon to v, so that the new sprite is shown. */ button_set_validation(0,t,DisplayAnim,v); } return 0; } /*************************************************/ /* toolbars_animation_drift() */ /* */ /* Advances the browser animation in the status */ /* bar by one frame, until it reaches the first */ /* frame, when it deregisters itself. */ /* */ /* Parameters are as for a standard Wimp event */ /* handler (this is called on null events). */ /*************************************************/ int toolbars_animation_drift(int eventcode, WimpPollBlock * b, IdBlock * idb, browser_data * handle) { if (handle->status_bar && animation_frames > 0) { ObjectId t; char v[8]; /* Find the status bar object Id. If it can't be */ /* found, deregister this handler and exit. */ t = toolbars_get_lower(handle); if (!t) { deregister_null_claimant(Wimp_ENull,(WimpEventHandler *) toolbars_animation_drift,handle); handle->anim_drift = 0; } /* Advance the frame counter and put a validation string */ /* that would give a button gadget the relevant animation */ /* sprite into v. */ handle->current_frame ++; if (handle->current_frame >= animation_frames) handle->current_frame = 0; sprintf(v,"sa%d",handle->current_frame); /* Set the validation string on the status bar animation */ /* button icon to v, so that the new sprite is shown. */ button_set_validation(0,t,DisplayAnim,v); /* Deregister the handler if at frame 0. */ if (handle->current_frame == 0) { deregister_null_claimant(Wimp_ENull,(WimpEventHandler *) toolbars_animation_drift,handle); handle->anim_drift = 0; } } return 0; } /*************************************************/ /* toolbars_animate_slow() */ /* */ /* Advances an animation in component */ /* DisplayAnim (see TBEvents.h) in an object of */ /* ID given in void * handle (must be cast to */ /* this to fit as a Wimp event handler) by one */ /* frame for each complete cycle of the main */ /* status animation (whether that is actually */ /* animating or not). */ /* */ /* So that more than one dialogue could be up */ /* and animating at once, the object's client */ /* handle is used to store the current animation */ /* frame. Consequently this routine is of no use */ /* to dialogues that need the client handle for */ /* other reasons. */ /* */ /* Parameters are as for a standard Wimp event */ /* handler (this is called on null events). */ /*************************************************/ int toolbars_animate_slow(int eventcode, WimpPollBlock * b, IdBlock * idb, void * handle) { ObjectId o = (ObjectId) handle; char v[8]; int slow_animation_frame; if (toolbox_get_client_handle(0, o, (void **) &slow_animation_frame)) return 0; /* Advance the frame counter and put a validation string */ /* that would give a button gadget the relevant animation */ /* sprite into v. */ slow_animation_frame ++; if (slow_animation_frame >= animation_frames * animation_frames) slow_animation_frame = 0; sprintf(v, "sa%d", slow_animation_frame / animation_frames); /* Set the validation string on the status bar animation */ /* button icon to v, so that the new sprite is shown. */ button_set_validation(0, o, DisplayAnim, v); toolbox_set_client_handle(0, o, (void *) slow_animation_frame); return 0; } /*************************************************/ /* toolbars_hide_cgi() */ /* */ /* This routine will look through a URL for a */ /* ? and turn it into a terminator. This can be */ /* used to hide CGI information from the user. */ /* */ /* Parameters: Pointer to the URL, which must be */ /* writable in memory, as the first */ /* '?' it contains (if any) will be */ /* turned into a zero byte. */ /*************************************************/ void toolbars_hide_cgi(char * url) { char * search = url; while (*search) { if (*search == '?') { *search = '\0'; break; } search++; } } /*************************************************/ /* toolbars_hide_internal() */ /* */ /* When passed a pointer to an internal URL, */ /* this routine finds the separator between the */ /* internal specifier plus message token and the */ /* extra information in the URL, and copies this */ /* extra data down to the start of the URL. */ /* */ /* Parameters: Pointer to the URL, which may be */ /* altered quite significantly; so */ /* this should be a local copy held */ /* by the caller to avoid corrupting */ /* any important full version of the */ /* URL. */ /*************************************************/ void toolbars_hide_internal(char * iurl) { char * extra; int exoff; exoff = urlutils_internal_extra(iurl); if (!exoff) return; extra = iurl + exoff; memmove(iurl, extra, strlen(extra) + 1); } /*************************************************/ /* toolbars_update_status() */ /* */ /* The standard external way of updating a */ /* browser window's status bar. The given */ /* requests that a given message type (see the */ /* definition of status_type in Toolbars.h) */ /* be used for the status bar. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* which wants the message putting */ /* in its (or its ancestor's) */ /* status bar; */ /* */ /* Type of message to display. */ /*************************************************/ _kernel_oserror * toolbars_update_status(browser_data * b, status_type type) { _kernel_oserror * e; browser_data * ancestor; status_content * contents; int i, found; #ifdef TRACE if (tl & (1u<<1)) Printf("toolbars_update_status: Called for %p, type %d\n",b,type); #endif /* Find the ancestor that the toolbars lie in */ ancestor = b->ancestor; if (!ancestor) ancestor = b; /* Refer to the array through 'contents' - slightly more */ /* convenient than 'ancestor->status_contents' every time! */ contents = ancestor->status_contents; /* See if the given browser has already got a message */ /* registered with the base browser... */ if (contents) { #ifdef TRACE if (tl & (1u<<1)) { Printf("\ntoolbars_update_status():"); Printf("\nUpdating for browser %p; old contents:\n",b); Printf("\nEntry | b | Message | Decay"); Printf("\n------+----------+------------+------\n"); for (i = 0; i < ancestor->nstatus; i++) { switch (contents[i].type) { default: Printf("%d | %08x | %02d UNKNOWN | %d\n",i, contents[i].b, contents[i].type, contents[i].end - contents[i].start); break; case 0: Printf("%d | %08x | %02d Ready | %d\n",i, contents[i].b, contents[i].type, contents[i].end - contents[i].start); break; case 1: Printf("%d | %08x | %02d Viewing | %d\n",i, contents[i].b, contents[i].type, contents[i].end - contents[i].start); break; case 2: Printf("%d | %08x | %02d Format | %d\n",i, contents[i].b, contents[i].type, contents[i].end - contents[i].start); break; case 3: Printf("%d | %08x | %02d Process | %d\n",i, contents[i].b, contents[i].type, contents[i].end - contents[i].start); break; case 4: Printf("%d | %08x | %02d GetPics | %d\n",i, contents[i].b, contents[i].type, contents[i].end - contents[i].start); break; case 5: Printf("%d | %08x | %02d Fetch | %d\n",i, contents[i].b, contents[i].type, contents[i].end - contents[i].start); break; case 6: Printf("%d | %08x | %02d LinkTo | %d\n",i, contents[i].b, contents[i].type, contents[i].end - contents[i].start); break; case 7: Printf("%d | %08x | %02d Help | %d\n",i, contents[i].b, contents[i].type, contents[i].end - contents[i].start); break; } } Printf("\n"); } #endif /* Loop round all the status entries, looking for */ /* one which matches the given browser_data. */ /* If found, set 'found' to the array index of */ /* that entry. */ found = -1; for (i = 0; i < ancestor->nstatus; i++) { if (contents[i].b == b) { found = i; break; } } } else { found = -1; #ifdef TRACE if (tl & (1u<<1)) { Printf("\ntoolbars_update_status():"); Printf("\nUpdating for browser %p; this has no contents array.\n\n",b); } #endif } /* If not found, add an entry for this browser */ if (found < 0) { #ifdef TRACE if (tl & (1u<<1)) Printf("toolbars_update_status: Adding item\n"); #endif e = toolbars_add_status_item(ancestor); if (e) return e; found = ancestor->nstatus - 1; contents = ancestor->status_contents; if (contents && found >= 0) { contents[found].b = b; contents[found].start = 0; contents[found].end = 0; contents[found].type = Toolbars_Status_NoType; } } /* Proceed if there's definitely an entry to proceed with... */ if (contents && found >= 0) { #ifdef TRACE if (tl & (1u<<1)) Printf("toolbars_update_status: Exitting through toolbars_update_specific_status\n"); #endif return toolbars_update_specific_status(b, ancestor, found, type); } else { #ifdef TRACE if (tl & (1u<<1)) Printf("toolbars_update_status: Failed, exitting quietly\n"); #endif return NULL; } } /*************************************************/ /* toolbars_update_specific_status() */ /* */ /* Updates the given browser's given entry in */ /* the given ancestor's status_content array the */ /* given message type... Messages will only be */ /* allowed to rise in priority, with the */ /* timeouts on all messages dealing with letting */ /* things fall back again. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* which owns the entry to be */ /* updated; */ /* */ /* Pointer to the ancestor */ /* browser_data struct holding the */ /* status_content array; */ /* */ /* Index of the entry to update; */ /* */ /* Message type to update it with. */ /*************************************************/ static _kernel_oserror * toolbars_update_specific_status(browser_data * b, browser_data * ancestor, int entry, status_type type) { _kernel_oserror * e; status_content * contents = ancestor->status_contents; int timeout = 0; int highest; #ifdef TRACE if (tl & (1u<<1)) Printf("toolbars_update_specific_status: Called for %p (ancestor %p)\n", b, ancestor); #endif /* This should never happen, but be defensive anyway... Fail */ /* if the ancestor seems to have no status_content array. */ if (!contents || !ancestor->nstatus) { #ifdef TRACE erb.errnum = Utils_Error_Custom_Normal; StrNCpy0(erb.errmess, "No status_content array in toolbars_update_specific_status()") show_error_ret(&erb); #endif return NULL; } /* If the message type requested is lower priority */ /* than that already in use by this browser, don't */ /* do anything - let the timeouts handle it. */ if (type < contents[entry].type) return NULL; /* Otherwise, update the message type */ contents[entry].type = type; /* Reset the timeout counter */ e = _swix(OS_ReadMonotonicTime, _OUT(0), &contents[entry].start); switch (contents[entry].type) { case Toolbars_Status_NoType: case Toolbars_Status_Ready: case Toolbars_Status_Viewing: { /* For the idle cases, set all timers to zero */ contents[entry].start = timeout = 0; break; } default: case Toolbars_Status_Formatting: case Toolbars_Status_Processing: case Toolbars_Status_GetPics: case Toolbars_Status_Fetching: { /* For some messages, a general timeout */ timeout = atoi(lookup_token("ShowMiscFor:50",0,0)); break; } case Toolbars_Status_LinkTo: { /* Specific timeout for LinkTo messages */ timeout = atoi(lookup_choice("ShowLinksFor:200",0,0)); break; } case Toolbars_Status_Help: { /* Specific timeout for Help messages */ timeout = atoi(lookup_token("ShowHelpFor:600",0,0)); break; } } contents[entry].end = contents[entry].start + timeout; /* Now reflect the highest priority message of the array */ highest = toolbars_write_status(ancestor); /* Work out if the timeout handler needs to be registered */ /* or deregistered. */ switch (highest) { case Toolbars_Status_NoType: case Toolbars_Status_Ready: case Toolbars_Status_Viewing: { /* If the highest priority is an idle case and the handler is */ /* registered, deregister it. */ if (ancestor->status_handler) { ancestor->status_handler = 0; deregister_null_claimant(Wimp_ENull, (WimpEventHandler *) toolbars_timeout_status, ancestor); } break; } default: { /* Otherwise, for non-idle cases, register the handler if it */ /* isn't already present. */ if (!ancestor->status_handler) { ancestor->status_handler = 1; register_null_claimant(Wimp_ENull, (WimpEventHandler *) toolbars_timeout_status, ancestor); } break; } } /* Finished */ #ifdef TRACE if (tl & (1u<<1)) Printf("toolbars_update_specific_status: Successful\n"); #endif return NULL; } /*************************************************/ /* toolbars_write_status() */ /* */ /* Reflects the highest priority message in a */ /* status_content array in the status bar of */ /* the array's owner. */ /* */ /* Called by toolbars_update_status as part of */ /* its normal operation. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* which is an ancestor holding a */ /* status_content array. */ /* */ /* Returns: The message type that was chosen */ /* as the highest priority. */ /* */ /* Assumes: That the ancestor browser_data */ /* pointer is not NULL. */ /*************************************************/ static int toolbars_write_status(browser_data * ancestor) { char new_status[MaxStaLen]; char * use_status = new_status; char url[sizeof(new_status)]; int dontappend = 0; int found = -1; int i, count; status_type highest; ObjectId t; browser_data * priority; status_content * contents = ancestor->status_contents; /* Fail if there's no status_content array */ if (!contents || !ancestor->nstatus) { #ifdef TRACE erb.errnum = Utils_Error_Custom_Normal; StrNCpy0(erb.errmess, "No status_content array in toolbars_write_status()") show_error_ret(&erb); #endif return 0; } /* Get the ID of the toolbar in which the status bar resides */ t = toolbars_get_lower(ancestor); if (!t) return 0; /* Examine all entries for the highest priority message */ highest = Toolbars_Status_NoType; count = 0; for (i = 0; i < ancestor->nstatus; i++) { /* If the message for this entry is higher than the highest recorded */ /* so far, then record this message as the highest. Reset the count */ /* of the number of times this message has been encountered to zero */ /* and remember the index into the array in 'found'. */ if (contents[i].type > highest) highest = contents[i].type, count = 1, found = i; /* If the message for this entry is the same as the highest recorded */ /* so far, then increment the count of the number of times the */ /* message has been encountered. */ else if (contents[i].type == highest) count++; } /* Now have the highest priority message in 'highest', the number of */ /* times it has been requested in 'count', and the index of the first */ /* occurrence of this message in 'found'. */ priority = contents[found].b; /* According to the highest priority message, write the new status into 'new_status' */ switch (highest) { default: /* (Drop through */ case Toolbars_Status_Ready: /* to Viewing) */ case Toolbars_Status_Viewing: { char title[MaxTiBLen + 1]; memset(title, 0, sizeof(title)); /* Try to get the window's title. If this generates an error or */ /* is equal to the 'blank page' case, use 'Ready' for the status. */ if ( window_get_title(0,ancestor->self_id,title,sizeof(title),NULL) || !strcmp(title, lookup_token("BlankPage:Blank page",0,0)) ) StrNCpy0(new_status, lookup_token("Ready:Ready",0,0)) else { char format[50]; StrNCpy0(format, lookup_token("Page:Viewing '%s'",0,0)) StrNCpy0(url, title) url[sizeof(new_status) - strlen(format) - 1] = 0; sprintf(new_status, format, url); } if (new_status[0] == '-') use_status++, dontappend = 1; } break; case Toolbars_Status_GetPics: { int images = image_count_pending(ancestor); if (images == 1) StrNCpy0(new_status, lookup_token("GetPic:Fetching 1 image...", 0, 0)) else if (images) sprintf(new_status, lookup_token("GetPics:Fetching %d images...", 0, 0), images); else StrNCpy0(new_status, lookup_token("GetPic0:All current images fetched...", 0, 0)); if (new_status[0] == '-') use_status++, dontappend = 1; } break; case Toolbars_Status_Fetching: /* (Drop through */ case Toolbars_Status_Processing: /* to Formatting) */ case Toolbars_Status_Formatting: { char format[50]; /* For many occurrences, report an appropriate 'cumulative' */ /* message; otherwise, report the one specific case. */ /* */ /* If the browser has a parent - i.e. this is a frames */ /* document - then the 'many' equivalents are always used; */ /* this is to stop flickering between the 'single' and */ /* 'many' states during a fetch/reformat/etc. */ if (count > 1 || priority->real_parent) { if (highest == Toolbars_Status_Fetching) StrNCpy0(new_status, lookup_token("FetchMany:Fetching frames contents...", 0,0)) else if (highest == Toolbars_Status_Processing) StrNCpy0(new_status, lookup_token("ProcessMany:Processing frames contents...",0,0)) else StrNCpy0(new_status, lookup_token("FormatMany:Formatting frames contents...", 0,0)) } else { /* Find the URL to display, if any */ if (browser_fetch_url (priority)) StrNCpy0(url, browser_fetch_url (priority)) else if (browser_current_url(priority)) StrNCpy0(url, browser_current_url(priority)) else *url = 0; if (*url) { /* Get the Messages file token as a format string for sprintf, into */ /* which the URL can be substituted. */ if (highest == Toolbars_Status_Fetching) StrNCpy0(format, lookup_token("Fetch:Fetching '%s'...", 0,0)) else if (highest == Toolbars_Status_Processing) StrNCpy0(format, lookup_token("Process:Processing '%s'...",0,0)) else StrNCpy0(format, lookup_token("Format:Formatting '%s'...", 0,0)) /* Possibly hide CGI information in the URL */ #ifdef HIDE_CGI toolbars_hide_cgi(url); #endif /* Ensure the URL isn't too long to fit int he new_status */ /* buffer along with the format string defined above. If */ /* the format string comes from a messages file we can't */ /* make assumptions about how many of its characters will */ /* actually appear in the final output string after the */ /* sprintf call, so we must treat it as if they all will */ /* appear - hence the ' - 1'. */ url[sizeof(new_status) - strlen(format) - 1] = 0; toolbars_hide_internal(url); /* Write the combined message into new_status */ sprintf(new_status, format, url); } else { /* The URL is not known, so can do a direct lookup into new_status */ if (highest == Toolbars_Status_Fetching) StrNCpy0(new_status, lookup_token("FetchUK:Fetching web page...", 0,0)) else if (highest == Toolbars_Status_Processing) StrNCpy0(new_status, lookup_token("ProcessUK:Processing web page...",0,0)) else StrNCpy0(new_status, lookup_token("FormatUK:Formatting web page...", 0,0)) } } if (new_status[0] == '-') use_status++, dontappend = 1; } break; case Toolbars_Status_LinkTo: /* (Drop through to Help) */ case Toolbars_Status_Help: { int filled = 0; HStream * over; if (highest == Toolbars_Status_LinkTo) { /* For comments on the use of the 'format' buffer, see the */ /* code above. */ over = priority->pointer_over; /* (First take the pointer_over token... */ /* */ if ( /* */ !over /* */ || /* */ ( /* */ over && /* ...and if it isn't suitable, go to the */ !over->anchor /* keyboard selected item, which may not be */ ) /* present either... */ || /* */ ( over && /* */ over->anchor && /* */ !*over->anchor /* */ ) /* */ ) /* */ over = ancestor->selected; /* ...so pointer takes priority over keyboard) */ if (over && over->anchor && *over->anchor) { char format[50]; StrNCpy0(format, lookup_token("LinkTo:Link to '%s'...",0,0)); StrNCpy0(url, over->anchor); #ifdef HIDE_CGI toolbars_hide_cgi(url); #endif url[sizeof(new_status) - strlen(format) - 1] = 0; toolbars_hide_internal(url); sprintf(new_status, format, url); if (new_status[0] == '-') use_status++, dontappend = 1; filled = 1; } } else { if (priority->status_help) { StrNCpy0(new_status, priority->status_help); dontappend = filled = 1; } } /* If not filled, the message is out of date; that is, no */ /* object is selected but the message is still on a timer. */ /* In that case, cancel it. */ if (!filled) { toolbars_cancel_status(priority, highest); return Toolbars_Status_Ready; } } break; } /* Right, finally have a message in new_status. Now */ /* append the fetch progress, if required. */ /* */ /* Any messages starting with '-' will never have a */ /* counter appended, as dontappend will have been */ /* set to 1 by things checking this above. */ if (fixed.appendstatus && !dontappend) { char progress[30]; int add; toolbars_create_progress(contents[found].b, progress); add = 1 + 1 + (fixed.usebrackets ? 2 : 0); /* (Terminating byte, plus separating space, possibly plus 2 brackets) */ /* Only proceed if there's room for the extra text */ if (strlen(new_status) + strlen(progress) + add <= sizeof(new_status)) { /* Concatenate the extra text */ strcat(new_status, " "); if (fixed.usebrackets) strcat(new_status, "("); strcat(new_status, progress); if (fixed.usebrackets) strcat(new_status, ")"); } } /* Check that the string isn't the same as already */ /* present, and if not, update the status bar. The */ /* 'url' char array used temporarily is now free, */ /* with the new status line in new_status, so we */ /* can reuse that here. */ *url = 0; ChkError(displayfield_get_value(0, t, DisplayStats, url, sizeof(url),NULL)); if ( strcmp(use_status, url) ) { /* If the string has changed, display it */ ChkError(displayfield_set_value(0, t, DisplayStats, use_status)); /* Ensure the toolbar buttons are up to date in light of the new status */ ChkError(toolbars_set_button_states(priority)); } #ifdef TRACE if (tl & (1u<<1)) Printf("toolbars_write_status: Successful, returning with message type %d\n",priority); #endif return highest; } /*************************************************/ /* toolbars_cancel_status() */ /* */ /* toolbars_remove_status_item is used to */ /* remove a browser from its ancestor's array of */ /* status_content structures because the browser */ /* is about to become invalid (e.g. be deleted */ /* because its associated window was closed). */ /* */ /* This function is used if an existing browser */ /* wants to cancel a message it registered */ /* earlier, before it times out. The message */ /* type that is being cancelled must be stated; */ /* if the browser's entry in the status_content */ /* array does not record this type, no action */ /* will be taken. Otherwise the message will be */ /* cancelled as asked. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* which wants the message */ /* cancelling; */ /* */ /* Message type that it wants to */ /* remove. */ /*************************************************/ _kernel_oserror * toolbars_cancel_status(browser_data * b, status_type type) { browser_data * ancestor; status_content * contents; int i, found; #ifdef TRACE if (tl & (1u<<1)) Printf("toolbars_cancel_status: Called for %p, type %d\n",b,type); #endif ancestor = b->ancestor; if (!ancestor) ancestor = b; contents = ancestor->status_contents; if (!contents || !ancestor->nstatus) { #ifdef TRACE if (tl & (1u<<1)) Printf("toolbars_cancel_status: Exitting - ancestor has no array\n"); #endif return NULL; } /* Find the entry */ found = -1; for (i = 0; i < ancestor->nstatus; i++) { if (contents[i].b == b) { found = i; break; } } /* If not found, exit */ if (found < 0) { #ifdef TRACE if (tl & (1u<<1)) Printf("toolbars_cancel_status: Exitting - can't find entry\n"); #endif return NULL; } /* If not the given message type, exit */ if (contents[found].type != type) { #ifdef TRACE if (tl & (1u<<1)) Printf("toolbars_cancel_status: Exitting - entry message type %d doesn't match given type %d\n", contents[found].type, type); #endif return NULL; } /* Otherwise, clear the message and restore something */ /* more meaningful with the inference routine */ contents[found].start = 0; contents[found].end = 0; contents[found].type = Toolbars_Status_NoType; #ifdef TRACE if (tl & (1u<<1)) Printf("toolbars_cancel_status: Exitting through toolbars_infer_status\n"); #endif return toolbars_infer_status(b, ancestor, found); } /*************************************************/ /* toolbars_timeout_status() */ /* */ /* A null event handler registered and deregis- */ /* tered by toolbars_update_specific_status, */ /* which handles checking the 'start' and 'end' */ /* fields of the given ancestor browser_data */ /* struct to see if any messages have expired. */ /* */ /* toolbars_update_specific_status will only */ /* allow browsers to register progressively */ /* higher priority messages than previously */ /* registered - this routine allows those */ /* previous priorities to drop down. */ /* */ /* Parameters are as standard for a Wimp null */ /* event handler (handle = pointer to the */ /* ancestor browser_data structure). */ /*************************************************/ int toolbars_timeout_status(int eventcode, WimpPollBlock * b, IdBlock * idb, browser_data * handle) { browser_data * ancestor = handle; /* Just to make things clearer */ status_content * contents = ancestor->status_contents; int i, timenow; /* Fail if there's no status_content array */ if (!contents || !ancestor->nstatus) { #ifdef TRACE erb.errnum = Utils_Error_Custom_Normal; StrNCpy0(erb.errmess, "No status_content array in toolbars_timeout_status()") show_error_ret(&erb); #endif return 0; } /* Go through all entries in the array */ for (i = 0; i < ancestor->nstatus; i++) { if (contents[i].start > 0) { /* An active message that is timing out */ _swix(OS_ReadMonotonicTime, _OUT(0), &timenow); if (timenow > contents[i].end) { /* Message has expired */ contents[i].start = 0; contents[i].end = 0; contents[i].type = Toolbars_Status_NoType; ChkError(toolbars_infer_status(contents[i].b, ancestor, i)); } } else { /* Message has <= 0 in 'start' field; if non-idle, this is an expiry indicator */ switch (contents[i].type) { case Toolbars_Status_NoType: case Toolbars_Status_Ready: case Toolbars_Status_Viewing: { /* Idle, so do nothing */ i = i; /* Ensure compiler handles this OK... */ } default: { /* Non-idle message; it has expired */ contents[i].start = 0; contents[i].end = 0; contents[i].type = Toolbars_Status_NoType; } break; } } } return 0; } /*************************************************/ /* toolbars_return_inferred() */ /* */ /* Looks at a given browser_data struct to work */ /* out what state it is in, returning this */ /* information as a status_type value (see */ /* Toolbars.h). */ /* */ /* Parameters: Pointer to the browser_data */ /* struct in question. */ /* */ /* Returns: Its status, as a status_type */ /* value. */ /*************************************************/ static status_type toolbars_return_inferred(browser_data * b) { status_type type = Toolbars_Status_Ready; /* Work out what message should be displayed. */ /* */ /* Is a page fetch in progress? */ if (fetch_fetching(b)) { /* If fetching, may in fact just be passing tokens through the fetcher */ /* with no new actual data to get from the server. In that case want */ /* to give a 'processing' message; otherwise, definitely 'fetching'. */ if (b->fetch_status == BS_PROCESS) type = Toolbars_Status_Processing; else type = Toolbars_Status_Fetching; } else { int specimg; /* If not fetching pages, are we fetching images? If so, display an */ /* appropriate message. */ specimg = image_count_specific_pending(b); if (specimg && image_fetching(b)) type = Toolbars_Status_GetPics; else { /* If not fetching images either, then may be formatting or idle. */ if (reformat_formatting(b)) type = Toolbars_Status_Formatting; else type = Toolbars_Status_Viewing; } } return type; } /*************************************************/ /* toolbars_infer_status() */ /* */ /* When a toolbar message in the status_content */ /* array of an ancestor browser has expired, */ /* this routine is called to work out what the */ /* current status for the owner browser should */ /* be. The toolbar is appropriately updated. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* which owns the message; */ /* */ /* Pointer to a browser_data struct */ /* which is the above's ancestor; */ /* */ /* Index into the ancestor's */ /* status_content array for the */ /* message owner. */ /* */ /* Assumes: That the ancestor pointer is not */ /* NULL. */ /*************************************************/ static _kernel_oserror * toolbars_infer_status(browser_data * b, browser_data * ancestor, int entry) { status_content * contents = ancestor->status_contents; status_type type; #ifdef TRACE if (tl & (1u<<1)) Printf("toolbars_infer_status: Called for %p (ancestor %p)\n",b,ancestor); #endif /* Fail if there's no status_content array */ if (!contents || !ancestor->nstatus) { #ifdef TRACE erb.errnum = Utils_Error_Custom_Normal; StrNCpy0(erb.errmess, "No status_content array in toolbars_infer_status()") show_error_ret(&erb); #endif return NULL; } /* Find the message type */ type = toolbars_return_inferred(b); /* Exit through the toolbar update routine */ #ifdef TRACE if (tl & (1u<<1)) Printf("toolbars_infer_status: Exitting through toolbars_update_specific_status with type %d\n",type); #endif return toolbars_update_specific_status(b, ancestor, entry, type); } /*************************************************/ /* toolbars_add_status_item() */ /* */ /* Adds an item to a given ancestor's array of */ /* status_content structures. The contents are */ /* not initialised - this is left to the caller. */ /* */ /* Parameters: Pointer to browser_data struct in */ /* which the addition should be made */ /* (i.e. the ancestor). */ /* */ /* Assumes: That the ancestor browser_data */ /* pointer is not NULL. */ /*************************************************/ static _kernel_oserror * toolbars_add_status_item(browser_data * ancestor) { /* Increment the status counter */ ancestor->nstatus ++; /* Allocate the required memory */ return memory_set_chunk_size(ancestor, NULL, CK_STAT, ancestor->nstatus * sizeof(status_content)); } /*************************************************/ /* toolbars_remove_status_item() */ /* */ /* Removes a browser_data structure from its */ /* ancestor's array of status_content structs. */ /* */ /* Typically called by windows_close_browser, so */ /* issues of having children within the window */ /* relating to the given browser are dealt with */ /* automatically (children would be being closed */ /* through a function in Frames.c, with the */ /* Windows.c function called as part of this). */ /* */ /* Parameters: Pointer to the browser_data */ /* struct to remove from the array; */ /* */ /* Pointer to the ancestor */ /* browser_data structure. */ /* */ /* Assumes: That the ancestor pointer is not */ /* NULL. */ /*************************************************/ _kernel_oserror * toolbars_remove_status_item(browser_data * b, browser_data * ancestor) { status_content * contents = ancestor->status_contents; int i, found; /* Can't proceed if the ancestor has no contents array */ /* (this is fine, so TRACE builds don't give an error */ /* either). */ if (!contents || !ancestor->nstatus) return NULL; /* Find the browser's entry in the status contents array */ found = -1; for (i = 0; i < ancestor->nstatus; i++) { if (contents[i].b == b) { found = i; break; } } /* If not found, exit */ if (found < 0) return NULL; /* Otherwise, remove the entry */ if (found < ancestor->nstatus - 1) { memmove(&contents[found], &contents[found + 1], (ancestor->nstatus - found - 1) * sizeof(status_content)); } ancestor->nstatus--; return memory_set_chunk_size(ancestor, NULL, CK_STAT, ancestor->nstatus * sizeof(status_content)); } /*************************************************/ /* toolbars_return_act_status() */ /* */ /* Returns the highest status for the ancestor */ /* of a given browser window that does not */ /* include non-active transient items */ /* (specifically, LinkTo and Help messages). */ /* */ /* This allows the caller to determine whether a */ /* browser and any of its frames are fetching, */ /* for example. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the ancestor in */ /* question. */ /* */ /* Returns: A status_type (see Toolbars.h) */ /* describing the ancestor's current */ /* status. */ /*************************************************/ static status_type toolbars_return_act_status(browser_data * b) { browser_data * ancestor; status_content * contents; status_type highest = Toolbars_Status_NoType; int i; if (!b) return Toolbars_Status_NoType; ancestor = b->ancestor; if (!ancestor) ancestor = b; /* Find the highest item that isn't a LinkTo or Help message */ contents = ancestor->status_contents; for (i = 0; i < ancestor->nstatus; i++) { if ( contents[i].type == Toolbars_Status_LinkTo || contents[i].type == Toolbars_Status_Help ) { status_type type; /* Non-active status entry, so need to work out what */ /* it would say if this message had timed out */ type = toolbars_return_inferred(b); if (type > highest) highest = type; } else { /* Otherwise, remember this status if its the highest */ if (contents[i].type > highest) highest = contents[i].type; } } return highest; } /*************************************************/ /* toolbars_update_progress() */ /* */ /* Reflects a fetch's progress in the browser */ /* window's status bar. */ /* */ /* Parameters: A pointer to the browser_data */ /* structure associated with the */ /* fetch. */ /*************************************************/ void toolbars_update_progress(browser_data * b) { char progress[MaxBytLen], old[MaxBytLen]; ObjectId t; _kernel_oserror * e; browser_data * ancestor = b->ancestor; if (!ancestor) ancestor = b; toolbars_create_progress(b, progress); t = toolbars_get_lower(ancestor); if (!t) return; e = displayfield_get_value(0, t, DisplayBytes, old, sizeof(old), NULL); /* Only update the display field if the contents have */ /* changed, to avoid flicker. */ if (!e && strcmp(progress,old)) displayfield_set_value(0, t, DisplayBytes, progress); else if (e && fixed.appendstatus) toolbars_update_status(b, Toolbars_Status_Ready); } /*************************************************/ /* toolbars_calculate_progress() */ /* */ /* Works out how much data has been fetched for */ /* a given browser and any children it may have. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the fetch. */ /* */ /* Returns: The amount collectively fetched, */ /* in bytes. */ /*************************************************/ static int toolbars_calculate_progress(browser_data * b) { int i, fetched = 0; if (b->nchildren) { for (i = 0; i < b->nchildren; i++) { fetched += toolbars_calculate_progress(b->children[i]); } } /* Count the image data obtained so far */ fetched += image_total_bytes_fetched(b); /* Add the current source store size */ if (b->source) fetched += flex_size((flex_ptr) &b->source); /* If saving, don't want to confuse the issue with any image */ /* fetch information or whatever. So just use the current */ /* output file size. */ // What to do about this for frames...? // // if (b->savefile) fetched = (int) (ftell(b->savefile)); b->bytes_fetched = fetched; return fetched; } /*************************************************/ /* toolbars_create_progress() */ /* */ /* Builds a string indicating fetch progress in */ /* a given buffer. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the fetch; */ /* Pointer to the buffer. */ /* */ /* Assumes: That the buffer is large enough */ /* for the string. */ /*************************************************/ static void toolbars_create_progress(browser_data * b, char * buffer) { browser_data * ancestor = b->ancestor; int fetched; if (!ancestor) ancestor = b; fetched = toolbars_calculate_progress(ancestor); /* Write the amount into the buffer; '0' for <=0 bytes, a byte */ /* amount for less than 10K, a K amount for less than 10M, else */ /* an amount in megabytes. */ if (fetched < 1) strcpy (buffer, "0"); else if (fetched < 10240) sprintf(buffer, "%d", fetched); else if (fetched < 10485760) sprintf(buffer, "%dK", (fetched + 512) / 1024); else sprintf(buffer, "%dM", (fetched + 524288) / 1048576); return; } /*************************************************/ /* toolbars_update_url() */ /* */ /* Reflects the currently fetching URL in the */ /* URL bar of a browser window. */ /* */ /* Parameters: A pointer to the browser_data */ /* structure relevant to the window. */ /*************************************************/ void toolbars_update_url(browser_data * b) { char url[MaxUrlLen]; char cmp[sizeof(url)]; ObjectId t; t = toolbars_get_upper(b); if (!t) return; if (browser_fetch_url(b)) { StrNCpy0(url, browser_fetch_url(b)); } else if (browser_current_url(b)) { StrNCpy0(url, browser_current_url(b)); } else *url = 0; /* If we can use URL aliases, then compare the text to put in */ /* the bar against the Hotlist URL string. If the same, use */ /* an alias for the hotlist instead of the given URL. */ #ifdef ALIAS_URLS { char compare[sizeof(url)]; urlutils_create_hotlist_url(compare, sizeof(compare)); if (!strcmp(url, compare)) StrNCpy0(url, lookup_token("AtHotlist:Hotlist",0,0)); } #endif #ifdef HIDE_CGI toolbars_hide_cgi(url); #endif /* For internal URLs, strip off the internal bit at the start */ toolbars_hide_internal(url); /* Write the URL in only if it's different from the existing contents */ writablefield_get_value(0, t, DisplayURL, cmp, sizeof(cmp), NULL); if (strcmp(cmp, url)) writablefield_set_value(0, t, DisplayURL, url); } /*************************************************/ /* toolbars_update_dialler_time() */ /* */ /* Updates the dialler status display in the */ /* upper toolbar. Should only be called if there */ /* is a Display Field component, ID 0xF, which */ /* can take the required display values. */ /* */ /* This shows online time. It is expected that */ /* the function will only be called from a null */ /* event handler to update the time regularly. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the display. */ /*************************************************/ _kernel_oserror * toolbars_update_dialler_time(browser_data * b) { _kernel_oserror * e; ObjectId t; int status, start_time, time_now; char display[MaxDiaLen]; char compare[sizeof(display)]; t = toolbars_get_upper(b); if (!t) return NULL; e = _swix(Dialler_Status, _IN(0) | _OUTR(0,1), Dialler_Status_ConnectTime, &status, &start_time); /* If the dialler isn't present, don't want to end up */ /* with a recursive error report, so fail silently. */ if (e) return NULL; _swix(OS_ReadMonotonicTime, _OUT(0), &time_now); /* Only update as often as specified in the Messages file */ if (time_now - b->dialler_last > fixed.show_dstat_for) { /* If connected, must display an online time, else show offline. */ if (status & Dialler_Connected) { int hours, minutes, seconds; /* Remember when this was done */ b->dialler_last = time_now; /* Work out how long we've been online */ time_now -= start_time; if (time_now < 0) time_now = 0; hours = time_now / 360000; time_now -= (hours * 360000); minutes = time_now / 6000; time_now -= (minutes * 6000); seconds = (time_now / (fixed.quantise * 100)) * fixed.quantise; if (hours > 99) hours = 99; sprintf(display, "%02u:%02u:%02u\0", hours, minutes, seconds); /* Get the current string into 'compare' */ e = displayfield_get_value(0, t, DiallerStatus, compare, sizeof(compare), NULL); if (e) return e; /* Update the display field if necessary, else exit */ if (strcmp(display, compare)) { e = displayfield_set_value(0, t, DiallerStatus, display); if (e) return e; } } } return NULL; } /*************************************************/ /* toolbars_update_dialler_status() */ /* */ /* Updates the dialler status display in the */ /* upper toolbar. Should only be called if there */ /* is a Display Field component, ID 0xF, which */ /* can take the required display values. */ /* */ /* If the status is 'online', a null event */ /* handler will be installed to call */ /* toolbars_update_dialler_time to show the */ /* online time. If the status is not 'online', */ /* that handler will be deregistered if present. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the display. */ /*************************************************/ _kernel_oserror * toolbars_update_dialler_status(browser_data * b) { _kernel_oserror * e; ObjectId t; int status; int showing_time = 0; char display[MaxDiaLen]; char statstr[MaxDiaLen]; char compare[sizeof(display)]; t = toolbars_get_upper(b); if (!t) return NULL; memset(statstr, 0, sizeof(statstr)); e = _swix(Dialler_Status, _INR(0,2) | _OUT(0), Dialler_Status_StatusString, statstr, sizeof(statstr), &status); /* If the dialler isn't present, don't want to end up */ /* with a recursive error report, so fail silently. */ if (e) return NULL; statstr[sizeof(statstr) - 1] = 0; if (status & Dialler_StatusChanged) { /* Use the status string. Put the text in the 'tokens' messages buffer */ /* as this is used lower down for the actual dialler status display */ /* string's source. To ensure that any future lookup_token calls don't */ /* look at the 'lasttokn' buffer and think they don't need to do any */ /* work, must mark the contents of 'tokens' as invalid by setting */ /* the first char of 'lasttokn' to 0. */ *lasttokn = 0; StrNCpy0(tokens, statstr); } else { int info = status & Dialler_GeneralInfoMask; /* Get the relevant status text in the 'tokens' messages buffer. */ /* If an item is missing from the Messages file, the lookup will */ /* fail silently leaving '!' in the tokens buffer. This is taken */ /* to mean 'don't show this status', and the routine will exit. */ switch (info) { case Dialler_ExecutingScript_Dial: lookup_token("Dialling", 0, 0); break; /* Executing dialling script */ case Dialler_ExecutingScript_Hangup: lookup_token("Hangup", 0, 0); break; /* Executing hangup script */ case Dialler_ExecutingScript_Answer: lookup_token("Answering", 0, 0); break; /* Answering */ case Dialler_AbortedScript_Syntax: lookup_token("SError", 0, 0); break; /* Script syntax error */ case Dialler_AbortedScript_Timeout: lookup_token("Timeout", 0, 0); break; /* Timed out */ case Dialler_AbortedScript_NoCarrier: lookup_token("Carrier", 0, 0); break; /* No carrier */ case Dialler_AbortedScript_Error: lookup_token("MError", 0, 0); break; /* 'ERROR' from modem */ case Dialler_AbortedScript_NoDialtone: lookup_token("Dialtone", 0, 0); break; /* No dialtone */ case Dialler_AbortedScript_Busy: lookup_token("MBusy", 0, 0); break; /* 'BUSY' from modem */ case Dialler_AbortedScript_NoAnswer: lookup_token("Answer", 0, 0); break; /* No answer */ /* If there's no specific special status, are we online? */ default: { if (status & Dialler_Connected) showing_time = 1; else lookup_token("Offline", 0, 0); } break; } } if (showing_time) { /* If we want to show online time, install the handler to do so */ /* (if it isn't already installed). */ if (!b->dialler_time) { b->dialler_time = 1; register_null_claimant(Wimp_ENull, (WimpEventHandler *) handle_dialler_display, b); } } else { /* Otherwise, remove the handler if it is installed */ if (b->dialler_time) deregister_null_claimant(Wimp_ENull, (WimpEventHandler *) handle_dialler_display, b); /* Copy the token contents to the display buffer and update the field */ /* if the value has changed. */ StrNCpy0(display, tokens); e = displayfield_get_value(0, t, DiallerStatus, compare, sizeof(compare), NULL); if (e) return e; /* Update the display field if necessary, else exit */ if (strcmp(display, compare)) { e = displayfield_set_value(0, t, DiallerStatus, display); if (e) return e; } } /* May need to alter the label on this display, if there is one */ { char label[DiaLabLen]; if (button_get_value(0, t, DiallerLabel, label, sizeof(label), NULL)) return NULL; if (!showing_time) lookup_token("DiaStatusDial:Dialler",0,0); else lookup_token("DiaStatusTime:Time" ,0,0); if (strcmp(label, tokens)) { StrNCpy0(label, tokens); return button_set_value(0, t, DiallerLabel, label); } } return NULL; } /*************************************************/ /* toolbars_merged_to_status() */ /* */ /* If the URL writable and status lines are */ /* merged, this ensures that the combined field */ /* is put into 'status' display. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the field; */ /* Object ID of the toolbar the */ /* gadgets are in. */ /* */ /* Assumes: That the fields are indeed merged */ /* though a null object ID can be */ /* given. */ /*************************************************/ void toolbars_merged_to_status(browser_data * b, ObjectId t) { if (t) { char label[StaLabLen]; show_gadget(t, DisplayStats); hide_gadget(t, DisplayURL); /* Update the label, if present */ if (!button_get_value(0, t, StatusLabel, label, sizeof(label), NULL)) { lookup_token("DisplayStatus:Status",0,0); if (strcmp(label, tokens)) { StrNCpy0(label, tokens); button_set_value(0, t, StatusLabel, label); } } } } /*************************************************/ /* toolbars_merged_to_url() */ /* */ /* If the URL writable and status lines are */ /* merged, this ensures that the combined field */ /* is put into 'URL entry' display. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the field; */ /* Object ID of the toolbar the */ /* gadgets are in. */ /* */ /* Assumes: That the fields are indeed merged */ /* though a null object ID can be */ /* given. */ /*************************************************/ void toolbars_merged_to_url(browser_data * b, ObjectId t) { if (t) { char label[StaLabLen]; show_gadget(t, DisplayURL); hide_gadget(t, DisplayStats); /* Update the label, if present */ if (!button_get_value(0, t, StatusLabel, label, sizeof(label), NULL)) { lookup_token("DisplayURL:URL",0,0); if (strcmp(label, tokens)) { StrNCpy0(label, tokens); button_set_value(0, t, StatusLabel, label); } } } } /*************************************************/ /* toolbars_set_bistate_state() */ /* */ /* Sets a bistate button to a given state, which */ /* depends on the type of button in use. */ /* */ /* Naming conventions on the button types should */ /* dictate which state is which, with the word */ /* order of the name relating to the states (so */ /* for example, BiState_Cancel_Back would be at */ /* Cancel for state 0, and Back for state 1). */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the button; */ /* Object ID of the toolbar the */ /* button is in; */ /* State (0 or 1) to set it to. */ /*************************************************/ void toolbars_set_bistate_state(browser_data * b, ObjectId t, int state) { switch (b->bistate) { case BiState_Cancel_Back: { ObjectId source; /* State 0 = Cancel, state 1 = Back */ source = state ? ButtonBack : ButtonCancel; /* Copy the characteristics of the gadget over. */ ChkError(copy_toolaction_info(t, source, t, ButtonBi)); b->bistate_state = state; } break; } } /*************************************************/ /* toolbars_set_tristate_state() */ /* */ /* Sets a tristate button to a given state, */ /* which depends on the type of button in use. */ /* */ /* Naming conventions on the button types should */ /* dictate which state is which, with the word */ /* order of the name relating to the states (so */ /* for example, TriState_Go_GoTo_Stop would be */ /* at Go for state 0, GoTo for state 1, and Stop */ /* for state 2). */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the button; */ /* Object ID of the toolbar the */ /* button is in; */ /* State (0, 1 or 2) to set it to. */ /*************************************************/ void toolbars_set_tristate_state(browser_data * b, ObjectId t, int state) { ObjectId source; switch (b->tristate) { case TriState_Go_GoTo_Stop: { /* State 0 = Go, 1 = GoTo, 2 = Stop */ if (!state) source = ButtonGo; else if (state == 1) source = ButtonGoTo; else source = ButtonStop; /* Copy the characteristics of the gadget over. */ ChkError(copy_toolaction_info(t, source, t, ButtonTri)); b->tristate_state = state; } break; } } /*************************************************/ /* toolbars_button_height() */ /* */ /* Returns the button bar height for a window, */ /* or 0 if there is no bar present. If there is */ /* both a button bar and URL bar present the */ /* combined height is less than the height of */ /* both individual bars summed, due to an extra */ /* gap at the edges for aesthetics. This */ /* function does not attempt to compensate for */ /* this (see toolbars_url_height(), which does). */ /* */ /* Parameters: Pointer to a browser_data struct */ /* which holds the toolbars of */ /* interest. */ /*************************************************/ int toolbars_button_height(browser_data * b) { if (b->button_bar) { ObjectId t; BBox w; if (b->all_in_bottom) return 0; t = toolbars_get_upper(b); if (!t) return 0; if (!gadget_get_bbox(0,t,ButtonSpacer,&w)) return (w.ymax - w.ymin); } return 0; } /*************************************************/ /* toolbars_url_height() */ /* */ /* Returns the URL bar height for a window, or 0 */ /* if there is no URL bar present. If there is */ /* both a button bar and a URL bar present the */ /* returned value will be reduced to account for */ /* the effective overlap of the two bars, as */ /* when each is present individually there is a */ /* gap at the edges for aesthetics. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* which holds the toolbars of */ /* interest. */ /*************************************************/ int toolbars_url_height(browser_data * b) { if (b->url_bar) { ObjectId t; BBox w; if (b->all_in_bottom) return 0; t = toolbars_get_upper(b); if (!t) return 0; if (!gadget_get_bbox(0,t,URLSpacer,&w)) { if (b->button_bar) return (w.ymax - w.ymin - toolbars_bar_overlap(t)); else return (w.ymax - w.ymin); } } return 0; } /*************************************************/ /* toolbars_status_height() */ /* */ /* Returns the status bar height for a window, */ /* or 0 if there is no status bar present. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* which holds the toolbars of */ /* interest. */ /*************************************************/ int toolbars_status_height(browser_data * b) { if (b->status_bar) { ObjectId t; BBox w; if (b->all_in_top) return 0; t = toolbars_get_lower(b); if (!t) return 0; if (!gadget_get_bbox(0,t,StatusSpacer,&w)) return (w.ymax - w.ymin); } return 0; } /*************************************************/ /* toolbars_set_button_states() */ /* */ /* Sets the button states in the button bar and */ /* URL bar of a given browser window, greying or */ /* ungreying them according to the browser's */ /* current state. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the browser window. */ /*************************************************/ _kernel_oserror * toolbars_set_button_states(browser_data * b) { _kernel_oserror * e; ObjectId t; t = toolbars_get_upper(b); if (!t) return NULL; if (!fixed.dontgrey) { // /* Can only stop if there's activity of some sort */ // // if ( // !fetch_fetching(b) && // !reformat_formatting(b) && // !image_fetching(b) // ) // // e = set_gadget_state(t, ButtonStop,1); // // else e = set_gadget_state(t, ButtonStop, 0); // // if (e) return e; /* Can only go back if we're not at the start of the history */ /* and there's a history to go into. */ if (!b->hnum || b->hpos == 1) e = set_gadget_state(t, ButtonBack, 1); else e = set_gadget_state(t, ButtonBack, 0); if (e) return e; /* Can only go forward if we're in the history somewhere */ if (!b->hpos) e = set_gadget_state(t, ButtonForward, 1); else e = set_gadget_state(t, ButtonForward, 0); if (e) return e; /* Can only view source if there's source to view */ if (!b->source) e = set_gadget_state(t, ButtonViewSrc, 1); else e = set_gadget_state(t, ButtonViewSrc, 0); if (e) return e; /* Only need to load images if delayed image loading is set */ /* in the local browser flags. */ if (b->displayimages || b->displayed == Display_External_Image) e = set_gadget_state(t, ButtonLoadImg, 1); else e = set_gadget_state(t, ButtonLoadImg, 0); if (e) return e; } if (fixed.dontgrey != 2) { /* Can only get a history menu if there's a history present. */ if (!b->hnum) e = set_gadget_state(t, DisplayMenu, 1); else e = set_gadget_state(t, DisplayMenu, 0); if (e) return e; } /* Handle bistate buttons */ if (b->bistate) { switch (b->bistate) { case BiState_Cancel_Back: { /* If the URL writable and status display are merged, */ /* and the URL display is visible, then the field is */ /* in writable mode and the bistate should be at */ /* 'cancel'. Otherwise, it should be at 'back'. */ if (b->merged_url && !gadget_hidden(t, DisplayURL)) toolbars_set_bistate_state(b, t, 0); else toolbars_set_bistate_state(b, t, 1); } break; } } /* Handle tristate buttons */ if (b->tristate) { switch (b->tristate) { case TriState_Go_GoTo_Stop: { /* If the URL writable and status display are merged, */ /* and the status display is hidden, then the field */ /* is in writable mode and the tristate should be at */ /* 'go'. */ if (b->merged_url && !gadget_hidden(t, DisplayURL)) toolbars_set_tristate_state(b, t, 0); else { status_type type = toolbars_return_act_status(b); switch (type) { default: case Toolbars_Status_NoType: case Toolbars_Status_Ready: case Toolbars_Status_Viewing: { toolbars_set_tristate_state(b, t, 1); /* Go To */ } break; case Toolbars_Status_Formatting: case Toolbars_Status_Processing: case Toolbars_Status_GetPics: case Toolbars_Status_Fetching: { toolbars_set_tristate_state(b, t, 2); /* Stop */ } break; } } } break; } } return NULL; } /*************************************************/ /* toolbars_set_all_button_states() */ /* */ /* Sets all the button states in the toolbars of */ /* all the browser windows currently open; that */ /* is, it greys and ungreys them as appropriate */ /* for that browser's current state. */ /*************************************************/ _kernel_oserror * toolbars_set_all_button_states(void) { _kernel_oserror * e; browser_data * b; b = last_browser; while (b) { e = toolbars_set_button_states(b); if (e) return e; b = b->previous; } return NULL; } /*************************************************/