/* 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 "Utils.h" #include "Browser.h" #include "ChoiceDefs.h" #include "CSIM.h" #include "CtrlDefs.h" #include "Fetch.h" /* (Which itself includes URLstat.h) */ #include "Handlers.h" #include "History.h" #include "Images.h" #include "Memory.h" #include "Mouse.h" #include "Reformat.h" #include "URLutils.h" #include "URLveneer.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 ObjectId toolbars_get_upper_backend (browser_data * b); static ObjectId toolbars_get_lower_backend (browser_data * b); 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_animation_set_sprite (browser_data * b); 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_count_file_saves (browser_data * b); static int toolbars_count_file_saves_r (browser_data * b); static int toolbars_calculate_progress (browser_data * b); static int toolbars_calculate_progress_r (browser_data * b, int saves); static int toolbars_create_progress (browser_data * b, char * buffer, int buffer_size); /*************************************************/ /* toolbars_get_upper() */ /* */ /* Returns the object ID of the upper toolbar. */ /* This may well not be the internal top left */ /* toolbar of the window due to the swap_bars */ /* flag or merged toolbars. */ /* */ /* 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) { if (controls.swap_bars) return toolbars_get_lower_backend(b); else return toolbars_get_upper_backend(b); } /*************************************************/ /* toolbars_get_upper_backend() */ /* */ /* Returns the object ID of the upper toolbar. */ /* This may well not be the internal top left */ /* toolbar of the window due to merged toolbars. */ /* */ /* 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. */ /*************************************************/ static ObjectId toolbars_get_upper_backend(browser_data * b) { ObjectId t; if (b->all_in_bottom) { 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 due to the */ /* swap_bars flag or merged toolbars. */ /* */ /* 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) { if (controls.swap_bars) return toolbars_get_upper_backend(b); else return toolbars_get_lower_backend(b); } /*************************************************/ /* toolbars_get_lower_backend() */ /* */ /* Returns the object ID of the lower toolbar. */ /* This may well not be the internal bottom */ /* left toolbar of the window due to merged */ /* toolbars. */ /* */ /* 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. */ /*************************************************/ static ObjectId toolbars_get_lower_backend(browser_data * b) { ObjectId t; if (b->all_in_top) { 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 /* This has to be here, or the toolbar can drop out of */ /* the parent window (?!)... */ 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. */ if (!controls.swap_bars) { s.visible_area.ymin = s.visible_area.ymax - height; ChkError(toolbox_show_object(0, o, 1, &s.visible_area, p, -1)); } else { s.visible_area.ymax = s.visible_area.ymin + 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 */ ChkError(window_set_tool_bars(flags, p, o, o, 0, 0)); /* Set the scroll position - the set_tool_bars call will */ /* trounce the scroll position (sigh) if this call is */ /* done beforehand, as part of the first show_object */ /* call. */ 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, URLBarSpacer, &url)) return 0; if (gadget_get_bbox(0, o, ButtonBarSpacer, &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: A browser_data struct relevant to */ /* the window holding the toolbars; */ /* */ /* A flags word holding the toolbars */ /* to alter - InternalTopLeft, or */ /* InternalBottomLeft. */ /*************************************************/ void toolbars_set_presence(browser_data * b, unsigned int flags) { ObjectId o; ObjectId t; #ifdef TRACE if (tl & (1u<<1)) Printf("\ntoolbars_set_presence: Called with %p\n", b); #endif /* 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. */ b = utils_ancestor(b); o = b->self_id; /* Deal with merged toolbars separately - they're a simple case */ if (b->all_in_top || b->all_in_bottom) { /* The URL bar flag is looked at to see if the toolbar is on or off */ /* as well as the status bar flag, as the former is used for upper */ /* toolbars (as it prompts the vertical shift of page objects), and */ /* the latter for bottom merged toolbars. */ if (b->all_in_top) { 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 if (b->url_bar && b->status_bar) ChkError(toolbox_show_object(0, t, 0, NULL, o, -1)); else ChkError(toolbox_hide_object(0, t)); } } else { 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 if (b->url_bar && b->status_bar) ChkError(toolbox_show_object(0, t, 0, NULL, o, -1)); else ChkError(toolbox_hide_object(0, t)); } } return; } /* Proceed with 'normal' toolbars */ 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, controls.swap_bars ? InternalBottomLeft : InternalTopLeft); /* URL bar only present */ else if ((b->url_bar) && !(b->button_bar)) toolbars_set_size(t, toolbars_url_height(b), 0, controls.swap_bars ? InternalBottomLeft : 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, URLBarSpacer, &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), controls.swap_bars ? InternalBottomLeft : 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 != Choices_MoveGadgets_Never) 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)); // coords_box_toworkarea(&s.visible_area, (WimpRedrawWindowBlock *) &s); // ChkError(wimp_force_redraw(b->window_handle, // 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 /* Small fetch windows can't move their status line contents */ if (p->small_fetch) return; /* Otherwise, continue as normal */ s.window_handle = p->window_handle; ChkError(wimp_get_window_state(&s)); b = s.visible_area; if ((b.xmax - b.xmin) < controls.minimum_convergence) b.xmax = b.xmin + controls.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, URLBarHistoryMenuR, &g1); /* Get the popup's bounding box */ ChkError(gadget_get_bbox(0, t, URLBarWrit, &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, URLBarHistoryMenuR, &g1)); } /* Resize the URL writable */ g2.xmax = g1.xmin - gap; ChkError(gadget_move_gadget(0, t, URLBarWrit, &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, StatusBarProgress, &g1)); ChkError(gadget_get_bbox(0, t, StatusBarStatus, &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, StatusBarProgress, &g1)); g2.xmax = g1.xmin - gap; ChkError(gadget_move_gadget(0, t, StatusBarStatus, &g2)); } } } #ifdef TRACE if (tl & (1u<<1)) Printf("toolbars_move_gadgets: Successful\n"); #endif } /*************************************************/ /* toolbars_animation_set_sprite() */ /* */ /* Sets the sprite in the button gadget used for */ /* the status bar animation. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the animation. */ /*************************************************/ static _kernel_oserror * toolbars_animation_set_sprite(browser_data * b) { ObjectId t; char v[32]; int time_now; /* Update the current frame time */ if ( _swix(OS_ReadMonotonicTime, _OUT(0), &time_now) ) b->current_time = 0; else b->current_time = time_now; /* Find the status bar object Id */ t = toolbars_get_lower(b); 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. */ b->current_frame ++; if (b->current_frame >= animation_frames) b->current_frame = 0; sprintf(v, "sa%d\0", b->current_frame); /* Set the validation string on the status bar animation */ /* button icon to v, so that the new sprite is shown. */ return button_set_validation(0, t, StatusBarAnimAnim, v); } /*************************************************/ /* 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) { int time_now; /* What is the time now? */ if ( _swix(OS_ReadMonotonicTime, _OUT(0), &time_now) ) return 0; /* If we have animation frames, a displayed status bar, and */ /* enough time has passed, advance the animation. */ if ( handle->status_bar && animation_frames > 0 && time_now > (handle->current_time + controls.anim_delay) ) ChkError(toolbars_animation_set_sprite(handle)); 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) { int time_now; /* What is the time now? */ if ( _swix(OS_ReadMonotonicTime, _OUT(0), &time_now) ) return 0; if ( handle->status_bar && animation_frames > 0 && time_now > (handle->current_time + controls.anim_delay) ) { ChkError(toolbars_animation_set_sprite(handle)); /* 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 */ /* StatusBarAnimAnim (see Toolbars.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[32]; int slow_animation_frame; /* Because client handles can only store one thing, in this case */ /* the animation frame, we have no easy way of storing the time */ /* that the animation was last advanced by a frame. So, make the */ /* assumption of a 1cs poll time, and use a strategy of integer */ /* division of frame count to get the actual frame number. The */ /* timing is thus not at all independent of machine load, but */ /* the dialogue box animations are a very low priority issue. */ 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 * controls.anim_delay) slow_animation_frame = 0; sprintf(v, "sa%d\0", slow_animation_frame / (animation_frames * controls.anim_delay)); /* 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, StatusBarAnimAnim, 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 = utils_ancestor(b); 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 /* 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: case Toolbars_Status_Connected: case Toolbars_Status_SentReq: case Toolbars_Status_Responded: case Toolbars_Status_Redirected: case Toolbars_Status_Connecting: { /* For some messages, a general timeout */ timeout = atoi(lookup_control("ShowMiscFor:50",0,0)); } break; case Toolbars_Status_PlugIn: { /* For Plug-In messages, use the Plug-in status timeout */ timeout = atoi(lookup_control("ShowPlugInFor:150",0,0)); } break; case Toolbars_Status_LinkTo: { /* Specific timeout for LinkTo messages */ timeout = atoi(lookup_control("ShowLinksFor:200",0,0)); } break; case Toolbars_Status_Help: { /* Specific timeout for Help messages */ timeout = atoi(lookup_control("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 [Limits_StatusBarStatus]; char url [Limits_StatusBarStatus]; char * use_status = 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[Limits_Title]; *title = 0; /* 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[Limits_StatusFormat]; title[sizeof(title) - 1] = 0; 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); } /* Append an 'active' suffix if needed */ if (priority->anim_handler || priority->plugin_active) { char * active = lookup_token("Actv: (active)",0,0); if (strlen(active) + strlen(new_status) + 1 < sizeof(new_status)) strcat(new_status, active); } /* If preceeded by a '-' don't ever append a progress counter */ 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_Connected: case Toolbars_Status_SentReq: case Toolbars_Status_Responded: case Toolbars_Status_Redirected: case Toolbars_Status_Connecting: case Toolbars_Status_Formatting: { char format[Limits_StatusFormat]; /* 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) { /* This one is a little special, as we could be saving an object to disc */ /* and the user may not have dealt with the save dialogue yet. */ if (priority->fetch_status == BS_DATAFETCH && !priority->save_file) { StrNCpy0(new_status, lookup_token("FetchWait:Waiting for a Save dialogue to be dealt with...", 0, 0)) } else { StrNCpy0(new_status, lookup_token("FetchMany:Fetching frame contents...", 0, 0)) } } /* Simpler cases */ else if (highest == Toolbars_Status_Processing) StrNCpy0(new_status, lookup_token("ProcessMany:Processing frames contents...", 0, 0)) else if (highest == Toolbars_Status_Connected) StrNCpy0(new_status, lookup_token("ConnectedMany:Connected to servers to fetch frames contents...", 0, 0)) else if (highest == Toolbars_Status_SentReq) StrNCpy0(new_status, lookup_token("SentReqMany:Sent requests to fetch frames contents...", 0, 0)) else if (highest == Toolbars_Status_Responded) StrNCpy0(new_status, lookup_token("RespondedMany:Responses received for frames contents...", 0, 0)) else if (highest == Toolbars_Status_Connecting) StrNCpy0(new_status, lookup_token("ConnectingMany:Trying to connect to fetch frames contents...", 0, 0)) else if (highest == Toolbars_Status_Redirected) StrNCpy0(new_status, lookup_token("RedirectedMany:Frame fetches are being redirected...", 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) { if (priority->fetch_status == BS_DATAFETCH && !priority->save_file) { StrNCpy0(format, lookup_token("FetchWait:Waiting for a Save dialogue to be dealt with...", 0, 0)) } else { 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 if (highest == Toolbars_Status_Connected) StrNCpy0(format, lookup_token("Connected:Connected to server to fetch '%s'...", 0, 0)) else if (highest == Toolbars_Status_SentReq) StrNCpy0(format, lookup_token("SentReq:Sent request to fetch '%s'...", 0, 0)) else if (highest == Toolbars_Status_Responded) StrNCpy0(format, lookup_token("RespondedMany:Response received for '%s'...", 0, 0)) else if (highest == Toolbars_Status_Connecting) StrNCpy0(format, lookup_token("ConnectingMany:Trying to connect to fetch '%s'...", 0, 0)) else if (highest == Toolbars_Status_Redirected) StrNCpy0(format, lookup_token("RedirectedMany:Redirecting to '%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) { if (priority->fetch_status == BS_DATAFETCH && !priority->save_file) { StrNCpy0(new_status, lookup_token("FetchWait:Waiting for a Save dialogue to be dealt with...", 0, 0)) } else { 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 if (highest == Toolbars_Status_Connected) StrNCpy0(new_status, lookup_token("ConnectedUK:Connected to server...", 0, 0)) else if (highest == Toolbars_Status_SentReq) StrNCpy0(new_status, lookup_token("SentReqUK:Sent request for web page...", 0, 0)) else if (highest == Toolbars_Status_Responded) StrNCpy0(new_status, lookup_token("RespondedUK:Server response received...", 0, 0)) else if (highest == Toolbars_Status_Connecting) StrNCpy0(new_status, lookup_token("ConnectingUK:Trying to connect to server...", 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_Help: { StrNCpy0(new_status, priority->status_help); dontappend = 1; } break; case Toolbars_Status_LinkTo: { HStream * over; char format[Limits_StatusFormat]; int allowed; int dealt_with = 0; /* For comments on the use of the 'format' buffer, see the */ /* code above. */ over = priority->pointer_over; /* If the token that the pointer is over isn't suitable for a LinkTo */ /* message, use the keyboard selected item instead; so the pointer */ /* always takes priority over the keyboard. */ if ( !over || ( ( !over->anchor || !*over->anchor ) && !(over->type & (TYPE_ISMAP | TYPE_ISCLIENTMAP)) ) ) over = ancestor->selected; /* So, do we now have a suitable token, be it from the token the */ /* pointer was over or whatever is keyboard selected? */ if ( over && ( ( over->anchor && *over->anchor ) || (over->type & (TYPE_ISMAP | TYPE_ISCLIENTMAP)) ) ) { /* Yes, so create an appropriate status message. */ StrNCpy0(format, lookup_token("LinkTo:Link to '%s'...",0,0)); /* If a client-side image map, must work out the URL */ if (over->type & TYPE_ISCLIENTMAP) { char * csim_url; csim_return_info(priority, over, priority->map_x, priority->map_y, &csim_url, NULL, NULL); if (csim_url && *csim_url) { dealt_with = 1; StrNCpy0(url, csim_url) } } /* If a server side map and either not client side as well, */ /* or the client side map gave us no URL, use the anchor */ /* instead, and append coordinate information. */ /* */ /* If not server side, just use the anchor information in */ /* the token on its own. */ if (!dealt_with && over->anchor && *over->anchor) { StrNCpy0(url, over->anchor); #ifdef HIDE_CGI toolbars_hide_cgi(url); #endif allowed = sizeof(new_status) - strlen(format); url[allowed - 1] = 0; toolbars_hide_internal(url); /* If a server-side map, want to append coordinate info. */ if (over->type & TYPE_ISMAP) { int required = utils_number_length(priority->map_x) + utils_number_length(priority->map_y) + 5; /* 5 = length of " (, )" */ if (strlen(url) + required + 1 <= allowed) { char append[128]; sprintf(append, " (%d, %d)", priority->map_x, priority->map_y); strcat(url, append); } } dealt_with = 1; } } /* Did we end up with something we can use in a LinkTo message? */ if (dealt_with) { /* Yes, so compile the final message together now */ sprintf(new_status, format, url); if (new_status[0] == '-') use_status++, dontappend = 1; } else { /* No suitable token, so the message is out of date; that */ /* is, no object is selected (say) but the message is */ /* still on a timer. In that case, cancel it. */ toolbars_cancel_status(priority, highest); return Toolbars_Status_Ready; } } break; case Toolbars_Status_PlugIn: { StrNCpy0(new_status, priority->plugin_status ? priority->plugin_status : ""); dontappend = 1; } 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 (controls.append_status && !dontappend) { char progress[Limits_FetchProgress]; int add; toolbars_create_progress(contents[found].b, progress, sizeof(progress)); add = 1 + 1 + (controls.use_brackets ? 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 (controls.use_brackets) strcat(new_status, "("); strcat(new_status, progress); if (controls.use_brackets) 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. */ ChkError(displayfield_get_value(0, t, StatusBarStatus, url, sizeof(url),NULL)); if ( strcmp(use_status, url) ) { /* If the string has changed, display it */ ChkError(displayfield_set_value(0, t, StatusBarStatus, 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 = utils_ancestor(b); 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 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. To do */ /* this, we'll need to free any Plug-In message, if */ /* there was one. */ if (type == Toolbars_Status_PlugIn) { free(b->plugin_status); b->plugin_status = 0; } 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_cancel_all() */ /* */ /* If a browser wants to cancel all messages it */ /* registered earlier, except for LinkTo or Help */ /* messages, perhaps to ensure that some message */ /* is displayed now rather than after another */ /* times out, it should call this function. The */ /* original idea was for browsers that have just */ /* completed a full fetch, which want to get rid */ /* of any timing out formatting or fetching */ /* messages. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* which wants the message */ /* cancelling. */ /*************************************************/ _kernel_oserror * toolbars_cancel_all(browser_data * b) { #ifdef TRACE if (tl & (1u<<1)) Printf("toolbars_cancel_all: Called\n"); #endif RetError(toolbars_cancel_status(b, Toolbars_Status_PlugIn)); RetError(toolbars_cancel_status(b, Toolbars_Status_Connecting)); RetError(toolbars_cancel_status(b, Toolbars_Status_Redirected)); RetError(toolbars_cancel_status(b, Toolbars_Status_Responded)); RetError(toolbars_cancel_status(b, Toolbars_Status_SentReq)); RetError(toolbars_cancel_status(b, Toolbars_Status_Connected)); RetError(toolbars_cancel_status(b, Toolbars_Status_Fetching)); RetError(toolbars_cancel_status(b, Toolbars_Status_GetPics)); RetError(toolbars_cancel_status(b, Toolbars_Status_Processing)); RetError(toolbars_cancel_status(b, Toolbars_Status_Formatting)); #ifdef TRACE if (tl & (1u<<1)) Printf("toolbars_cancel_all: Successful\n"); #endif return NULL; } /*************************************************/ /* 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 (contents[i].start != contents[i].end && timenow > contents[i].end) { /* Message has expired. Was it a Plug-In status message? */ if (contents[i].type == Toolbars_Status_PlugIn) { free(ancestor->plugin_status); ancestor->plugin_status = NULL; } /* OK, set the status to 'nothing', then infer the current state */ 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. Was it a Plug-In status message? */ if (contents[i].type == Toolbars_Status_PlugIn) { free(ancestor->plugin_status); ancestor->plugin_status = NULL; } /* OK, set the status to 'nothing' */ 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. */ /* Since this is most often called when a */ /* status message expires, any messages which */ /* should appear persistently in the status */ /* bar - such as Fetching or Viewing - should */ /* be returned by this function. Otherwise, */ /* the message will be replaced by another */ /* that this function chooses. */ /* Is there a Plug-In status message? */ if (b->plugin_status && *b->plugin_status) { type = Toolbars_Status_PlugIn; } /* Otherwise, is a page fetch in progress? */ else { 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 { /* Need to find out the genuine fetch status, or all of the */ /* varied fetch information that we can get out of the */ /* fetchers gets collapsed to some less meaningful generic */ /* message by this routine. */ int s; if (!url_status(0, b->fetch_handle, &s, NULL, NULL)) { if (s & URL_Status_Transfer) type = Toolbars_Status_Fetching; else if (s & URL_Status_Responded) type = Toolbars_Status_Responded; else if (s & URL_Status_SentReq) type = Toolbars_Status_SentReq; else if (s & URL_Status_SentData) type = Toolbars_Status_SentReq; else if (s & URL_Status_Connected) type = Toolbars_Status_Connected; else if (!s) type = Toolbars_Status_Connecting; } 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 = utils_ancestor(b); status_type highest = Toolbars_Status_NoType; status_content * contents; int i; if (!b) return Toolbars_Status_NoType; /* 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 [Limits_StatusBarProgress]; char old [Limits_StatusBarProgress]; ObjectId t; _kernel_oserror * e; browser_data * ancestor = utils_ancestor(b); int saves; /* May want to only update at specific intervals of time */ if ( controls.progress_update_delay && ! ( b->fetch_status == BS_IDLE || b->fetch_status == BS_PROCESS ) ) { int time_now; if ( !_swix(OS_ReadMonotonicTime, _OUT(0), &time_now) ) { if (time_now - ancestor->progress_updated < controls.progress_update_delay) return; ancestor->progress_updated = time_now; } } /* Find out how many file saves, if any, are in progress */ saves = toolbars_create_progress(b, progress, sizeof(progress)); /* Find the toolbar and try to get the old progress string */ t = toolbars_get_lower(ancestor); if (!t) return; e = button_get_value(0, t, StatusBarProgress, old, sizeof(old), NULL); /* Only update the display field if the contents have */ /* changed, to avoid flicker. */ if (!e && strcmp(progress, old)) { int flags; int colour_old, colour_new; /* Set the colour if required */ if (controls.colour_progress != Controls_ColourProgress_NotAColour) { if (!button_get_flags(0, t, StatusBarProgress, &flags)) { browser_data * ancestor = b->ancestor; if (!ancestor) ancestor = b; /* Remember the old colour, and set the new according */ /* to whether or not file saves are in progress. If */ /* there, use the Messages file defined colour; else */ /* use the colour recorded in the browser_data struct. */ colour_old = (flags & 0x0f000000) >> 24; colour_new = saves ? controls.colour_progress : ancestor->progress_colour; if (colour_new != colour_old) { button_set_flags(0, t, StatusBarProgress, 0x0f000000, colour_new << 24); } } } button_set_value(0, t, StatusBarProgress, progress); } else if (e && controls.append_status) toolbars_update_status(b, Toolbars_Status_Ready); } /*************************************************/ /* toolbars_count_file_saves() */ /* */ /* Checks to see if any frames in a given */ /* frameset are saving out files. */ /* */ /* Parameters: Pointer to any browser_data */ /* structure in the frameset. */ /* */ /* Returns: The number of file saves going on */ /* in the parent and any children it */ /* might have. */ /*************************************************/ static int toolbars_count_file_saves(browser_data * b) { browser_data * ancestor = b->ancestor; if (!ancestor) ancestor = b; return toolbars_count_file_saves_r(ancestor); } /*************************************************/ /* toolbars_count_file_saves_r() */ /* */ /* Recursive back-end to */ /* toolbars_count_file_saves. */ /* */ /* Parameters: Pointer to the ancestor */ /* browser_data struct in the */ /* frameset. */ /* */ /* Returns: As toolbars_count_file_saves. */ /*************************************************/ static int toolbars_count_file_saves_r(browser_data * b) { int i, count = 0; if (b->nchildren) { for (i = 0; i < b->nchildren; i++) { count += toolbars_count_file_saves_r(b->children[i]); } } if (b->save_file) count ++; return count; } /*************************************************/ /* toolbars_calculate_progress() */ /* */ /* Works out how much data has been fetched for */ /* a frameset. */ /* */ /* If a file save is in progress, the amount of */ /* data fetched for that file save and nothing */ /* else is returned as a negative number (to */ /* flag that this is happening). Callers must */ /* remember to check for this, even if only to */ /* reverse the sign on the returned value. */ /* */ /* Parameters: Pointer to any browser_data */ /* struct in the frameset in */ /* question. */ /* */ /* Returns: The amount collectively fetched, */ /* in bytes, or the amount fetched */ /* for a file save with its sign */ /* reversed (i.e. a negative number) */ /* again in bytes. */ /*************************************************/ static int toolbars_calculate_progress(browser_data * b) { browser_data * ancestor = b->ancestor; int saves = toolbars_count_file_saves(b); if (!ancestor) ancestor = b; return toolbars_calculate_progress_r(ancestor, saves); } /*************************************************/ /* toolbars_calculate_progress_r() */ /* */ /* Recursive back-end to */ /* toolbars_calculate_progress. */ /* */ /* Parameters: Pointer to the ancestor */ /* browser_data struct in the */ /* frameset; */ /* */ /* 0 if no browser in the frameset */ /* is saving data to a file, else */ /* non-zero. */ /* */ /* Returns: As toolbars_calculate_progress. */ /*************************************************/ static int toolbars_calculate_progress_r(browser_data * b, int saves) { int i, fetched = 0; if (b->nchildren) { int localfetched; /* For children, if the frameset has some file saving going on */ /* use only negative numbers returned, i.e. report cumulative */ /* file sizes. Otherwise, use only positive numbers, i.e. */ /* report image / page source fetched data. */ for (i = 0; i < b->nchildren; i++) { localfetched = toolbars_calculate_progress_r(b->children[i], saves); if (saves) { if (localfetched < 0) fetched += localfetched; } else { if (localfetched > 0) fetched += localfetched; } } } if (b->save_file) { /* If saving, don't want to confuse the issue with any image */ /* fetch information or whatever. So just use the current */ /* output file size. */ fetched = (int) (-(ftell(b->save_file))); } else { /* Count the image data obtained so far */ fetched += image_total_bytes_fetched(b); /* Add the current source store size */ if (b->save_oldstore) fetched += b->save_oldstore; else if (b->source) fetched += flex_size((flex_ptr) &b->source); } 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; */ /* */ /* Size of that buffer. */ /* */ /* Returns: The number of file saves going on */ /* inside the frameset, from */ /* toolbars_count_file_saves. */ /*************************************************/ static int toolbars_create_progress(browser_data * b, char * buffer, int buffer_size) { browser_data * ancestor = utils_ancestor(b); int saves = 0; int len = 1; /* Start with the terminating zero byte accounted for */ int percentage = 0; int fetched; fetched = toolbars_calculate_progress(ancestor); if (fetched < 0) fetched = -fetched, saves = toolbars_count_file_saves(b); /* If a negative number, the absolute value is the amount of data being saved to a file */ /* Is the buffer big enough? */ if (fetched < 10240) len = utils_number_length(fetched); else if (fetched < 10485760) len = utils_number_length((fetched + 512) / 1024) + 1; /* For 'K' */ else len = utils_number_length((fetched + 524288) / 1048576) + 1; /* For the 'M' */ if (saves) len += utils_number_length(saves) + 2; if (len > buffer_size) { #ifdef TRACE erb.errnum = Utils_Error_Custom_Normal; sprintf(erb.errmess, "Buffer size of %d isn't as large as required (%d) in toolbars_create_progress", buffer_size, len); show_error_ret(&erb); #endif return saves; } /* For single file saves, we may be able to use a percentage counter */ if (saves == 1 && b->data_size > 0 && b->save_link) { percentage = (fetched * 100) / b->data_size; /* If the data size was misjudged, we can exceed 100% fetched. In */ /* that case, drop back to a byte counter, rather than sticking */ /* at 100%. */ if (percentage > 100) percentage = 0; } /* 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 (saves < 2) { if (percentage) { /* Special case - if we know the data size, and we're saving an object, */ /* then display a percentage amount fetched instead. */ sprintf(buffer, "%d%%", percentage); } 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); } } else { if (fetched < 10240) sprintf(buffer, "%d: %d", saves, fetched); else if (fetched < 10485760) sprintf(buffer, "%d: %dK", saves, (fetched + 512) / 1024); else sprintf(buffer, "%d: %dM", saves, (fetched + 524288) / 1048576); } return saves; } /*************************************************/ /* 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[Limits_URLBarWrit]; 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, URLBarWrit, cmp, sizeof(cmp), NULL); if (strcmp(cmp, url)) writablefield_set_value(0, t, URLBarWrit, 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[Limits_URLBarDiallerStatus]; char compare[Limits_URLBarDiallerStatus]; 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 > controls.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 / (controls.quantise * 100)) * controls.quantise; if (hours > 99) hours = 99; if (sizeof(display) >= 9) sprintf(display, "%02u:%02u:%02u\0", hours, minutes, seconds); else *display = 0; /* Get the current string into 'compare' */ e = displayfield_get_value(0, t, URLBarDiallerStatus, compare, sizeof(compare), NULL); if (e) return e; compare[sizeof(compare) - 1] = 0; /* Update the display field if necessary, else exit */ if (strcmp(display, compare)) { e = displayfield_set_value(0, t, URLBarDiallerStatus, 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[Limits_URLBarDiallerStatus]; char statstr[Limits_URLBarDiallerStatus]; 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_last) { /* dialler_last holds a timer, and if non-zero is used by window close */ /* routines to show that the null claimant needs deregistering. It is */ /* unlikely, but nonetheless possible, that the window could be closed */ /* after the handler is registered but before it is called, so the */ /* dialler_last field would still be zero and no deregistration would */ /* take place. Hence to ensure that this doesn't happen, set the field */ /* to a (small) non-zero value for now. */ b->dialler_last = 1; register_null_claimant(Wimp_ENull, (WimpEventHandler *) handle_dialler_display, b); } } else { /* Otherwise, remove the handler if it is installed */ if (b->dialler_last) deregister_null_claimant(Wimp_ENull, (WimpEventHandler *) handle_dialler_display, b); b->dialler_last = 0; /* 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, URLBarDiallerStatus, 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, URLBarDiallerStatus, display); if (e) return e; } } /* May need to alter the label on this display, if there is one */ { char label[Limits_URLBarDiallerStatusLabel]; if (button_get_value(0, t, URLBarDiallerStatusLabel, label, sizeof(label), NULL)) return NULL; label[sizeof(label) - 1] = 0; 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, URLBarDiallerStatusLabel, 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[Limits_StatusBarStatusLabel]; show_gadget(t, StatusBarStatus); hide_gadget(t, URLBarWrit); /* Update the label, if present */ if (!button_get_value(0, t, StatusBarStatusLabel, label, sizeof(label), NULL)) { label[sizeof(label) - 1] = 0; lookup_token("DisplayStats:Status",0,0); if (strcmp(label, tokens)) { StrNCpy0(label, tokens); button_set_value(0, t, StatusBarStatusLabel, 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[Limits_StatusBarStatusLabel]; show_gadget(t, URLBarWrit); hide_gadget(t, StatusBarStatus); /* Update the label, if present */ if (!button_get_value(0, t, StatusBarStatusLabel, label, sizeof(label), NULL)) { label[sizeof(label) - 1] = 0; lookup_token("DisplayURL:URL",0,0); if (strcmp(label, tokens)) { StrNCpy0(label, tokens); button_set_value(0, t, StatusBarStatusLabel, 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 ? ButtonBarBack : ButtonBarCancel; /* Copy the characteristics of the gadget over. */ ChkError(copy_toolaction_info(t, source, t, ButtonBarBistate)); 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 = ButtonBarGo; else if (state == 1) source = ButtonBarGoTo; else source = ButtonBarStop; /* Copy the characteristics of the gadget over. */ ChkError(copy_toolaction_info(t, source, t, ButtonBarTristate)); 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, ButtonBarSpacer, &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, URLBarSpacer, &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, StatusBarSpacer, &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. */ /* */ /* This call will not return errors derived from */ /* attempts to access non-existant gadgets; they */ /* will fail silently, to allow more varied UI */ /* designs to work correctly. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the browser window. */ /*************************************************/ _kernel_oserror * toolbars_set_button_states(browser_data * b) { ObjectId t; t = toolbars_get_upper(b); if (!t) return NULL; /* (E.g., parents (but not ancestors) in framesets won't have toolbars) */ if (controls.dont_grey == Controls_DontGrey_GreyAll) { /* Can only stop if there's activity of some sort */ if ( (b->anim_handler && !b->anim_drift) || (b->meta_refresh_at && b->meta_refresh_url) ) set_gadget_state(t, ButtonBarStop, 0); else set_gadget_state(t, ButtonBarStop, 1); /* Can only go back if we're not at the start of the history */ /* and there's a history to go into. */ if (!history_can_go_backwards(b)) set_gadget_state(t, ButtonBarBack, 1); else set_gadget_state(t, ButtonBarBack, 0); /* Can only go forward if we're in the history somewhere */ if (!history_can_go_forwards(b)) set_gadget_state(t, ButtonBarForward, 1); else set_gadget_state(t, ButtonBarForward, 0); /* Can only view or save the document source if there is some... */ if (!b->source) { set_gadget_state(t, ButtonBarViewSource, 1); set_gadget_state(t, ButtonBarSaveSource, 1); } else { set_gadget_state(t, ButtonBarViewSource, 0); set_gadget_state(t, ButtonBarSaveSource, 0); } /* Can only save the page as text or a Draw file, or print it, if */ /* we have a visible line list. */ if (b->nchildren || !b->cell || !b->cell->nlines) { set_gadget_state(t, ButtonBarPrint, 1); set_gadget_state(t, ButtonBarSaveAsText, 1); set_gadget_state(t, ButtonBarSaveAsDraw, 1); } else { set_gadget_state(t, ButtonBarPrint, 0); set_gadget_state(t, ButtonBarSaveAsText, 0); set_gadget_state(t, ButtonBarSaveAsDraw, 0); } /* Only need to load images if delayed image loading is set */ /* in the local browser flags, or plain backgrounds are set. */ if ( ( !b->show_foreground && b->displayed != Display_External_Image ) || !b->show_background ) set_gadget_state(t, ButtonBarLoadImages, 0); else set_gadget_state(t, ButtonBarLoadImages, 1); } if (controls.dont_grey != Controls_DontGrey_GreyNone) { /* Can only get a history menu if there's a history present. */ if (history_empty(b)) set_gadget_state(t, URLBarHistoryMenuR, 1); else set_gadget_state(t, URLBarHistoryMenuR, 0); } /* 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, URLBarWrit)) 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, URLBarWrit)) toolbars_set_tristate_state(b, t, 0); else { /* If we have an animation handler, should be showing 'Stop', */ /* else we should be at 'go to'. However, odd states can */ /* occur so it's wise to check with this tristate, where the */ /* state is very important (you can't go anywhere if it's */ /* stuck on Stop, for example), what the status bar says. */ if ( (b->anim_handler && !b->anim_drift) || (b->meta_refresh_at && b->meta_refresh_url) ) { toolbars_set_tristate_state(b, t, 2); /* Stop */ } 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_Connected: case Toolbars_Status_SentReq: case Toolbars_Status_Responded: case Toolbars_Status_Redirected: case Toolbars_Status_Connecting: 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; }