/* 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 : Utils.c */ /* */ /* Purpose: Infrequently altered utilities. */ /* */ /* Author : A.D.Hodgkinson */ /* */ /* History: 18-Oct-96: Created. */ /***************************************************/ #include "setjmp.h" #include <stdlib.h> #include <stdio.h> #include <string.h> #include <stdarg.h> #include <ctype.h> #include "HTMLLib.h" /* HTML library API, Which will include html2_ext.h, tags.h and struct.h */ #include "wimp.h" #include "wimplib.h" #include "event.h" #include "swis.h" #include "kernel.h" #include "toolbox.h" #include "event.h" #include "quit.h" #include "proginfo.h" #include "window.h" #include "gadgets.h" #include "ToolAction.h" /* NOT the proper Toolbox header, as this needed OSLib... */ #include "svcprint.h" #include "Global.h" #include "MiscDefs.h" #include "FromROSLib.h" #include "Main.h" #include "NestWimp.h" #include "Save.h" #include "Toolbars.h" /* Debug build includes */ #ifdef TRACE /* Needed to check conversion to millipoints routines don't overflow */ #include <math.h> #endif /* Finally, Utils.h itself */ #include "Utils.h" /* Local definitions */ #define Acorn_Agent_Start "Acorn-" #define Acorn_Agent_Middle " (RISC OS " #define Acorn_Agent_End ")" #define Netscape_Agent_Start "Mozilla/4.01 (Compatible; Acorn " #define Netscape_Agent_Middle "; RISC OS " #define Netscape_Agent_End ")" #define MillipointsPerOSUnit 400 /* Locals */ static int millipoints_per_os_unit_x = 400; /* See read_os_to_points */ static int millipoints_per_os_unit_y = 400; static int half_mppou_x = 200; static int half_mppou_y = 200; static int overflow_limit_x = 0x28f5c1; /* = (0x3fffffff / 400) rounded down for caution*/ static int overflow_limit_y = 0x28f5c1; /* Static function prototypes */ static char * lookup_in_given (MessagesFD * control, char * s, int flag, char * arg); /*************************************************/ /* lookup_token() */ /* */ /* Returns a pointer to an expanded message */ /* token, or '!' if there was an error. */ /* */ /* NB, due to various limitations of C, the */ /* lookup is done into a global fixed-size */ /* buffer. So if you pass multiple calls to this */ /* function in as parameters to something else, */ /* *it will fail* as each call points to the */ /* same buffer (which will only contain data */ /* from the last call). */ /* */ /* Parameters: Pointer to a message token; */ /* */ /* 1 to report an error if the token */ /* isn't found as well as returning */ /* a string of '!', else 0; */ /* */ /* An optional pointer to an */ /* argument to substitute into the */ /* looked up string, or NULL. */ /* */ /* Returns: Pointer to the full message text */ /* or '!' to signal an error; never */ /* a null pointer. */ /* */ /* Assumes: That the pointer to the message */ /* token is never NULL. */ /*************************************************/ char * lookup_token(char * s, int flag, char * arg) { #ifdef TRACE if (tl & (1u<<0)) Printf("lookup_token: Called, exitting through lookup_in_given\n"); #endif return lookup_in_given(&meb, s, flag, arg); } /*************************************************/ /* lookup_choice() */ /* */ /* As lookup_token, but looks up the token in */ /* the Choices file, rather than the Messages */ /* file. */ /* */ /* Parameters: As lookup_token. */ /* */ /* Returns: As lookup_token. */ /* */ /* Assumes: As lookup_token. */ /*************************************************/ char * lookup_choice(char * s, int flag, char * arg) { #ifdef TRACE if (tl & (1u<<0)) Printf("lookup_choice: Called, exitting through lookup_in_given\n"); #endif return lookup_in_given(chb, s, flag, arg); } /*************************************************/ /* lookup_control() */ /* */ /* As lookup_token, but looks up the token in */ /* the Controls file, rather than the Messages */ /* file. */ /* */ /* Parameters: As lookup_token. */ /* */ /* Returns: As lookup_token. */ /* */ /* Assumes: As lookup_token. */ /*************************************************/ char * lookup_control(char * s, int flag, char * arg) { #ifdef TRACE if (tl & (1u<<0)) Printf("lookup_control: Called, exitting through lookup_in_given\n"); #endif return lookup_in_given(cob, s, flag, arg); } /*************************************************/ /* lookup_in_given() */ /* */ /* Workhorse back-end to lookup_token, */ /* lookup_choice and so-on. See lookup_token */ /* for more information. */ /* */ /* Parameters: A MessagesFD pointer, giving the */ /* control block of the file to */ /* look in; */ /* */ /* Pointer to a message token; */ /* */ /* 1 to report an error if the token */ /* isn't found as well as returning */ /* a string of '!', else 0; */ /* */ /* An optional pointer to an */ /* argument to substitute into the */ /* looked up string, or NULL. */ /* */ /* Returns: Pointer to the full message text */ /* or '!' to signal an error; never */ /* a null pointer. */ /* */ /* Assumes: That the pointer to the message */ /* token is never NULL. */ /*************************************************/ static char * lookup_in_given(MessagesFD * control, char * s, int flag, char * arg) { _kernel_oserror * e; #ifdef TRACE if (tl & (1u<<0)) Printf("lookup_in_given: Lookup token '%s'\n",s); #endif if (strcmp(lasttokn, s)) { #ifdef TRACE if (tl & (1u<<0)) Printf("lookup_in_given: Proceeding\n"); #endif StrNCpy0(lasttokn, s); e = _swix(MessageTrans_Lookup, _INR(0,7), control, /* Pointer to control block */ s, /* String to look up */ tokens, /* Global buffer to take looked up string */ sizeof(tokens) - 1, /* Size of the buffer */ arg, /* Parameter 0 */ 0, /* Parameter 1 */ 0, /* Parameter 2 */ 0); /* Parameter 3 */ tokens[Limits_Message - 1] = 0; if (e) { /* If the lookup fails, put '!' into the lookup buffer and if the */ /* flag passed into the function is 1, report the error too. */ #ifdef TRACE if (tl & (1u<<0)) Printf("lookup_in_given: Failed\n"); #endif *lasttokn = 0; strcpy(tokens, "!"); if (flag == 1) show_error_cont(e); } } #ifdef TRACE if (tl & (1u<<0)) Printf("lookup_in_given: Returning %s\n",tokens); #endif return (char *) &tokens; } /*************************************************/ /* show_error() */ /* */ /* Reports a (generally serious) error and exits */ /* with EXIT_FAILURE. */ /* */ /* Parameters: Pointer to a _kernel_oserror */ /* structure. */ /* */ /* Assumes: The pointer may be NULL. */ /*************************************************/ void show_error(_kernel_oserror *e) { if (e!=NULL) { _kernel_swi_regs r; char name[Limits_TaskName]; char spri[Limits_OS_SpriteName]; WimpSysInfo s; /* This call checks if errors can be reported in the Desktop, */ /* or if they need to go into a command window (useful for */ /* CLI routines for example) */ s.r0=0; s.r1=0; wimp_read_sys_info(3,&s); if (s.r0==0) fprintf(stderr,"%s\n",e->errmess); else { StrNCpy0(name,lookup_token("_TaskName:Browse",0,0)); /* Task name for 'Message from...' */ StrNCpy0(spri,lookup_token("_SpriName:!browse",0,0)); /* Sprite name to put in error box */ r.r[0] = (int) e; /* Pointer to error block */ r.r[1] = (2<<9)+(1<<8); /* Category 2 (warning) */ r.r[2] = (int) &name; /* Application name looked up above */ r.r[3] = (int) &spri; /* Sprite name looked up above */ r.r[4] = 1; /* Sprite block pointer (1 = WIMP) */ r.r[5] = (int) lookup_token("ErrorBtns:Quit",0,0); /* Custom button, 'Quit' */ _kernel_swi(Wimp_ReportError,&r,&r); } exit(EXIT_FAILURE); /* Exit after reporting the error */ } } /*************************************************/ /* show_error_cont() */ /* */ /* Reports an error but allows execution to then */ /* continue (rather than calling exit()) if the */ /* user clicks on 'Continue' rather than 'Quit'. */ /* This is accomplished by a longjmp back into */ /* wherever the setjmp was (e.g. in a poll */ /* loop). */ /* */ /* Parameters: Pointer to a _kernel_oserror */ /* structure. */ /* */ /* Assumes: The pointer may be NULL. */ /*************************************************/ void show_error_cont(_kernel_oserror *e) { if (e!=NULL) { _kernel_swi_regs r; char name[Limits_TaskName]; char spri[Limits_OS_SpriteName]; WimpSysInfo s; #ifdef TRACE if (e->errnum == Utils_Error_Custom_Fatal) e->errnum = Utils_Error_Custom_Normal; #endif /* Force 'Quit' only for fatal errors */ if (e->errnum == Utils_Error_Custom_Fatal) show_error(e); /* This all works in much the same way as show_error above. */ s.r0=0; s.r1=0; wimp_read_sys_info(3,&s); if (s.r0==0) fprintf(stderr,"%s\n",e->errmess); else { StrNCpy0(name, lookup_token("_TaskName:Browse",0,0)); StrNCpy0(spri, lookup_token("_SpriName:!browse",0,0)); r.r[0] = (int) e; r.r[1] = (2<<9)+(1<<8)+1; r.r[2] = (int) &name; r.r[3] = (int) &spri; r.r[4] = 1; /* Have a quit button if not running full screen and the */ /* error number isn't one defined as having a Continue */ /* button only. */ if ( e->errnum != Utils_Error_OS_Escape && e->errnum != Utils_Error_Custom_Message && !choices.full_screen ) r.r[5] = (int) lookup_token("ErrorBtns:Quit",0,0); else r.r[5] = 0; _kernel_swi(Wimp_ReportError,&r,&r); if (r.r[1] == 1) longjmp(env, Main_FromShowErrorCont); exit(EXIT_FAILURE); /* Exit if 'Quit' is selected */ } } } /*************************************************/ /* show_error_ret() */ /* */ /* Reports an error but allows execution to then */ /* continue (rather than calling exit()) if the */ /* user clicks on 'Continue' rather than 'Quit'. */ /* This is accomplished by simply returning. */ /* */ /* Parameters: Pointer to a _kernel_oserror */ /* structure. */ /* */ /* Assumes: The pointer may be NULL. */ /*************************************************/ void show_error_ret(_kernel_oserror *e) { if (e!=NULL) { _kernel_swi_regs r; char name[Limits_TaskName]; char spri[Limits_OS_SpriteName]; WimpSysInfo s; #ifdef TRACE if (e->errnum == Utils_Error_Custom_Fatal) e->errnum = Utils_Error_Custom_Normal; #endif /* Force 'Quit' only for fatal errors */ if (e->errnum == Utils_Error_Custom_Fatal) show_error(e); /* This all works in much the same way as show_error above. */ s.r0=0; s.r1=0; wimp_read_sys_info(3,&s); if (s.r0==0) fprintf(stderr,"%s\n",e->errmess); else { StrNCpy0(name,lookup_token("_TaskName:Browse",0,0)); StrNCpy0(spri,lookup_token("_SpriName:!browse",0,0)); r.r[0] = (int) e; r.r[1] = (2<<9)+(1<<8)+1; r.r[2] = (int) &name; r.r[3] = (int) &spri; r.r[4] = 1; /* Have a quit button if not running full screen and the */ /* error number isn't one defined as having a Continue */ /* button only. */ if ( e->errnum != Utils_Error_OS_Escape && e->errnum != Utils_Error_Custom_Message && !choices.full_screen ) r.r[5] = (int) lookup_token("ErrorBtns:Quit",0,0); else r.r[5] = 0; _kernel_swi(Wimp_ReportError,&r,&r); if (r.r[1] != 1) exit(EXIT_FAILURE); /* Exit if 'Quit' is selected, else return normally */ } } } /*************************************************/ /* report_toolbox_error() */ /* */ /* If the toolbox generates an error this funct- */ /* ion will be called to report it. Parameters */ /* are as standard for a Toolbox event handler. */ /*************************************************/ int report_toolbox_error(int eventcode,ToolboxEvent *event,IdBlock *idb,void *handle) { ChkError((_kernel_oserror *) &event->data); return 1; } /*************************************************/ /* make_no_fetch_memory_error() */ /* */ /* Typically called from Fetch.c, if a memory */ /* claim fails early in a fetch. Stores an */ /* appropriate error in the global error */ /* block 'erb'. */ /* */ /* Parameters: A numerical value to include in */ /* the message to help the */ /* programmer know where the error */ /* came from. */ /*************************************************/ _kernel_oserror * make_no_fetch_memory_error(int stage) { char num[20]; sprintf(num, "%d", stage); erb.errnum = 0; StrNCpy0(erb.errmess, lookup_token("NoMemFet:There is not enough free memory to perform this fetch (%0).", 0, num)); return &erb; } /*************************************************/ /* make_no_cont_memory_error() */ /* */ /* Called if a memory claim fails during a fetch */ /* - stores an appropriate error in the global */ /* error block 'erb'. */ /* */ /* Parameters: A numerical value to include in */ /* the message to help the */ /* programmer know where the error */ /* came from. */ /*************************************************/ _kernel_oserror * make_no_cont_memory_error(int stage) { char num[20]; sprintf(num, "%d", stage); erb.errnum = 0; StrNCpy0(erb.errmess, lookup_token("NoMemRea:There is not enough free memory to continue the page fetch (%0).", 0, num)); return &erb; } /*************************************************/ /* make_no_table_memory_error() */ /* */ /* Typically called from Tables.c, if a memory */ /* claim fails during table parsing routines. */ /* Stores an appropriate error in the global */ /* error block 'erb'. */ /* */ /* Parameters: A numerical value to include in */ /* the message to help the */ /* programmer know where the error */ /* came from. */ /*************************************************/ _kernel_oserror * make_no_table_memory_error(int stage) { char num[20]; sprintf(num, "%d", stage); erb.errnum = 0; StrNCpy0(erb.errmess, lookup_token("NoMemTab:There is not enough free memory to display this table (%0).", 0, num)); return &erb; } /*************************************************/ /* make_no_memory_error() */ /* */ /* A general error generation routine for failed */ /* memory claims. Stores the error in the global */ /* error block 'erb'. */ /* */ /* Parameters: A numerical value to include in */ /* the message to help the */ /* programmer know where the error */ /* came from. */ /*************************************************/ _kernel_oserror * make_no_memory_error(int stage) { char num[20]; sprintf(num, "%d", stage); erb.errnum = 0; StrNCpy0(erb.errmess, lookup_token("NoMemGen:There is not enough free memory to continue this operation (%0).", 0, num)); return &erb; } /*************************************************/ /* show_centred() */ /* */ /* Shows a Toolbox object centred to the screen, */ /* opened persistently where possible */ /* */ /* Parameters: An Object ID of any Toolbox */ /* object that will return its Wimp */ /* window handle when */ /* Toolbox_ObjectMiscOp is called */ /* for it with a reason code of 0 - */ /* e.g. Window, DCS, or ColourDBox. */ /* */ /* Assumes: That the ID is a valid one. */ /*************************************************/ void show_centred(ObjectId o) { WimpGetWindowStateBlock w; ObjectId p; BBox b; _kernel_oserror *e; /* Get the Wimp window handle of the Toolbox object */ /* and get its size using Wimp_GetWindowState. This */ /* sounds simple, but there's a different function */ /* call to get the window handle for each object */ /* class. The best (but still poor) approach is to */ /* call the Toolbox_MiscOp SWI with a reason code */ /* of 0, which in most cases will mean 'return Wimp */ /* window handle'. If this is not the case for an */ /* object type, the Wimp call will then fault and */ /* this error condition can be used to default down */ /* to some coordinate value that seems appropriate. */ w.window_handle = 0; _swix(Toolbox_ObjectMiscOp, _INR(0,2) | _OUT(0), 0, o, 0, &w.window_handle); e = wimp_get_window_state(&w); if (e != NULL) { w.visible_area.xmin = 480; w.visible_area.ymin = 320; } else { w.visible_area.xmin = w.visible_area.xmax - w.visible_area.xmin; w.visible_area.ymin = w.visible_area.ymax - w.visible_area.ymin; } /* Find the screen x and y size in pixels and scale them */ /* to OS units using OS_ReadModeVariable calls; also work */ /* out the top left coordinates at the same time */ _swix(OS_ReadModeVariable,_INR(0,1) | _OUT(2),-1,11,&w.xscroll); _swix(OS_ReadModeVariable,_INR(0,1) | _OUT(2),-1,4,&w.yscroll); b.xmin = (((w.xscroll + 1) << w.yscroll) - w.visible_area.xmin) / 2; _swix(OS_ReadModeVariable,_INR(0,1) | _OUT(2),-1,12,&w.xscroll); _swix(OS_ReadModeVariable,_INR(0,1) | _OUT(2),-1,5,&w.yscroll); b.ymin = (((w.xscroll + 1) << w.yscroll) + w.visible_area.ymin) / 2; ChkError(toolbox_get_parent(0,o,&p,NULL)); ChkError(toolbox_show_object(0, /* Bit 0 set - Wimp_CreateMenu semantics; */ /* Bit 1 set - Wimp_CreateSubMenu semantics */ o, /* Object ID given to function */ 2, /* 0 - 'default position'; 1 - specify position in */ /* full; 2 - use top left corner coordinate pair */ &b.xmin, /* Top left corner coordinate pair */ p, /* Parent object ID */ -1)); /* Parent component ID (not interested in that) */ } /*************************************************/ /* set_corrected_extent() */ /* */ /* Sets the extent of a window, making sure that */ /* xmin = 0 and ymax = 0 (so ymin is negative, */ /* etc. etc.) - this means that topx = topy = 0. */ /* */ /* Parameters: Flags to pass to the Toolbox in */ /* the SetExtent call; */ /* */ /* The object ID of the browser */ /* window to be altered; */ /* */ /* Pointer to a BBox holding the */ /* extent coordinates. */ /*************************************************/ _kernel_oserror * set_corrected_extent(unsigned int f, ObjectId o, BBox * w) { BBox t; t.xmin = 0; t.ymin = w->ymin - w->ymax; t.xmax = w->xmax - w->xmin; t.ymax = 0; return window_set_extent(f,o,&t); } /*************************************************/ /* find_behind() */ /* */ /* Returns the window handle of the first non- */ /* pane window in front of a given window. */ /* */ /* Parameters: The window handle in question. */ /* */ /* Returns: Handle of the first non-pane */ /* window in front of the given one, */ /* or -1 if it is at the top of the */ /* stack. */ /*************************************************/ int find_behind(int w) { WimpGetWindowStateBlock s; s.window_handle = w; ChkError(wimp_get_window_state(&s)); if (s.behind != -1) { do { s.window_handle = s.behind; ChkError(wimp_get_window_state(&s)); } while(((s.flags & WimpWindow_Pane) != 0) && (s.behind != -1)); s.behind = s.window_handle; } return s.behind; } /*************************************************/ /* find_tool_sizes() */ /* */ /* Returns the title bar and scroll bar widths */ /* in OS units, including their outlines. */ /* */ /* Parameters: Pointer to an int, in which the */ /* title bar height is placed; */ /* */ /* Pointer to an int, in which the */ /* horizontal scroll bar bar height */ /* is placed; */ /* */ /* Pointer to an int, in which the */ /* vertical scroll bar width is */ /* placed. */ /* */ /* Assumes: Any of the pointers may be NULL. */ /*************************************************/ _kernel_oserror * find_tool_sizes(int * theight, int * hheight, int * vwidth) { _kernel_oserror * e; WimpGetWindowOutlineBlock outline; WimpGetWindowStateBlock s; ObjectId o; int th, hh, vw; /* Create an object with a title bar and both scroll bars */ e = toolbox_create_object(0, "ToolSizes", &o); if (e) return e; /* Open it behind the Pinboard */ s.visible_area.xmin = 256; s.visible_area.ymin = 256; s.visible_area.xmax = 512; s.visible_area.ymax = 512; s.xscroll = 0; s.yscroll = 0; s.behind = -3; e = toolbox_show_object(0, o, Toolbox_ShowObject_FullSpec, &s.visible_area, 0, -1); if (e) return e; /* Get the window state (for current visible area) and outline */ e = window_get_wimp_handle(0, o, &s.window_handle); if (e) return e; e = wimp_get_window_state(&s); if (e) return e; outline.window_handle = s.window_handle; e = wimp_get_window_outline(&outline); if (e) return e; /* Work out the various sizes */ th = outline.outline.ymax - s.visible_area.ymax; hh = s.visible_area.ymin - outline.outline.ymin; vw = outline.outline.xmax - s.visible_area.xmax; if (theight) *theight = th; if (hheight) *hheight = hh; if (vwidth) *vwidth = vw; /* Return via. deleting the temporary window */ return toolbox_delete_object(0, o); } /*************************************************/ /* register_null_claimant() */ /* */ /* Call if you want to claim null polls. */ /* */ /* Parameters: As for a Wimp event handler, but */ /* without the object ID. */ /*************************************************/ void register_null_claimant(int eventcode,WimpEventHandler * handler,browser_data * handle) { null_counter++; ChkError(event_register_wimp_handler(-1,eventcode,handler,handle)); #ifdef TRACE if (tl & (1u<<2)) { int shut_up_compiler = (int) handler; int * function_name = (int *) shut_up_compiler; Printf("register_null_claimant: Registered a claimant for browser %p\n", handle); /* If the word before the function address has ff in the high */ /* byte, the function name starts as many bytes before the */ /* function address as specified in the low 3 bytes. */ function_name -= 1; if (((*function_name) & 0xff000000) == 0xff000000) { Printf(" Handler is '\0213%s\0217'\n", (char *) (((char *) function_name) - ((*function_name) & 0x00ffffff))); } else { Printf(" Cannot find handler's name; address is \02130x%08x\0217\n", (int) handler); } } #endif if (null_counter == 1) { unsigned int mask; ChkError(event_get_mask(&mask)); mask = (mask & (~Wimp_Poll_NullMask)); ChkError(event_set_mask(mask)); #ifdef TRACE if (tl & (1u<<2)) Printf("register_null_claimant: \0211Nulls claimed\0217\n"); #endif } } /*************************************************/ /* deregister_null_claimant() */ /* */ /* Call if you want to release null polls. */ /* */ /* Parameters: As for a Wimp event handler, but */ /* without the object ID. */ /*************************************************/ void deregister_null_claimant(int eventcode,WimpEventHandler * handler,browser_data * handle) { null_counter--; ChkError(event_deregister_wimp_handler(-1,eventcode,handler,handle)); #ifdef TRACE if (tl & (1u<<2)) { int shut_up_compiler = (int) handler; int * function_name = (int *) shut_up_compiler; Printf("deregister_null_claimant: Deregistered a claimant for browser %p\n", handle); /* If the word before the function address has ff in the high */ /* byte, the function name starts as many bytes before the */ /* function address as specified in the low 3 bytes. */ function_name -= 1; if (((*function_name) & 0xff000000) == 0xff000000) { Printf(" Handler is '\0216%s\0217'\n", (char *) (((char *) function_name) - ((*function_name) & 0x00ffffff))); } else { Printf(" Cannot find handler's name; address is \2160x%08x\0217\n", (int) handler); } } #endif if (null_counter < 0) null_counter = 0; if (!null_counter) { unsigned int mask; ChkError(event_get_mask(&mask)); mask = (mask | Wimp_Poll_NullMask); ChkError(event_set_mask(mask)); #ifdef TRACE if (tl & (1u<<2)) Printf("deregister_null_claimant: \0212Nulls released\0217\n"); #endif } } /*************************************************/ /* intersection() */ /* */ /* Takes two BBoxes and returns a pointer to a */ /* third which is the the intersection between */ /* the first two, or NULL, if they don't */ /* intersect. */ /* */ /* Parameters: Pointer to a BBox; */ /* */ /* Pointer to another BBox. */ /* */ /* Returns: Pointer to a BBox which is the */ /* intersection of the given two, */ /* or NULL, if they don't intersect. */ /*************************************************/ BBox * intersection(BBox * a, BBox * b) { static BBox intersect; #define max(a,b) ((a) > (b) ? (a) : (b)) #define min(a,b) ((a) < (b) ? (a) : (b)) if (!a || !b) return NULL; if ((a->xmin >= b->xmax) || (a->xmax <= b->xmin) || (a->ymin >= b->ymax) || (a->ymax <= b->ymin)) return NULL; intersect.xmin = max(a->xmin,b->xmin); intersect.xmax = min(a->xmax,b->xmax); intersect.ymin = max(a->ymin,b->ymin); intersect.ymax = min(a->ymax,b->ymax); return &intersect; } /*************************************************/ /* set_graphics_intersection() */ /* */ /* Intended for redraw loop routines, this sets */ /* up a given graphics rectangle, but takes */ /* account of the intersection between this and */ /* the current (given) graphics rectangle for */ /* the redraw. The rectangle *must* be restored */ /* with restore_graphics_intersection() as soon */ /* as the rectangle set here is finished with; */ /* the caller must thus remember this rectangle */ /* for later. */ /* */ /* Parameters: Pointer to a BBox describing the */ /* rectangle to set, where xmax and */ /* ymax are inclusive; */ /* */ /* Pointer to a BBox describing the */ /* current graphics rectangle, where */ /* xmax and ymax are exclusive (e.g. */ /* as in a WimpRedrawWindowBlock's */ /* redraw_area BBox). */ /* */ /* Returns: Pointer to a BBox describing the */ /* actual rectangle that was set. If */ /* this is NULL, the two do not */ /* intersect at all and the redraw */ /* subsequent graphics window */ /* restoration can and should be */ /* skipped. */ /*************************************************/ BBox * set_graphics_intersection(BBox * rbox, BBox * cbox) { BBox * ibox; BBox ogrect = *cbox; ogrect.xmax -= 1; ogrect.ymax -= 1; ibox = intersection(rbox, &ogrect); if (!ibox) return NULL; bbc_gwindow(ibox->xmin, ibox->ymin, ibox->xmax, ibox->ymax); return ibox; } /*************************************************/ /* restore_graphics_intersection() */ /* */ /* Restores the Wimp's redraw graphics rectangle */ /* which was changed by a call to */ /* set_graphics_intersection (which *must* have */ /* been called before this restoring function). */ /* */ /* Parameters: Pointer to a BBox holding the */ /* graphics rectangle as it was */ /* before set_graphics_intersection */ /* was called, where xmax and ymax */ /* are exclusive (e.g. as in a */ /* WimpRedrawWindowBlock's */ /* redraw_area BBox). */ /*************************************************/ void restore_graphics_intersection(BBox * cbox) { BBox ogrect = *cbox; ogrect.xmax -= 1; ogrect.ymax -= 1; bbc_gwindow(ogrect.xmin, ogrect.ymin, ogrect.xmax, ogrect.ymax); } /*************************************************/ /* read_os_to_points() */ /* */ /* To avoid having to use a SWI every time */ /* a conversion is made between OS units and */ /* points or vice versa, this initialises */ /* some internal variables which are used */ /* subsequently. It may be called on a mode */ /* change, for example, to ensure things are up */ /* to date. */ /* */ /* If printing, values of MillipointsPerOSUnit */ /* as defined at the top of this file are used, */ /* since you can't read it; it seems that during */ /* a print job, this call may *not* be used, */ /* contrary to the information on PRM 3-573. */ /* This bug caused *severe* grief during the */ /* development of the print routines... */ /*************************************************/ void read_os_to_points(void) { int x = 1, y = 1; if (!printing) { if ( _swix(Font_Converttopoints, _INR(1,2) | _OUTR(1,2), x, y, &x, &y) ) { millipoints_per_os_unit_x = MillipointsPerOSUnit; millipoints_per_os_unit_y = MillipointsPerOSUnit; } else { millipoints_per_os_unit_x = x; millipoints_per_os_unit_y = y; } } else { millipoints_per_os_unit_x = MillipointsPerOSUnit; millipoints_per_os_unit_y = MillipointsPerOSUnit; } overflow_limit_x = (0x3fffffff / millipoints_per_os_unit_x) - 1; overflow_limit_y = (0x3fffffff / millipoints_per_os_unit_y) - 1; half_mppou_x = millipoints_per_os_unit_x / 2; half_mppou_y = millipoints_per_os_unit_y / 2; } /*************************************************/ /* convert_pair_to_os() */ /* */ /* Converts from millipoints to OS units. The */ /* scale factor is determined by a previous call */ /* to read_os_to_points. */ /* */ /* Parameters: A coordinate in millipoints; */ /* */ /* Another coord in millipoints; */ /* */ /* Pointer to an int into which the */ /* first coordinate, converted to OS */ /* units, is placed; */ /* */ /* Similarly a pointer to an int for */ /* the second coordinate. */ /* */ /* Assumes: The pointers may NOT be NULL. The */ /* input and output variables may be */ /* the same (so passing in x, y, &x, */ /* &y would work correctly). */ /*************************************************/ void convert_pair_to_os(int x, int y, int * osx, int * osy) { *osx = ((x + half_mppou_x) / millipoints_per_os_unit_x) & ~(wimpt_dx() - 1); *osy = ((y + half_mppou_y) / millipoints_per_os_unit_y) & ~(wimpt_dy() - 1); } /*************************************************/ /* convert_pair_to_points() */ /* */ /* Converts from OS units to millipoints. The */ /* scale factor is determined by a previous call */ /* to read_os_to_points. */ /* */ /* Parameters: A coordinate in OS units; */ /* */ /* Another coordinate in OS units; */ /* */ /* Pointer to an int into which the */ /* first coordinate, converted to */ /* millipoints, is placed; */ /* */ /* Similarly a pointer to an int for */ /* the second coordinate. */ /* */ /* Assumes: The pointers may not be NULL. The */ /* input and output variables may be */ /* the same (so passing in x, y, &x, */ /* &y would work correctly). */ /*************************************************/ void convert_pair_to_points(int x, int y, int * mpx, int * mpy) { #ifdef TRACE if (abs(x) > overflow_limit_x || abs(y) > overflow_limit_y) { erb.errnum = Utils_Error_Custom_Normal; sprintf(erb.errmess, "convert_pair_to_points: Can't convert (%d, %d) to millipoints without overflow.", x,y); show_error_ret(&erb); *mpx = *mpy = 0; return; } #endif *mpx = x * millipoints_per_os_unit_x; *mpy = y * millipoints_per_os_unit_y; } /*************************************************/ /* convert_to_os() */ /* */ /* As convert_pair_to_os, but only converts one */ /* coordinate at a time. */ /* */ /* Parameters: An x coordinate in millipoints; */ /* */ /* Pointer to an int into which the */ /* coordinate, converted to OS */ /* units, is placed. */ /* */ /* Assumes: That the pointer is not NULL. The */ /* input and output variable may be */ /* the same (so passing in x, &x */ /* would work correctly); */ /* */ /* If x and y scalings differ, this */ /* will only ever use the x scaling. */ /*************************************************/ void convert_to_os(int x, int * osx) { *osx = ((x + half_mppou_x) / millipoints_per_os_unit_x) & ~(wimpt_dx() - 1); } /*************************************************/ /* convert_to_points() */ /* */ /* As convert_pair_to_points, but only converts */ /* one coordinate at a time. */ /* */ /* Parameters: An x coordinate in OS units; */ /* */ /* Pointer to an int into which the */ /* coordinate, converted to milli- */ /* points, is placed. */ /* */ /* Assumes: That the pointer is not NULL. The */ /* input and output variable may be */ /* the same (so passing in x, &x */ /* would work correctly); */ /* */ /* If x and y scalings differ, this */ /* will only ever use the x scaling. */ /*************************************************/ void convert_to_points(int x, int * mpx) { #ifdef TRACE if (abs(x) > overflow_limit_x) { erb.errnum = Utils_Error_Custom_Normal; sprintf(erb.errmess, "convert_to_points: Can't convert '%d' to millipoints without overflow.", x); show_error_ret(&erb); *mpx = 0; return; } #endif *mpx = x * millipoints_per_os_unit_x; } /*************************************************/ /* convert_box_to_os() */ /* */ /* As convert_pair_to_os, but converts the four */ /* coordinates inside a BBox in one go. */ /* */ /* Parameters: Pointer to a BBox containing */ /* coords in millipoints; */ /* */ /* Pointer to a BBox into which the */ /* first box's coords, converted to */ /* OS units, are placed. */ /* */ /* Assumes: That neither pointer is NULL. The */ /* two pointers may be the same (so */ /* passing in &box, &box would work */ /* correctly). */ /*************************************************/ void convert_box_to_os(const BBox * mp, BBox * os) { os->xmin = ((mp->xmin + half_mppou_x) / millipoints_per_os_unit_x) & ~(wimpt_dx() - 1); os->ymin = ((mp->ymin + half_mppou_y) / millipoints_per_os_unit_y) & ~(wimpt_dy() - 1); os->xmax = ((mp->xmax + half_mppou_x) / millipoints_per_os_unit_x) & ~(wimpt_dx() - 1); os->ymax = ((mp->ymax + half_mppou_y) / millipoints_per_os_unit_y) & ~(wimpt_dy() - 1); } /*************************************************/ /* convert_box_to_points() */ /* */ /* As convert_pair_to_points, but converts the */ /* four coordinates inside a BBox in one go. */ /* */ /* Parameters: Pointer to a BBox containing */ /* coords in OS units; */ /* */ /* Pointer to a BBox into which the */ /* first box's coords, converted to */ /* millipoints, are placed. */ /* */ /* Assumes: That neither pointer is NULL. The */ /* two pointers may be the same (so */ /* passing in &box, &box would work */ /* correctly). */ /*************************************************/ void convert_box_to_points(const BBox * os, BBox * mp) { #ifdef TRACE if ( abs(os->xmin) > overflow_limit_x || abs(os->ymin) > overflow_limit_y || abs(os->xmax) > overflow_limit_x || abs(os->ymax) > overflow_limit_y ) { erb.errnum = Utils_Error_Custom_Normal; sprintf(erb.errmess, "convert_box_to_points: Can't convert (%d, %d, %d, %d) to millipoints without overflow.", os->xmin, os->ymin, os->xmax, os->ymax); show_error_ret(&erb); mp->xmin = mp->ymin = 0; mp->xmax = mp->ymax = 0; return; } #endif mp->xmin = os->xmin * millipoints_per_os_unit_x; mp->ymin = os->ymin * millipoints_per_os_unit_y; mp->xmax = os->xmax * millipoints_per_os_unit_x; mp->ymax = os->ymax * millipoints_per_os_unit_y; } /*************************************************/ /* read_sprite_size() */ /* */ /* Finds out the size of a given sprite in the */ /* application's sprite pool in OS units. */ /* */ /* Parameters: Pointer to the sprite name; */ /* */ /* Pointer to int into which the */ /* sprite's width is returned; */ /* */ /* Pointer to int into which the */ /* sprite's height is returned. */ /* */ /* Assumes: The name pointer is not NULL, but */ /* either of the two int pointers */ /* may be. */ /*************************************************/ _kernel_oserror * read_sprite_size(char * name, int * width, int * height) { int w, h, m; _kernel_oserror * e; e = _swix(OS_SpriteOp, _INR(0,2) | _OUTR(3,4) | _OUT(6), 0x128, sprite_block, name, &w, &h, &m); if (e) return e; w = w << bbc_modevar(m, BBC_XEigFactor); h = h << bbc_modevar(m, BBC_YEigFactor); if (width) *width = w; if (height) *height = h; return NULL; } /*************************************************/ /* utils_text_width() */ /* */ /* Returns the width of a given piece of text, */ /* in OS units, if it were to be plotted in the */ /* Desktop. Wimp_TextOp is used if available, */ /* else the width and spacing of the bitmap font */ /* is read and the width is calculated from */ /* this instead. */ /* */ /* Parameters: Pointer to the text; */ /* */ /* Pointer to an int, into which the */ /* width is written; */ /* */ /* 0 to work out the whole string */ /* width, or the number of chars to */ /* read. */ /* */ /* Assumes: Either pointer may be NULL; */ /* */ /* If the number of chars to read is */ /* greater than the string length, */ /* the value given is ignored and */ /* the string length used instead. */ /*************************************************/ _kernel_oserror * utils_text_width(char * text, int * width, int scan) { int cwidth, cspacing; int len; /* Return if there's no text or 'width' is NULL */ if (!width) return NULL; if (!text || !*text) { *width = 0; return NULL; } /* Otherwise, set 'len' either to the string length, */ /* if 'scan' is zero, or to the value of 'scan'. */ len = strlen(text); if (scan && scan < len) len = scan; /* Rather than try mucking about guessing what version number of */ /* Wimp supports Wimp_TextOp, simply use the alternative method */ /* if the SWI raises an error. */ if ( _swix(Wimp_TextOp, _INR(0,2) | _OUT(0), 1, text, len, width) ) { /* Find out the spacing (start of one char to start of next) */ /* and width of the text the Wimp is using, assuming that if */ /* there is no nested wimp, Wimp_TextOp is unavailable. */ int vars[3] = { BBC_GCharSizeX, BBC_GCharSpaceX, -1 }; RetError(bbc_vduvars(vars, vars)); cwidth = vars[0]; cspacing = vars[1]; /* cspacing gives how much to increment x by after plotting a */ /* character, and therefore includes cwidth; so to find the */ /* width, we'd use (len * cspacing) - (cspacing - cwidth), */ /* which simplifies to the below (plus conversion to OS units). */ *width = ((len - 1) * cspacing + cwidth) * wimpt_dx(); } /* Finished */ return NULL; } /*************************************************/ /* set_gadget_state() */ /* */ /* Greys or ungreys a gadget, only changing its */ /* state to avoid flicker. */ /* */ /* Parameters: Object ID the gadget resides in; */ /* */ /* Component ID of the gadget; */ /* */ /* 1 to grey, 0 to ungrey. */ /*************************************************/ _kernel_oserror * set_gadget_state(ObjectId o, ComponentId c, int grey_state) { _kernel_oserror * e; unsigned int flags; e = gadget_get_flags(0, o, c, &flags); if (e) return e; /* Only change state, to avoid flicker. */ if (!!grey_state != !!(flags & Gadget_Faded)) { if (grey_state) flags |= Gadget_Faded; else flags &= ~Gadget_Faded; return gadget_set_flags(0, o, c, flags); } return NULL; } /*************************************************/ /* anti_twitter() */ /* */ /* Calls the anti-twitter code over a given */ /* redraw area. */ /* */ /* Parameters: Pointer to a WimpRedrawWindow */ /* block with the redraw_area BBox */ /* holding the area over which the */ /* anti-twitter code should be */ /* called. */ /*************************************************/ void anti_twitter(WimpRedrawWindowBlock * r) { char nhantitwitter[256]; int mode, ok = 1; unsigned int modeflags; #define AntiTwitter1 50 #define AntiTwitter2 55 #ifndef TRACE /* Older interlace modules only support modes 50 and 55 */ /* directly, though can still appear to work in others. */ _swix(OS_Byte, _IN(0) | _OUT(2), 135, &mode); if (mode == AntiTwitter1 || mode == AntiTwitter2) ok = 1; else { /* If the current mode is not mode 50 or 55, this mode module */ /* may be new enough to support setting bit 8 of the modeflags */ /* to indicate interlace. */ _swix(OS_ReadModeVariable, _INR(0,1) | _OUT(2), -1, 0, &modeflags); ok = !!(modeflags & (1<<8)); } #else /* For trace builds, always try to anti-twitter (allows testing */ /* in certain non-interlaced Desktop screen modes) */ ok = 1; /* Hmph - stop compiler complaining about things not being used... */ mode = 0; modeflags = 0; #endif if (ok) { BBox gwind; BBox * area; gwind.xmin = (bbc_vduvar(BBC_GWLCol)) * wimpt_dx(); gwind.ymin = (bbc_vduvar(BBC_GWBRow)) * wimpt_dy(); gwind.xmax = (bbc_vduvar(BBC_GWRCol) + 1) * wimpt_dx(); gwind.ymax = (bbc_vduvar(BBC_GWTRow) + 1) * wimpt_dy(); area = intersection(&gwind, &r->redraw_area); if (area) { sprintf(nhantitwitter, "%%NHAntiTwitter %d %d %d %d\n", area->xmin, area->ymin, area->xmax - area->xmin, area->ymax - area->ymin); _swix(OS_CLI, _IN(0), nhantitwitter); } } } /*************************************************/ /* adjust() */ /* */ /* Returns 1 if Wimp_GetPointerInfo says that */ /* Adjust is being pressed, else 0. */ /*************************************************/ int adjust(void) { WimpGetPointerInfoBlock info; wimp_get_pointer_info(&info); return !!(info.button_state & Wimp_MouseButtonAdjust); } /*************************************************/ /* hide_gadget() */ /* */ /* Hides a given gadget by moving it out of the */ /* visible area of the window it is in. */ /* */ /* Parameters: Object ID the gadget lies in; */ /* Component ID of the gadget. */ /* */ /* Returns: 1 if the gadget was moved out, */ /* else 0. */ /*************************************************/ int hide_gadget(ObjectId o, ComponentId c) { BBox g; if (gadget_get_bbox(0, o, c, &g)) return 0; /* If the gadget has a large negative X coordinate, */ /* assume it's been moved out already. */ if (g.xmin < -4096) return 0; /* Otherwise, move it */ g.xmin -= 8192; g.xmax -= 8192; if (gadget_move_gadget(0, o, c, &g)) return 0; return 1; } /*************************************************/ /* show_gadget() */ /* */ /* Shows a given gadget hidden by hide_gadget. */ /* */ /* Parameters: Object ID the gadget lies in; */ /* Component ID of the gadget. */ /* */ /* Returns: 1 if the gadget was moved in, */ /* else 0. */ /*************************************************/ int show_gadget(ObjectId o, ComponentId c) { BBox g; if (gadget_get_bbox(0, o, c, &g)) return 0; /* If the gadget hasn't got a large negative X coordinate, */ /* assume it's not been moved out. */ if (g.xmin > -4096) return 0; /* Otherwise, move it */ g.xmin += 8192; g.xmax += 8192; if (gadget_move_gadget(0, o, c, &g)) return 0; return 1; } /*************************************************/ /* gadget_hidden() */ /* */ /* Call to find out if a gadget has been moved */ /* out with hide_gadget or is still visible. */ /* */ /* Parameters: Object ID the gadget lies in; */ /* Component ID of the gadget. */ /* */ /* Returns: 1 if the gadget is hidden else 0. */ /*************************************************/ int gadget_hidden(ObjectId o, ComponentId c) { BBox g; /* If there's an error getting the gadget's bounding box, then the gadget */ /* is missing altogether or something else has gone wrong; in any case, */ /* safest action is to say it has been hidden. */ if (gadget_get_bbox(0, o, c, &g)) return 1; /* Simple assumption that this much of a negative X value = gadget hidden */ if (g.xmin < -4096) return 1; return 0; } /*************************************************/ /* slab_gadget() */ /* */ /* Slabs a gadget in briefly, by setting its */ /* Selected bit. Gadget must be made of one icon */ /* only. */ /* */ /* Parameters: Object ID the gadget lies in; */ /* Component ID of the gadget. */ /*************************************************/ void slab_gadget(ObjectId o, ComponentId c) { WimpSetIconStateBlock set; WimpGetIconStateBlock get; int icon[2]; /* Get the icon number and window handle of the gadget */ if (gadget_get_icon_list(0, o, c, icon, sizeof(icon), NULL)) return; if (window_get_wimp_handle(0, o, &get.window_handle)) return; get.icon_handle = icon[0]; /* Get the icon state */ if (wimp_get_icon_state(&get)) return; /* Set the flags as selected */ set.window_handle = get.window_handle; set.icon_handle = get.icon_handle; set.EOR_word = get.icon.flags | WimpIcon_Selected; set.clear_word = 0xffffffff; if (wimp_set_icon_state(&set)) return; /* Wait a while */ { int time_now, time_start; _swix(OS_ReadMonotonicTime, _OUT(0), &time_start); do { _swix(OS_ReadMonotonicTime, _OUT(0), &time_now); } while (time_now - time_start < 15); } /* Restore the old flags */ set.EOR_word = get.icon.flags; wimp_set_icon_state(&set); } /*************************************************/ /* slab_gadget_in() */ /* */ /* Slabs a gadget in, by setting its Selected */ /* bit. Gadget must be made of one icon only. */ /* */ /* Parameters: Object ID the gadget lies in; */ /* Component ID of the gadget. */ /*************************************************/ void slab_gadget_in(ObjectId o, ComponentId c) { WimpSetIconStateBlock set; WimpGetIconStateBlock get; int icon[2]; /* Get the icon number and window handle of the gadget */ if (gadget_get_icon_list(0, o, c, icon, sizeof(icon), NULL)) return; if (window_get_wimp_handle(0, o, &get.window_handle)) return; get.icon_handle = icon[0]; /* Get the icon state */ if (wimp_get_icon_state(&get)) return; /* Set the flags as selected */ set.window_handle = get.window_handle; set.icon_handle = get.icon_handle; set.EOR_word = get.icon.flags | WimpIcon_Selected; set.clear_word = 0xffffffff; wimp_set_icon_state(&set); } /*************************************************/ /* slab_gadget_out() */ /* */ /* Slabs a gadget out, by clearing its Selected */ /* bit. Gadget must be made of one icon only. */ /* */ /* Parameters: Object ID the gadget lies in; */ /* Component ID of the gadget. */ /*************************************************/ void slab_gadget_out(ObjectId o, ComponentId c) { WimpSetIconStateBlock set; WimpGetIconStateBlock get; int icon[2]; /* Get the icon number and window handle of the gadget */ if (gadget_get_icon_list(0, o, c, icon, sizeof(icon), NULL)) return; if (window_get_wimp_handle(0, o, &get.window_handle)) return; get.icon_handle = icon[0]; /* Get the icon state */ if (wimp_get_icon_state(&get)) return; /* Set the flags as unselected */ set.window_handle = get.window_handle; set.icon_handle = get.icon_handle; set.EOR_word = get.icon.flags &~ WimpIcon_Selected; set.clear_word = 0xffffffff; wimp_set_icon_state(&set); } /*************************************************/ /* utils_check_caret_restoration() */ /* */ /* Checks to see if the given dialogue has the */ /* caret, and if it has a parent. If so, it'll */ /* return the Object ID of that parent, else */ /* NULL_ObjectId is written. */ /* */ /* Parameters: The Object ID of the dialogue */ /* to check. */ /*************************************************/ ObjectId utils_check_caret_restoration(ObjectId window_id) { WimpGetCaretPositionBlock caret_b; int caret_w; ObjectId parent = NULL_ObjectId; _kernel_oserror * e = NULL; /* Do we have the input focus? */ e = wimp_get_caret_position(&caret_b); if (!e) { e = window_get_wimp_handle(0, window_id, &caret_w); if (!e) { if (caret_w == caret_b.window_handle) { /* Yes, we have the caret. So move it back to the Print */ /* dialogue - well, this object's parent, anyway. */ e = toolbox_get_parent(0, window_id, &parent, NULL); if (e) parent = NULL_ObjectId; else if (parent == NULL_ObjectId) { /* Maybe there's an ancestor? (E.g. PrintDBox using an */ /* alternate window -> doesn't pass Parent info through */ /* so we need the ancestor instead... sigh). */ e = toolbox_get_ancestor(0, window_id, &parent, NULL); if (e) parent = NULL_ObjectId; } } } } /* Finished */ return parent; } /*************************************************/ /* utils_restore_caret() */ /* */ /* If the given dialogue has the caret, put the */ /* caret into the parent of this object, in the */ /* default input focus position. */ /* */ /* Parameters: The Object ID of the dialogue */ /* whos parent is to gain the caret. */ /* */ /* Assumes: The parent has a default caret */ /* position set up. */ /*************************************************/ _kernel_oserror * utils_restore_caret(ObjectId window_id) { _kernel_oserror * e = NULL; ObjectId parent; ComponentId focus_c; /* Do we have the input focus and a parent? */ parent = utils_check_caret_restoration(window_id); if (parent != NULL_ObjectId) { /* Find the default caret position of the parent */ e = window_get_default_focus(0, parent, &focus_c); /* Set the focus there */ if (!e) { if (focus_c != NULL_ComponentId) gadget_set_focus(0, parent, focus_c); } } /* Finished */ return e; } /*************************************************/ /* copy_toolaction_info() */ /* */ /* Copies the internal details of a given */ /* ToolAction gadget to another. */ /* */ /* Ident off, ident on, ident faded, select */ /* action, adjust action and click-show details */ /* are copied with ToolAction SWIs; the help */ /* text is copied with gadget library calls. */ /* */ /* This does *not* copy state info, such as */ /* on/off or greyed. */ /* */ /* Parameters: Object ID the source gadget is */ /* in; */ /* */ /* Component ID of the source; */ /* */ /* Object ID the destination gadget */ /* is in; */ /* */ /* Component ID of the destination. */ /*************************************************/ _kernel_oserror * copy_toolaction_info(ObjectId src_o, ComponentId src_c, ObjectId dst_o, ComponentId dst_c) { _kernel_oserror * e; char ident [Limits_ToolActionIdent]; char help [Limits_Help]; unsigned int flags; int adjust_act, select_act; int adjust_cs, select_cs; /* Off state ident string */ e = _swix(Toolbox_ObjectMiscOp, _INR(0,5), toolaction_SET_IDENT_OFF, src_o, ToolAction_GetIdent, src_c, ident, sizeof(ident)); if (e) return e; e = _swix(Toolbox_ObjectMiscOp, _INR(0,4), toolaction_SET_IDENT_OFF, dst_o, ToolAction_SetIdent, dst_c, ident); if (e) return e; /* On state ident string */ e = _swix(Toolbox_ObjectMiscOp, _INR(0,5), toolaction_SET_IDENT_ON, src_o, ToolAction_GetIdent, src_c, ident, sizeof(ident)); if (e) return e; e = _swix(Toolbox_ObjectMiscOp, _INR(0,4), toolaction_SET_IDENT_ON, dst_o, ToolAction_SetIdent, dst_c, ident); if (e) return e; /* Faded state ident string */ e = _swix(Toolbox_ObjectMiscOp, _INR(0,5), toolaction_SET_IDENT_FADE, src_o, ToolAction_GetIdent, src_c, ident, sizeof(ident)); if (e) return e; e = _swix(Toolbox_ObjectMiscOp, _INR(0,4), toolaction_SET_IDENT_FADE, dst_o, ToolAction_SetIdent, dst_c, ident); if (e) return e; /* Adjust and select actions */ e = _swix(Toolbox_ObjectMiscOp, _INR(0,3) | _OUTR(0,1), 0, src_o, ToolAction_GetAction, src_c, &select_act, &adjust_act); if (e) return e; e = _swix(Toolbox_ObjectMiscOp, _INR(0,5), 0, dst_o, ToolAction_SetAction, dst_c, select_act, adjust_act); if (e) return e; /* Adjust and select click-shows */ e = _swix(Toolbox_ObjectMiscOp, _INR(0,3) | _OUTR(0,1), 0, src_o, ToolAction_GetClickShow, src_c, &select_cs, &adjust_cs); if (e) return e; e = _swix(Toolbox_ObjectMiscOp, _INR(0,5), 0, dst_o, ToolAction_SetClickShow, dst_c, select_cs, adjust_cs); if (e) return e; /* The gadget flags */ e = gadget_get_flags(0, src_o, src_c, &flags); if (e) return e; e = gadget_set_flags(flags, dst_o, dst_c, flags); if (e) return e; /* Finally, the help text */ e = gadget_get_help_message(0, src_o, src_c, help, sizeof(help), NULL); if (e) return e; return gadget_set_help_message(0, dst_o, dst_c, help); } /*************************************************/ /* set_window_flags() */ /* */ /* Sets the flags of a given window, assuming */ /* the nested Wimp is available... */ /* */ /* Parameters: Window handle; */ /* EOR word; */ /* Clear word. */ /* */ /* Assumes: That a window manager that */ /* supports extended Wimp_OpenWindow */ /* calls (R2 = 'TASK') is present. */ /* */ /* The flags are set according to */ /* */ /* new = (old BIC clear word) EOR EOR word */ /* */ /* i.e.: */ /* */ /* C E Effect */ /* ------------ */ /* 0 0 Preserve bit */ /* 0 1 Toggle bit */ /* 1 0 Clear bit */ /* 1 1 Set bit */ /*************************************************/ _kernel_oserror * set_window_flags(int window_handle, unsigned int clear_word, unsigned int eor_word) { /* Block required for the extended Wimp_OpenWindow */ typedef struct { WimpOpenWindowBlock open; unsigned int flags; } ExtendedOpenBlock; _kernel_oserror * e; WimpGetWindowStateBlock s; unsigned int parent, align; unsigned int new_flags; ExtendedOpenBlock ext_o; /* Get the current window details */ s.window_handle = window_handle; e = _swix(Wimp_GetWindowState, _INR(1, 2) | _OUTR(3, 4), &s, Magic_Word_TASK, /* See MiscDefs.h */ &parent, &align); if (e) return e; /* Obtain the new flags word */ new_flags = (s.flags & ~clear_word) ^ eor_word; /* Fill in the new open block and reopen the window with it */ ext_o.open.window_handle = s.window_handle; ext_o.open.visible_area = s.visible_area; ext_o.open.xscroll = s.xscroll; ext_o.open.yscroll = s.yscroll; ext_o.open.behind = s.behind; ext_o.flags = new_flags; return _swix(Wimp_OpenWindow, _INR(1,4), &ext_o, Magic_Word_TASK, parent, align | Alignment_NewFlagsGiven); } /*************************************************/ /* debounce_keypress() */ /* */ /* For some key presses (e.g. function keys), it */ /* is not desirable to let the key autorepeat. */ /* This function sits in a tight loop waiting */ /* for all keys to be released before exitting. */ /* */ /* Returns: 1 if a key was being pressed and the */ /* function waited for its release, */ /* else 0. */ /*************************************************/ int debounce_keypress(void) { int key, waited = 0; _kernel_oserror * e; do { e = _swix(OS_Byte, _INR(0,1) | _OUT(1), 121, /* Keyboard scan */ 0, /* Scan all keys */ &key); if (key != 255) waited = 1; } while (!e && key != 255); if (waited) _swix(OS_Byte, _INR(0,1), 21, 0); /* Flush keyboard buffer */ return waited; } /*************************************************/ /* task_from_window() */ /* */ /* Returns the task handle of the owner of a */ /* given window. */ /* */ /* Parameters: A window handle. */ /* */ /* Returns: Task handle of the window owner. */ /*************************************************/ int task_from_window (int window_handle) { WimpMessage m; int handle; m.hdr.size = 20; m.hdr.your_ref = 0; m.hdr.action_code = 0; if ( wimp_send_message(Wimp_EUserMessageAcknowledge, &m, window_handle, 0, &handle) ) return 0; return handle; } /*************************************************/ /* utils_browser_from_window() */ /* */ /* Given a window handle, returns an associated */ /* browser_data struct, if one may be found. */ /* */ /* Parameters: The window handle; */ /* */ /* Pointer to a pointer to a */ /* browser_data struct, in which the */ /* associated structure's address is */ /* written, or NULL for none found / */ /* there is an error. */ /* */ /* Assumes: The last parameter may not be */ /* NULL (it is pointless to allow */ /* this). */ /*************************************************/ _kernel_oserror * utils_browser_from_window(int window_handle, browser_data ** browser) { ObjectId o = NULL, a = NULL; *browser = NULL; /* Get the associated Object ID */ RetError(window_wimp_to_toolbox(0, window_handle, -1, &o, NULL)); /* If this has an ancestor, check the ID against the ancestor's */ /* toolbar IDs. This is because we could be a frame - in which */ /* case there is an ancestor - or a toolbar, in which case we */ /* want to return the browser_data struct for that ancestor. */ RetError(toolbox_get_ancestor(0, o, &a, NULL)); if (a) { browser_data * ancestor = NULL; ObjectId upper, lower; /* Get the ancestor's client handle */ RetError(toolbox_get_client_handle(0, a, (void *) &ancestor)); /* If a known browser, get the toolbars */ if (is_known_browser(ancestor)) { upper = toolbars_get_upper(ancestor); lower = toolbars_get_lower(ancestor); /* If either ID matches that which the message */ /* came from, the return the ancestor ID. */ if (upper == o || lower == o) { *browser = ancestor; return NULL; } } } /* Get its client handle */ RetError(toolbox_get_client_handle(0, o, (void *) browser)); /* If this is not a known browser, don't use it */ if (!is_known_browser(*browser)) *browser = NULL; /* Finished */ return NULL; } /*************************************************/ /* is_known_browser() */ /* */ /* Finds out if the given browser_data struct is */ /* in the list of known structures. The struct */ /* may be NULL (0 would be returned), so this */ /* doesn't need to be checked for externally. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* to check. */ /* */ /* Returns: 1 if the structure is in the list */ /* or 0 if it isn't. */ /*************************************************/ int is_known_browser(browser_data * b) { browser_data * check = last_browser; int found = 0; /* If b is NULL, can't be a valid browser_data structure! */ if (!b) return 0; /* Search the list */ while (check && !found) { if (check == b) found = 1; else check = check->previous; } /* Return the search results */ return found; } /*************************************************/ /* utils_parent() */ /* */ /* Works out a given browser_data structure's */ /* parent. If there is only an ancestor or */ /* neither an ancestor or parent, it returns */ /* NULL. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* that you want the parent of. */ /* */ /* Returns: Pointer to the structure's */ /* parent, or NULL if there is only */ /* an ancestor or no parents at all. */ /* */ /* Assumes: If it gets NULL, it returns NULL. */ /*************************************************/ browser_data * utils_parent(browser_data * b) { if (!b) return NULL; if (b->parent && b->parent != b->ancestor) return b->parent; else if (b->real_parent && b->real_parent != b->ancestor) return b->real_parent; return NULL; } /*************************************************/ /* utils_ancestor() */ /* */ /* Works out a given browser_data structure's */ /* ancestor (which may be the given structure!). */ /* */ /* Parameters: Pointer to a browser_data struct */ /* that you want the ancestor of. */ /* */ /* Returns: Pointer to the structure's */ /* ancestor. */ /* */ /* Assumes: If it gets NULL, it returns NULL. */ /*************************************************/ browser_data * utils_ancestor(browser_data * b) { if (!b) return NULL; if (b->ancestor) return b->ancestor; return b; } /*************************************************/ /* encode_base64() */ /* */ /* Passed a pointer to data and its length, this */ /* will fill the output buffer with Base64- */ /* encoded data, returning the length of the */ /* output data (this is not terminated). */ /* */ /* The length of the output will be - */ /* */ /* (length of input, rounded up to next multiple */ /* of 3) times 4 */ /* */ /* - so make sure you have a big enough output */ /* buffer. */ /* */ /* Parameters: Pointer to the data to encode; */ /* */ /* Length of the data to encode; */ /* */ /* Pointer to the output buffer. */ /* */ /* Assumes: That the output buffer is big */ /* enough (see above). */ /*************************************************/ int encode_base64(const char * in, int len, char * out) { const static char * base64_table = "ABCDEFGHIJKLMNOP" "QRSTUVWXYZabcdef" "ghijklmnopqrstuv" "wxyz0123456789+/"; unsigned long chunk; char * out_ptr = out; int p = 0; int i; while (p < len) { chunk = 0; for (i = 0; i < 3; i++, p++) { if (p < len) chunk = (chunk << 8) | in[p]; else chunk = (chunk << 8); } *out++ = base64_table[chunk >> 18]; *out++ = base64_table[(chunk >> 12) & 0x3f]; if (p <= len + 1) { *out++ = base64_table[(chunk >> 6) & 0x3f]; if (p <= len) *out++ = base64_table[chunk & 0x3f]; else *out++ = '='; } else { *out++ = '='; *out++ = '='; } } return out - out_ptr; } /*************************************************/ /* utils_strcasecmp() */ /* */ /* Function to compare two strings case */ /* insensitively. */ /* */ /* Originally by sbrodie. */ /* */ /* The conversions to unsigned int stop the */ /* compiler messing around with shifts all over */ /* the place whilst trying to promote the chars */ /* to int whilst retaining the sign. */ /* */ /* Problems: Choice of return value when strings */ /* do not match is based upon character number */ /* rather than any alphabetic sorting. */ /* */ /* Parameters: As strcmp. */ /* */ /* Returns: As strcmp. */ /*************************************************/ int utils_strcasecmp(const char *a, const char *b) { for (;;) { unsigned int f1 = *a++; unsigned int s1 = *b++; if (f1 == 0) return -s1; if (f1 != s1) { unsigned int f2 = (unsigned int) tolower(f1); unsigned int s2 = (unsigned int) tolower(s1); signed int result = f2 - s2; if (result != 0) return result; } } } /*************************************************/ /* utils_strncasecmp() */ /* */ /* Function to compare two strings case */ /* insensitively up to a maximum char count. */ /* */ /* Originally by sbrodie. */ /* */ /* The conversions to unsigned int stop the */ /* compiler messing around with shifts all over */ /* the place whilst trying to promote the chars */ /* to int whilst retaining the sign. */ /* */ /* Problems: Choice of return value when strings */ /* do not match is based upon character number */ /* rather than any alphabetic sorting. */ /* */ /* Parameters: As strncmp. */ /* */ /* Returns: As strncmp. */ /*************************************************/ int utils_strncasecmp(const char * a, const char * b, unsigned int n) { for (; n; --n) { unsigned int f1 = *a++; unsigned int s1 = *b++; if (f1 == 0) return -s1; if (f1 != s1) { unsigned int f2 = (unsigned int) tolower(f1); unsigned int s2 = (unsigned int) tolower(s1); signed int result = f2 - s2; if (result != 0) return result; } } return 0; } /*************************************************/ /* utils_get_task_handle() */ /* */ /* Returns the task handle of the given task */ /* (name comparison is case insensitive). */ /* */ /* Parameters: Pointer to a null-terminated task */ /* name; */ /* */ /* Pointer to an unsigned int, in */ /* which the task handle is written, */ /* or 0 if the task is not found. */ /* */ /* Assumes: Neither pointer may be NULL. */ /*************************************************/ _kernel_oserror * utils_get_task_handle(const char * task_to_get, unsigned int * found_handle) { _kernel_oserror * e; char * c; int * p; int buffer [32]; char taskname[Limits_TaskName]; int * notused; int t; int len = strlen(task_to_get); int next = 0; unsigned int handle = 0; do { e = _swix(TaskManager_EnumerateTasks, _INR(0,2) | _OUTR(0,1), next, buffer, sizeof(buffer), &next, ¬used); if (e) return e; /* Go through as much of the buffer as the call said it used */ for (p = buffer; p < notused && handle == 0; p += 4) { c = (char *) p[1]; t = 0; memset(taskname, 0, sizeof(taskname)); while (*c > 31 && t < sizeof(taskname) - 1) taskname[t++] = *c++; if (!utils_strncasecmp(taskname, task_to_get, len)) handle = p[0]; } } while (next >= 0 && handle == 0); /* Return the handle */ *found_handle = handle; return NULL; } /*************************************************/ /* utils_stop_proxy() */ /* */ /* Stops the [WebServe] application by sending */ /* an AppControl message. */ /*************************************************/ _kernel_oserror * utils_stop_proxy(void) { unsigned int handle = 0; WimpMessage msg; /* First, get WebServe's task handle */ RetError(utils_get_task_handle(lookup_token("ProxyName:Acorn WebServe",0,0), &handle)); #ifdef TRACE if (!handle) { /* Didn't find WebServe, so complain and exit */ erb.errnum = Utils_Error_Custom_Message; StrNCpy0(erb.errmess, "WebServe is not present"); show_error_ret(&erb); return NULL; } #else if (!handle) { /* Didn't find WebServe, so exit */ return NULL; } #endif /* If WebServe is present, send the message */ msg.hdr.size = 32; msg.hdr.your_ref = 0; msg.hdr.action_code = Wimp_MAppControl; msg.data.app_control.version = 1; msg.data.app_control.flags = Wimp_MAppControl_ImmediateAction; msg.data.app_control.reason = Wimp_MAppControl_Stop; return wimp_send_message(Wimp_EUserMessage, &msg, handle, -1, NULL); } /*************************************************/ /* utils_len_printf() */ /* */ /* Returns the length of a given printf string. */ /* Can be useful for buffer length determination */ /* when wishing to use sprintf, for example. */ /* */ /* This function runs *very* slowly. If it is */ /* difficult / impossible to determine the */ /* length (or upper limit of length) any other */ /* way, use it; else find a different approach */ /* (e.g. see utils_number_length). */ /* */ /* Parameters: As for printf. */ /* */ /* Returns: Length of the output string (as */ /* returned by vfprintf) or -1 if */ /* some error occurred. */ /*************************************************/ int utils_len_printf(const char * format, ...) { int len; va_list ptr; FILE * fp; fp = fopen("Null:", "wb"); if (!fp) return -1; va_start(ptr, format); len = vfprintf(fp, format, ptr); va_end(ptr); fclose(fp); return len; } /*************************************************/ /* utils_number_length() */ /* */ /* Returns the number of bytes the given number */ /* would occupy if converted to a string by */ /* sprintf with a '%d' specifier used in the */ /* format string. */ /* */ /* Parameters: The number to check. */ /* */ /* Returns: The number of characters it would */ /* occupy as a string. */ /* */ /* Assumes: An 'int' is 32-bit signed. */ /*************************************************/ int utils_number_length(int number) { int len = 0; if (number < 0) len += 1, number = -number; if (number < 10) len += 1; else if (number < 100) len += 2; else if (number < 1000) len += 3; else if (number < 10000) len += 4; else if (number < 100000) len += 5; else if (number < 1000000) len += 6; else if (number < 10000000) len += 7; else if (number < 100000000) len += 8; else if (number < 1000000000) len += 9; else len += 10; return len; } /*************************************************/ /* utils_return_hash() */ /* */ /* Returns a very crude hash number for a given */ /* string. */ /* */ /* Parameters: Pointer to the string. */ /* */ /* Returns: A hash number. */ /*************************************************/ unsigned int utils_return_hash(const char * s) { unsigned int h = 0; if (!s) return 0; while (*s) h += *s++; return h; } /*************************************************/ /* utils_build_user_agent_string */ /* */ /* Works out an appropriate HTTP response User */ /* Agent string. */ /* */ /* Parameters: Flag to indicate fake Netscape */ /* header required. */ /* */ /* Pointer to a buffer to place the */ /* string in; */ /* */ /* Size of the buffer - note that a */ /* string longer than */ /* Limits_UserAgent will never be */ /* generated. */ /*************************************************/ void utils_build_user_agent_string(int netscape, char * buffer, int buffer_size) { _kernel_oserror * e; char agent[Limits_UserAgent]; char ver [32]; int modnum, bcdver = 0; const char * agent_middle, * agent_end; if (!buffer || !buffer_size) return; buffer[0] = '\0'; /* The AGENT_... definitions are at the top of ths file. */ /* */ /* First, the string start, e.g. 'Mozilla/2.0 (compatible; ' */ StrNCpy0(agent, netscape ? Netscape_Agent_Start : Acorn_Agent_Start); /* The browser name */ *lasttokn = 0; /* Being very (maybe over?) cautions here */ *tokens = 0; lookup_token("_TaskName",1,0); /* Can't have spaces outside the comment field */ if (!strchr(agent, '(')) { while (strchr(tokens, ' ')) *strchr(tokens, ' ') = '-'; } if (strlen(tokens) + strlen(agent) + 2 < sizeof(agent)) { strcat(agent, tokens); /* Separate version by a / outside the comment field (see HTTP 1.1 spec section 3.8) */ if (strchr(agent, '(')) strcat(agent, " "); else strcat(agent, "/"); } /* The browser version */ *lasttokn = 0; *tokens = 0; lookup_token("Version",1,0); /* Just do a simple version outside of the comment field */ if (!strchr(agent, '(')) { if (strchr(tokens, ' ')) *strchr(tokens, ' ') = '\0'; } /* Can't have nested ()s in the User Agent string */ if (strchr(tokens, '(')) *strchr(tokens, '(') = '['; if (strchr(tokens, ')')) *strchr(tokens, ')') = ']'; /* Append the version, if it'll fit */ if (strlen(tokens) + strlen(agent) + 1 < sizeof(agent)) strcat(agent, tokens); /* Intermediate string */ agent_middle = netscape ? Netscape_Agent_Middle : Acorn_Agent_Middle; if (strlen(agent) + strlen(agent_middle) + 1 < sizeof(agent)) strcat(agent, agent_middle); /* Extract the Utility Module version number */ e = _swix(OS_Module, _INR(0,1) | _OUT(1), 18, "UtilityModule", &modnum); if (e) return; e = _swix(OS_Module, _INR(0,2) | _OUT(6), 20, modnum, -1, &bcdver); if (e) return; /* Write the version number and put it into the agent string */ sprintf(ver, "%d.%02x", bcdver >> 16, (bcdver & 0xffff) >> 8); if (strlen(agent) + strlen(ver) + 1 < sizeof(agent)) strcat(agent, ver); /* Finish things off */ agent_end = netscape ? Netscape_Agent_End : Acorn_Agent_End; if (strlen(agent) + strlen(agent_end) + 1 < sizeof(agent)) strcat(agent, agent_end); /* Copy into the given buffer */ strncpy(buffer, agent, buffer_size - 1); buffer[buffer_size - 1] = 0; /* Finished. */ return; } /*************************************************/ /* utils_check_scrap() */ /* */ /* If Save_ScrapFile (which should represent a */ /* system variable name, see Save.h) does not */ /* exist, report an appropriate error and */ /* return 1. Else return 0. */ /* */ /* Returns: See above. */ /*************************************************/ int utils_check_scrap(void) { int len; _kernel_swi_regs r; r.r[0] = (int) Save_ScrapVar; r.r[1] = (int) NULL; r.r[2] = -1; r.r[3] = 0; r.r[4] = 4; /* _swix will not work correctly for this particular SWI if */ /* requiring the returned R2 value. Something to do with */ /* the call relying on generating an error, but _swix spots */ /* it and pulls out earlier than the call expects. Or some */ /* such thing... */ _kernel_swi(OS_ReadVarVal, &r, &r); len = r.r[2]; if (!len) { erb.errnum = Utils_Error_Custom_Message; sprintf(erb.errmess, "%s%s", Save_ScrapFile, lookup_token("NoScrapDef: not defined.",0,0)); show_error_ret(&erb); return 1; } return 0; } /*************************************************/ /* utils_canonicalise_path() */ /* */ /* Take some pathname (which may include */ /* a path or other general system variable) and */ /* expand (or canonicalise) it. */ /* */ /* Caller is responsible for calling free() on */ /* the returned block. */ /* */ /* Parameters: Pointer to the path to */ /* canonicalise; */ /* */ /* Pointer to a char *, which will */ /* be filled in with the address of */ /* a malloced block - the caller is */ /* responsible for freeing it. */ /* */ /* Returns: If there is an error, it returns */ /* it, but it may return NULL and */ /* also return NULL as the pointer */ /* to the malloced block if some */ /* other internal failure occurred. */ /*************************************************/ _kernel_oserror * utils_canonicalise_path(const char * in, char ** out) { int required; if (!in || !*in || !out) return NULL; RetError(_swix(OS_FSControl, _INR(0, 5) | _OUT(5), 37, in, NULL, NULL, NULL, 0, &required)); /* Path length not including terminator returned as MINUS r5 */ *out = malloc(1 - required); /* (Yes, '1 - required' - see above!) */ if (!*out) return make_no_memory_error(30); RetError(_swix(OS_FSControl, _INR(0, 5) | _OUT(5), 37, in, *out, NULL, NULL, 1 - required, &required)); /* Er, 'something' went wrong... PRMs say to check, but not what to */ /* do if you don't get 1 back here and haven't had an error from */ /* the SWI call! */ if (required != 1) { free (*out); *out = NULL; } return NULL; } /*************************************************/ /* utils_build_tree() */ /* */ /* Takes a fully canonicalised pathname and */ /* ensures that all the directories in the path */ /* exist. This is useful if you are going to */ /* save something to a temporary directory in */ /* Scrap or somewhere in <Choices$Write>, say, */ /* and need to ensure that the directory */ /* structure you're addressing is present. */ /* */ /* Parameters: Pointer to the path to ensure is */ /* present. */ /*************************************************/ _kernel_oserror * utils_build_tree(const char * path) { char * temp; char * p; int level, len; /* Sanity check, and take a local copy of the path */ if (!path || !*path) return NULL; len = strlen(path); temp = malloc(len + 1); if (!temp) return make_no_memory_error(31); retry: level = 0; strcpy(temp, path); /* Create the directories */ do { p = strrchr(temp, '.'); if (p) { *p = '\0'; if (!_swix(OS_File, _INR(0,1) | _IN(4), 8, temp, 0)) { if (level) goto retry; else break; } } level++; } while (p); /* Finished */ free(temp); return NULL; }