/* ht_help.c - hypertext help */ /* * ht_help.c - on-line context-sensitive hypertext help * Version for Motif programs (based on a PC version) * last revision - 6 June 1994 */ /* * Copyright 1991 - 1994, Andrzej Stochniol, London, UK * * ASEDIT text editor, both binary and source (hereafter, Software) is * copyrighted by Andrzej Stochniol (hereafter, AS) and ownership remains * with AS. * * AS grants you (hereafter, Licensee) a license to use the Software * for academic, research and internal business purposes only, without a * fee. Licensee may distribute the binary and source code (if released) * to third parties provided that the copyright notice and this statement * appears on all copies and that no charge is associated with such copies. * * Licensee may make derivative works. However, if Licensee distributes * any derivative work based on or derived from the Software, then * Licensee will: * (1) notify AS regarding its distribution of the derivative work, and * (2) clearly notify users that such derivative work is a modified version * and not the original ASEDIT distributed by AS. * * Any Licensee wishing to make commercial use of the Software should * contact AS to negotiate an appropriate license for such commercial use. * Commercial use includes: * (1) integration of all or part of the source code into a product for sale * or license by or on behalf of Licensee to third parties, or * (2) distribution of the binary code or source code to third parties that * need it to utilize a commercial product sold or licensed by or on * behalf of Licensee. * * A. STOCHNIOL MAKES NO REPRESENTATIONS ABOUT THE SUITABILITY OF THIS * SOFTWARE FOR ANY PURPOSE. IT IS PROVIDED "AS IS" WITHOUT EXPRESS OR * IMPLIED WARRANTY. IN NO EVENT SHALL A. STOCHNIOL BE LIABLE FOR ANY * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * By using or copying this Software, Licensee agrees to abide by the * copyright law and all other applicable laws, and the terms of this * license. * AS shall have the right to terminate this license immediately by * written notice upon Licensee's breach of, or non-compliance with, any * of its terms. Licensee may be held legally responsible for any * copyright infringement that is caused or encouraged by Licensee's * failure to abide by the terms of this license. * * * Andrzej Stochniol (A.Stochniol@ic.ac.uk) * 30 Hatch Road * London SW16 4PN * UK */ #include #include #include #include #include #include #include #include #include /* needed for declaration of _XmSelectColorDefault (prcatically needed only since Motif 1.2x) */ #if defined(vax11c) || defined(__DECC) # define MAXPATHLEN 256 #else # include /* for MAXPATHLEN declaration */ #endif /* vax11c */ #include "asedit.h" /* for the definition of aseditWindowStruct */ /* set help directory (in a way compatible with the old Imakefile file) */ #ifndef HELPDIR # ifndef vax11c char *help_dir = "/usr/lib/X11"; # else char *help_dir = "ASEDIT$HELP:"; # endif /* !vax11c */ #else char *help_dir = HELPDIR; #endif /* default help file name */ static char *help_name_def = "asedit.hlp"; /* declarations & data specific for ht_help */ #define MAX_CROSS_REF 99 #define MAX_TOPIC_LEN 32000 #define MAX_TOPIC_LINKS 1000 typedef struct /* characterize currently chosen topic & its state*/ { long id; /* id of the topic */ char buf[MAX_TOPIC_LEN+2]; /* text of the topic */ int links; /* number of links */ long link_pos[MAX_TOPIC_LINKS]; /* link positions (starts)*/ int link_len[MAX_TOPIC_LINKS]; /* length of link words */ /* topic current state flags follow ... */ long cursor_pos; /* current cursor position */ Boolean link_selected; /* link ready to select flag */ int link_index; /* link index (when link_selected is True) */ } hypertext_topic; typedef struct { Boolean initialized; /* when hypertext initialize = True */ int n_topics; /* number of help topics */ int n_cross_refs; /* number of cross references */ char *file_name; /* A.S. hypertext help file name */ long *topic_ids; /* pointer to the topic id's table */ long *topic_adrs; /* pointer to the topic adresses's table*/ long *cross_refs; /* pointer to the cross references table; [2*i] element shows an adress which should be substituted by [2*i+1] adress; */ } hypertext_help; typedef struct { char *label; /* PushButton's Label */ void (*callback)(); /* pointer to a button callback routine (when activated) */ int data; /* client data for the callback routine */ } ActionAreaItem; hypertext_topic htopic; hypertext_help hth = {False, 0, 0, NULL}; Widget hypertext_text, follow_help_button, back_help_button; /* specific definitions & data for going back in the help window */ /* size of the back_help stack*/ #define BackHelpSTACK_SIZE 30 typedef struct { long id; /* id of the topic */ long cursor_pos; /* cursor position when new topic was called */ Boolean link_selected; /* link ready to select flag */ int link_index; /* link index (when link_selected is True)*/ } BackHelpEl, *BackHelpPtr; typedef struct { int bottom; /* bootom (head) of the stack */ int top; /* top (tail) of the stack */ BackHelpPtr el[BackHelpSTACK_SIZE]; /* pointers to stack elements */ } BackHelpStack; BackHelpStack bhelp_stack = {0, 0}; /* procedure prototypes */ #ifdef _NO_PROTO void TurnOffSashTraversal ( ); static void SaveBackHelpStep ( ); static void HelpBackCB ( ); static Widget CreateHelpScrolledText ( ); static void flush_backhelp_stack ( ); static void pop_backhelp_stack ( ); static void push_backhelp_stack ( ); #else /* _NO_PROTO */ void TurnOffSashTraversal(Widget pane); static void SaveBackHelpStep(hypertext_topic *topic); static void HelpBackCB (Widget w, XtPointer client_data, XtPointer call_data); static Widget CreateHelpScrolledText (Widget parent); static void flush_backhelp_stack(BackHelpStack *s); static void pop_backhelp_stack (BackHelpStack *s, BackHelpPtr *el); static void push_backhelp_stack(BackHelpStack *s, BackHelpPtr el); #endif #ifdef _NO_PROTO char *createFullHelpName(helpDir, helpName, helpDirFallback, helpNameFallback) char *helpDir; char *helpName; char *helpDirFallback; char *helpNameFallback; #else /* _NO_PROTO */ char *createFullHelpName(char *helpDir, char *helpName, char *helpDirFallback, char *helpNameFallback) #endif { char help_file_name[MAXPATHLEN]; /* full help file name */ char *fullName=NULL; int len; /* create full help file name (including path); user specified help directory takes precedence over the one set during installation */ if(!helpDir) strcpy(help_file_name, helpDirFallback); /* helpDir (X resource) was NULL */ else strcpy(help_file_name, helpDir); #if !defined(vax11c) && !defined(__DECC) if(help_file_name[strlen(help_file_name) - 1] != '/') strcat(help_file_name,"/"); /* add '/' if needed */ #endif /* !vax11c */ if(!helpName) strcat(help_file_name, helpNameFallback); /* helpName (X resource) was NULL */ else strcat(help_file_name, helpName); len = strlen(help_file_name); fullName = (char *) XtMalloc(len+1); if(fullName) strcpy(fullName, help_file_name); return fullName; } /* createFullHelpName */ #ifdef _NO_PROTO static void get_hypertext_topic(topic_id, hth, topic) long topic_id; hypertext_help *hth; hypertext_topic *topic; #else /* _NO_PROTO */ static void get_hypertext_topic(long topic_id, hypertext_help *hth, hypertext_topic *topic) #endif { long l; int i, len; int kind=0; /* okresla typ (kolor) wprowadzanego wyrazu: 0-normalny, 1-b.jasny, nazwa komendy, 2-innego koloru normalny wyraz */ char ch; FILE *in; /* char Copyright[17] = {"(C) A. Stochniol"}; *** such initialization is not allowed on DEC */ char *Copyright = "(C) A. Stochniol"; char Cop_test[17]; int links=0, index=0; int nr_wyr = 0, len_wyr[100]; /* old extension for support of colour terms- max. 100 */ long x_wyr[100]; if(!hth->file_name) { /* first call; create the full help file name (including path) */ hth->file_name = createFullHelpName(lstr.helpDir, lstr.helpName, help_dir, help_name_def); } /* assign default data for not existing or for an "error topic" */ topic->id = 0L; topic->links = 0; if ( (in = fopen(hth->file_name,"rb")) == NULL ) { sprintf(topic->buf, lstr.help_err_fopen, hth->file_name ); return; } if(!hth->initialized) /* przeczytanie calego zbioru i ustalenie adresow poczatkow help_informacji */ { long int file_pos0; /* sprawdzenie poprawnosci zbioru - odczytanie hasla itp. ***/ fseek(in,20L,0); /* od tego miejsca powinno byc inf. Copyright */ fscanf(in,"%17c",Cop_test); if(strncmp(Copyright,Cop_test,16)) { sprintf(topic->buf, lstr.help_err_finc, hth->file_name ); fclose(in); return; } fseek(in,40L,0); /* przeskoczenie hasla */ /* all addresses are stored in the ASCII form; it is important because the same help file might be used on computers with different byte ordering schemes (for example: IBM RS/6000, SGI IRIS use big endian ordering, but DEC VAX, Intel 80x86 use little endian byte ordering). So storing the data in a binary form would be restricted to only one, specific byte ordering (additionally we could run into problems with sizeof of int and long on different computers!). ASCII way of reading follows: */ fscanf(in,"%d%d",&hth->n_topics, &hth->n_cross_refs); hth->topic_ids = (long *)calloc(hth->n_topics,sizeof(long)); hth->topic_adrs= (long *)calloc(hth->n_topics,sizeof(long)); hth->cross_refs= (long *)calloc(2*hth->n_cross_refs,sizeof(long)); for(i=0; i< hth->n_topics;i++) fscanf(in,"%ld%ld", &hth->topic_ids[i], &hth->topic_adrs[i]); for (i=0; i< hth->n_cross_refs; i++) fscanf(in,"%ld%ld", &hth->cross_refs[2*i], &hth->cross_refs[2*i+1]); /* dopasowanie adresow o poczatkowa czesc zbioru .. */ file_pos0=ftell(in) + 1; /* za ostatnia liczba byla spacja */ for(i=0; i< hth->n_topics; i++) hth->topic_adrs[i] += file_pos0; } hth->initialized =True; /* Help file was succesfuly opened and adresses of the help topics have been initialized */ /* okreslenie numeru help_informacji ... - dostajemy go z zewnatrz */ /* sprawdzenie czy nie ma cross referencji ..*/ for(i=0; in_cross_refs; i++) { if(topic_id == hth->cross_refs[2*i]) { topic_id=hth->cross_refs[2*i+1]; break; } } /* find address (position) of the topic */ index = -1; for(i=0; in_topics; i++) { if(topic_id == hth->topic_ids[i]) index=i; } if(index < 0) { sprintf(topic->buf, lstr.help_no_info /* language specific: "SORRY. No help information for this item." */ ); fclose(in); return; } topic->id = topic_id; /* store the retrieved topic id */ /* read in a topic into topic buffer (topic->buf) */ links = 0; l=kind=len=nr_wyr=0; fseek(in, hth->topic_adrs[index] , 0); while( l 0 && kind == 1) topic->link_len[links++]=len; if(len > 0 && kind == 2) len_wyr[nr_wyr++]=len; len=kind=0; break; case 1: kind=1; /* poczatek slowa kluczowego (komendy)*/ topic->link_pos[links] = l; /* position in the text widget start from 0*/ break; case 4: kind=2; /* poczatek wyrazenia kolorow.*/ x_wyr[nr_wyr]= l; break; default: if(ch == '~') break; topic->buf[l++] = ch; if(ch != '\n' && kind > 0) len++; if(ch == '\n' && len > 0) /* zamykamy slowo kluczowe (just in case) */ { if(kind == 1) topic->link_len[links++]=len; else if(kind == 2) len_wyr[nr_wyr++]=len; len=kind=0; } break; } /* switch */ if(ch == '~') break; } /* while */ /* ewentualne zamkniecie hasla */ if(len > 0) /* zamykamy slowo kluczowe (just in case) */ { if(kind == 1) topic->link_len[links++]=len; else if(kind == 2) len_wyr[nr_wyr++]=len; len=kind=0; } /* add the NULL to the constructed string */ #if defined(vax11c) || defined(__DECC) topic->buf[l++] = '\0'; #else topic->buf[l++] = NULL; #endif /* vax11c */ topic->links = links; /* store the number of links */ topic->link_selected= False; /* set initial - not selected */ topic->link_index = -1; /* link index (not set) */ topic->cursor_pos = 0L; /* starting cursor position */ /* wyroznienie niektorych wyrazow ..*| *OLD* for (i=0; ilinks; i++) { left = (XmTextPosition)htopic->link_pos[i]; right = (XmTextPosition) (htopic->link_pos[i] + htopic->link_len[i]); /**** underline the whole - NOT USED XmTextSetHighlight(text, (XmTextPosition)htopic->link_pos[i], (XmTextPosition) (htopic->link_pos[i] + htopic->link_len[i]), XmHIGHLIGHT_SECONDARY_SELECTED); ****/ /* highlight the brackets only but do that in the reverse */ XmTextSetHighlight(text, left-1, left, XmHIGHLIGHT_SELECTED); XmTextSetHighlight(text, right, right+1, XmHIGHLIGHT_SELECTED); } } /* a workaround procedure for Motif 1.1 (to switch off the highlighting) */ #ifdef _NO_PROTO static void hide_hypertext_links(text, htopic) Widget text; hypertext_topic *htopic; #else /* _NO_PROTO */ static void hide_hypertext_links(Widget text, hypertext_topic *htopic) #endif { int i; XmTextPosition left, right; /* switch off hypertext highlighting if in an appropriate place (needed only for Motif before Motif 1.2 !!!- buggy old Motif) */ for( i=0; i < htopic->links; i++) { left = (XmTextPosition)htopic->link_pos[i]; right = (XmTextPosition) (htopic->link_pos[i] + htopic->link_len[i]); /* restore the default bracket's colour */ XmTextSetHighlight(text, left-1, left, XmHIGHLIGHT_NORMAL); XmTextSetHighlight(text, right, right+1, XmHIGHLIGHT_NORMAL); } } /* hide_hypertext_links */ /***************************** HelpCB ************************************* ** ** Process help callbacks */ #ifdef _NO_PROTO void HelpCB (w, client_data, call_data) Widget w; XtPointer client_data; XtPointer call_data; #else /* _NO_PROTO */ void HelpCB (Widget w, XtPointer client_data, XtPointer call_data) #endif { int id = get_id_from_asdat(client_data); aseditWindowStruct *win = get_win_from_asdat(client_data); TurnWatchCursor(True); if(help_dialog == NULL) /* first create the help dialog */ { /* we create just one help dialog (common for all asedit windows); it reduces clutter on the display and reduces memory requirements for multiple editing windows session; */ help_dialog = CreateHyperTextHelp(toplevel, "help window", NULL, 0); } /* if it is not a first call save data from the previous topic for the Back Help step; */ if(hth.initialized) SaveBackHelpStep(&htopic); /* a special hack for the find and change dialogs help; the top widget is exactly the same in both cases so the only way to tell if the call actually comes from the change dialog box is to check if the new_text widget is managed or not; there is no easy and nice way of doing that **/ if(id == DIALOG_FIND_OR_CHANGE && (win->replace_dialog != NULL) && XtIsManaged(win->new_text)) id += 200; if(xmUseVersion < 1002) /* work around a bug in Motif before 1.2 */ hide_hypertext_links(hypertext_text, &htopic); get_hypertext_topic(id, &hth, &htopic); XmTextSetString(hypertext_text, (char *)htopic.buf); /* add the file string to the text widget */ show_hypertext_links(hypertext_text, &htopic); /* set the sensitivity of the Follow button */ XtSetSensitive(follow_help_button, False); asManageDialog (help_dialog); TurnWatchCursor(False); } /* HelpCB */ /***************************** HelpTextCB ************************************* ** ** Process callbacks from hypertext_text widget. ** actually only motionVerifyCallback & XmCR_ACTIVATE can happen; ** additionally this procedure is installed as a callback for follow_help_button ** activates the link (remember that the callback structure in that case ** IS NOT a TextVerifyCallbackStruct, the same is true for XmCR_ACTIVATE reason) */ #ifdef _NO_PROTO static void HelpTextCB (w, client_data, call_data) Widget w; XtPointer client_data; XtPointer call_data; #else /* _NO_PROTO */ static void HelpTextCB (Widget w, XtPointer client_data, XtPointer call_data) #endif { XmTextVerifyCallbackStruct *txt_data = (XmTextVerifyCallbackStruct *) call_data; /* this callback may be currently activated from a button as well so be careful and don't use improperly txt_data for the second reason of the callback */ long pos; /* in Motif XmTextPosition is defined as long */ Arg al[2]; int reason; /* reason for the callback */ int i; long link_startPos=0L, link_endPos=0L; long new_htopic_id; static Time timestamp_move; static Boolean setting_selection = False; /* !! special flag for recursive calls */ int id = get_id_from_asdat(client_data); aseditWindowStruct *win = get_win_from_asdat(client_data); reason = txt_data->reason; switch(reason) { case XmCR_MODIFYING_TEXT_VALUE: /* should not happen */ break; case XmCR_VALUE_CHANGED: /* should not happen - the callback structure is illegal */ return; case XmCR_MOVING_INSERT_CURSOR: if(setting_selection ) return; /* it was a recursive call during setting a selection !!!!! return immediately ****/ break; /* link may be activated or by double click in the hypertext text widget or by pressing "Folow" button below that text widget !!!!! */ case XmCR_OK: case XmCR_ACTIVATE: if(htopic.link_selected) { /* activate the hypertext link; first calculate a new id using the old one and link_index; */ if(htopic.id < 10000000L) new_htopic_id = htopic.id * 100L + (long) htopic.link_index; else new_htopic_id = htopic.id + (long) htopic.link_index + 1L; if(hth.initialized) SaveBackHelpStep(&htopic); /* save data from previous topic for the Back Help step */ if(xmUseVersion < 1002) /* work around a bug in Motif before 1.2 */ hide_hypertext_links(hypertext_text, &htopic); get_hypertext_topic(new_htopic_id, &hth, &htopic); /* inside get_... htopic.link_selected was already set to False */ XmTextSetString(hypertext_text, (char *)htopic.buf); /* add the file string to the text widget */ show_hypertext_links(hypertext_text, &htopic); /* set the sensitivity of the Follow button */ if(xmUseVersion >= 1002) /* in SGI IRIX5, Motif 1.2.2 you can't set the current default button to insensitive, so first traverse to the next button for each Motif 1.2 implementation !!!*/ XmProcessTraversal(follow_help_button, XmTRAVERSE_NEXT); XtSetSensitive(follow_help_button, False); } return; default: /* an unknown client_data was received and there is no setup to handle this */ fprintf(stderr, "Warning: an unknown client_data in text callback\n"); fprintf(stderr,"Detected reason = %d \n", reason); return; } /* we got here - this callback was called from hypertext_text ( == w) and txt_data has its all values defined */ pos = txt_data->newInsert; htopic.cursor_pos = pos; /* store the cursor position */ switch(reason) { case XmCR_MODIFYING_TEXT_VALUE: break; case XmCR_MOVING_INSERT_CURSOR: /** if we do not allow resetting of the selection we will have all the time one link active (after first time selected) htopic.link_selected = False; *******/ /* make hypertext highlighting if in an appropriate place */ for( i=0; i < htopic.links; i++ ) { /* we accept both positon at the beggining and at the end of the link word (and of course inside it); this gives us a way of highlighting link using jumping to a next word in both directions */ if(pos >= htopic.link_pos[i] && pos < (htopic.link_pos[i] + htopic.link_len[i]) ) { /* we are just over the link word; set a logical link flag and set link_startPos, link_endPos; set the link_index */ htopic.link_selected = True; link_startPos = htopic.link_pos[i]; link_endPos = htopic.link_pos[i] + htopic.link_len[i]; htopic.link_index = i; /* set the sensitivity of the follow button */ XtSetSensitive(follow_help_button, True); /* set the timestamp needed to set selection (it is needed for the Search option as well ) */ /* we have to check if the event is different from NULL (very important on X11R4) */ if(txt_data->event) timestamp_move = txt_data->event->xkey.time; setting_selection = True; XmTextSetSelection(hypertext_text, link_startPos, link_endPos, CurrentTime); /* *USE* the CurrentTime */ /**** asedit 1.1x timestamp_move); ***/ /* above we use CurrentTime because on AIX 3.2.3e the old selections are not properly turned off; the CurrentTime solve the problem; we might possibly get rid off timestamp_move altogether (1.09.1993) */ setting_selection = False; break; } } break; } } /* HelpTextCB*/ #define TIGHTNESS 20 /* TIGHTNESS determines how closely the items are placed in the action area. The higher the value, the close together the PusButtons appear; the lower the value, the further apart they are. The value above (20) was chosen arbitrarily and purely for aesthetic reasons. */ /***************************** CreateActionArea ********************** ** create and return a Composite widget thet contains a number of ** PushButtons that are evenly distributed horizontally throughout the ** widget. The actions and number of actions are specified in the actions ** and num_actions parameters. The default button position is specified as ** a parameter. ** Procedure sets the kid widgets table (we might need one or few buttons ** to be accessible globally, for example to change their sensitivity etc.) */ #ifdef _NO_PROTO Widget CreateActionArea(parent, actions, num_actions, default_button_pos, kid ) Widget parent; ActionAreaItem *actions; int num_actions; int default_button_pos; Widget *kid; #else /* _NO_PROTO */ Widget CreateActionArea(Widget parent, ActionAreaItem *actions, int num_actions, int default_button_pos, Widget *kid ) #endif { Widget action_area, button; Arg al[15]; /* Arg List */ register int ac = 0; /* Arg Count */ int i; XtSetArg(al[ac], XmNleftOffset, 10); ac++; XtSetArg(al[ac], XmNrightOffset, 10); ac++; XtSetArg(al[ac], XmNfractionBase, TIGHTNESS*num_actions - 1); ac++; action_area= XmCreateForm ( parent, "action_area", al, ac ); for (i = 0; i < num_actions; i++) { ac = 0; XtSetArg(al[ac], XmNleftAttachment, i? XmATTACH_POSITION : XmATTACH_FORM); ac++; XtSetArg(al[ac], XmNleftPosition, TIGHTNESS*i); ac++; XtSetArg(al[ac], XmNtopAttachment, XmATTACH_FORM); ac++; XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_FORM); ac++; XtSetArg(al[ac], XmNrightAttachment, i != num_actions-1? XmATTACH_POSITION : XmATTACH_FORM); ac++; XtSetArg(al[ac], XmNrightPosition, TIGHTNESS*i + (TIGHTNESS-1)); ac++; XtSetArg(al[ac], XmNshowAsDefault, i == default_button_pos); ac++; #if (XmVersion > 1000) XtSetArg(al[ac], XmNdefaultButtonShadowThickness, 1); ac++; #endif button = XmCreatePushButton (action_area, actions[i].label, al, ac); XtManageChild(button); /* store that widget to be accessible in the calling procedure */ kid[i] = button; if (actions[i].callback) XtAddCallback(button, XmNactivateCallback, (XtCallbackProc)actions[i].callback, mk_asdat(actions[i].data) ); if (i == default_button_pos) { /* Set the action_area's default button to the first widget * created (or, make the index a parameter to the function * or have it be part of the data structure). Also, set the * pane window constraint for max and min heights so this * particular pane in the PanedWindow is not resizable. */ #if (XmVersion == 1000) short int mheight; #else Dimension mheight; #endif Dimension h; ac = 0; XtSetArg (al[ac], XmNmarginHeight, &mheight); ac++; XtGetValues(action_area, al, ac); ac = 0; XtSetArg (al[ac], XmNheight, &h); ac++; XtGetValues(button, al, ac); #if (XmVersion == 1000) /* do not increase h ... (just a Motif 1.0 bug) */ #else h += 2*mheight; #endif ac = 0; XtSetArg (al[ac], XmNdefaultButton, button); ac++; XtSetArg (al[ac], XmNpaneMaximum, h); ac++; XtSetArg (al[ac], XmNpaneMinimum, h); ac++; XtSetValues(action_area, al, ac); } } XtManageChild(action_area); return action_area; } /* When activate action happen in help text widget (Enter press or double click), respond by activating the follow_help_button in the action area as if the user had selected it. To make the procedure more general the button to activate is passed as a client data. The following procedure can be used only for X11R4 (XtCallActionProc was not available before) */ #if (XtSpecificationRelease > 3) #ifdef _NO_PROTO static void arm_and_activate_button(text_w, client_data, cbs) Widget text_w; XtPointer client_data; XmAnyCallbackStruct *cbs; #else /* _NO_PROTO */ static void arm_and_activate_button(Widget text_w, XtPointer client_data, XmAnyCallbackStruct *cbs) #endif { Widget button = (Widget)client_data; /* borrow the "event" field from cbs */ /* make the button think it got pushed. This causes * appropriate callback to be called, but XtCallActionProc() causes * the button appear to be activated as if the user selected it. * Call that procedure only when the button is sensitive !!!! * Just in case check if there is no NULL widget pass in (Should NEVER * happen, unless the client_data was set up incorrectly) */ if(button && XtIsSensitive(button)) XtCallActionProc(button, "ArmAndActivate", cbs->event, NULL, 0); } /* arm_and_activate_button */ #endif /* the following version of CreateHyperTextHelp set the action area labels directly (i.e. everything passed with arglist and argcount is ignored !!) I left the original declaration only for compatibility reasons with previous versions (i.e. CreateHyperTextHelp1 & CreateHyperTextHelp2). */ #ifdef _NO_PROTO Widget CreateHyperTextHelp(parent, name, arglist, argcount ) Widget parent; String name; Arg arglist[]; int argcount; #else /* _NO_PROTO */ Widget CreateHyperTextHelp( Widget parent, String name, Arg arglist[], int argcount ) #endif { Widget kid[10]; /* Children created in the action area */ Arg al[10]; /* Arg List */ register int ac = 0; /* Arg Count */ Dimension h_space, v_space; Widget pane, help_dialog, apply_button, index_button, action_area, scrolled_window; static ActionAreaItem action_items[] = { { "Follow", HelpTextCB, 0 }, { "Back", HelpBackCB, 0 }, { "Index", HelpCB, HTEXT_INDEX }, { "Close", DialogCancelCB, HTEXT_HELP }, { "Help", HelpCB, HTEXT_HELP }, }; /* following widgets are defined as global: hypertext_text - main hypertext widget (Text widget) follow_help_button - button to activate link (to be made sensitive or not) back_help_button - button to go back one step in help history */ XtSetArg(al[ac], XmNallowShellResize, TRUE); ac++; XtSetArg (al[ac], XmNdefaultPosition, False); ac++; help_dialog = XmCreateDialogShell ( parent, "help_dialog", al, ac ); /* Create a PanedWindow to manage the stuff in this dialog. This will * contain the control area (a ScrolledText widget) and the action area * (created by CreateActionArea() using the action_items defined above). */ ac = 0; XtSetArg(al[ac], XmNsashWidth, 1); ac++; XtSetArg(al[ac], XmNsashHeight,1); ac++; pane = XmCreatePanedWindow(help_dialog, "pane", al, ac); /* create a scrolled text inside paned window */ hypertext_text = CreateHelpScrolledText (pane); XtManageChild (hypertext_text); /* create the action area - default button -> Index (button 2)*/ action_area = CreateActionArea(pane, action_items, XtNumber(action_items), 2, kid); #if (XmVersion == 1000) XmAddTabGroup(hypertext_text); XmAddTabGroup(action_area); #endif /* get the button used to activate the hypertext help link and the button for back help steps */ follow_help_button = kid[0]; back_help_button = kid[1]; index_button = kid[2]; /* it is needed only in this procedure */ /* set the initial sensitivity of the Back button to False */ XtSetSensitive(back_help_button, False); /* the action_area was already fixed to its current height inside CreateActionArea -- we never let it resize !! */ /* ADD the activate callback - this callback is essential for hypertext help */ /** version 1 & 2 : XtAddCallback (hypertext_text, XmNactivateCallback, HelpTextCB, NULL); *** OLD (ver. 1 & 2)***/ /* lets register callback which will simulate pressing the follow_help_button; the "true" callback that we are really interested is actually registered for the button which we "press" by calling arm_and_activate_button (in this case it is specified in the action_items); use a follow_help_button as a client data for that callback; Unfortunately this is ONLY available for X11R4 upwards.... use the standard callback for X11R3... (without emulating button pressing) ***/ #if (XmVersion == 1000) XtAddCallback (hypertext_text, XmNactivateCallback, (XtCallbackProc)HelpTextCB, mk_asdat(0)); #else XtAddCallback (hypertext_text, XmNactivateCallback, (XtCallbackProc)arm_and_activate_button, (XtPointer)follow_help_button); #endif /* register helpCalback for the pane area; it will give us the right callback for both control and action areas when F1 (Motif Help) key is pressed; For X11R4 use a "clever" callback to emulate pressing the Help button in the action area */ #if (XmVersion == 1000) XtAddCallback (pane ,XmNhelpCallback, (XtCallbackProc)HelpCB, mk_asdat(HTEXT_HELP) ); #else XtAddCallback (pane ,XmNhelpCallback, (XtCallbackProc)arm_and_activate_button, (XtPointer)kid[4]); #endif /**** ??????? HOW to SET ESCAPE key to unmanage this dialog ???? */ /* set a special callback to set the focus to the index button in the action area when the dialog gets its first focus; */ /** the following focusCallback trick does not work for my DEC's !!! so we are making conditional compilation for all such calls (full discussion - see comments in the asedit.c) **/ #ifndef XFOCUS_BUG XtAddCallback(hypertext_text, XmNfocusCallback, (XtCallbackProc)focusCB, (XtPointer)index_button); #endif /* turn off traversal on the sash */ TurnOffSashTraversal(pane); { Dimension width, height; /* initially position the dialog in the middle of the screen */ ac = 0; XtSetArg(al[ac], XmNwidth, &width); ac++; XtSetArg(al[ac], XmNheight, &height); ac++; XtGetValues (pane, al, ac); ac = 0; XtSetArg(al[ac], XmNx, (int)(WidthOfScreen(XtScreen(toplevel))-width)/2); ac++; XtSetArg(al[ac], XmNy, (int)(HeightOfScreen(XtScreen(toplevel))-height)/2); ac++; XtSetValues (help_dialog, al, ac); } return(pane); } /* CreateHyperTextHelp */ /***************************** CreateHelpScrolledText ************************** ** ** Create Help Scrolled Text widget and set the colour of recessed windows. */ #ifdef _NO_PROTO static Widget CreateHelpScrolledText (parent) Widget parent; #else /* _NO_PROTO */ static Widget CreateHelpScrolledText (Widget parent) #endif { Arg al[15]; /* arg list */ register int ac; /* arg count */ Widget text; /* create scrolled text widget */ ac = 0; XtSetArg (al[ac], XmNresizeWidth, False); ac++; XtSetArg (al[ac], XmNresizeHeight, False); ac++; XtSetArg (al[ac], XmNscrollVertical, True); ac++; XtSetArg (al[ac], XmNscrollHorizontal, False); ac++; XtSetArg (al[ac], XmNeditMode, XmMULTI_LINE_EDIT); ac++; XtSetArg (al[ac], XmNeditable, False); ac++; XtSetArg (al[ac], XmNwordWrap, True); ac++; text = XmCreateScrolledText (parent, "hypertext_text", al, ac); /* add motion callback */ XtAddCallback (text, XmNmotionVerifyCallback, (XtCallbackProc)HelpTextCB, mk_asdat(0)); if(lstr.useOldColorSetup) { Pixel foreground, background, background1; /* used for a proper set up of mono screens */ /* get the standard colour of the scrolled background */ ac = 0; XtSetArg(al[ac], XmNbackground, &background1); ac++; XtSetArg(al[ac], XmNforeground, &foreground); ac++; XtGetValues(text, al, ac); /* Set colours of a recessed vertical scrollbar We are doing that only for screens supporting more than 16 colours (theoritically it should be done if the colour display is available, but practically if we have less than 16 colours all of them probably have been already used up and the nearest available colour which will be assigned maybe inappropriate )*/ if (DefaultDepthOfScreen(XDefaultScreenOfDisplay(display)) > 4) { Widget scrolled_window; Widget vsbar; /* ScrollBar */ XrmValue pixel_data; scrolled_window = XtParent(text); XtSetArg (al[ac], XmNverticalScrollBar, &vsbar); ac++; XtGetValues (scrolled_window, al, ac); /* get the scroll bar */ /* get a colour to be used for the scrollbar and remember it */ _XmSelectColorDefault (scrolled_window, 0, &pixel_data); background = *((Pixel *) pixel_data.addr); /* now check if the background is not by chance identical with the foreground (it happens on mono screens or for a black/white setup); if so do not apply it, but use the standard background1. */ if(background == foreground) background = background1; XtSetArg (al[0], XmNbackground, background); XtSetValues (vsbar, al, 1); } } /* useOldColorSetup */ return(text); } /* CreateHelpScrolledText */ /* include private Sash header file - see below why */ #include #ifdef _NO_PROTO void TurnOffSashTraversal(pane) Widget pane; #else /* _NO_PROTO */ void TurnOffSashTraversal(Widget pane) #endif { /* procedure turn off XmtraversalOn on all the sashes in a PanedWindow; the PanedWindow sashes are widgets that are not described or defined publicly (therefore they are not technically supported). Because of that we have to include a private header file SashP.h. I do not now any other way of setting off the traversal on sashes. Probably there isn't any because internals of the PanedWindow widget hard-code its Sash widgets' XmNtraversalOn resource to True as they are created ! This procedure works only for X11R4 (XmNchildren & XmNnumchildren are not defined in X11R3). On the other hand I do not need this procedure in X11R3 because traversal mechanism is set by the user. */ #if (XmVersion > 1000) Widget *children; int n_children=0; Arg al[3]; register int ac = 0; XtSetArg( al[ac], XmNchildren, &children); ac++; XtSetArg( al[ac], XmNnumChildren, &n_children); ac++; XtGetValues( pane, al, ac); while(n_children-- > 0) { if(XmIsSash(children[n_children])) { ac = 0; XtSetArg( al[0], XmNtraversalOn, False); ac++; XtSetValues(children[n_children], al, ac); } } #endif } /* TurnOffSashTraversal */ #ifdef _NO_PROTO static void push_backhelp_stack(s, el) BackHelpStack *s; BackHelpPtr el; #else /* _NO_PROTO */ static void push_backhelp_stack(BackHelpStack *s, BackHelpPtr el) #endif { /* push an element on the back help stack (on its top); the stack has predefined maximum length (BackHelpSTACK_SIZE); if the stack is filled up one element from the bottom of the stack is destroyed, address of the bottom is moved up so we get one space on the stack. In fact it is not a classic stack which I implemented here but this "cyclic" version gives an easy way of restoring last BackHelpSTACK_SIZE help topics; because of the possibility of moving of the stack bottom the address of any element on the stack must be calculated "modulo" BackHelpSTACK_SIZE; */ s->el[s->top] = el; s->top = (s->top + 1) % BackHelpSTACK_SIZE; /* check if the stack is used up completely; if so destroy the oldest element (i.e. from the bottom of the stack) and move the address of the bottom */ if(s->bottom == s->top) { s->bottom = (s->bottom + 1) % BackHelpSTACK_SIZE; } } /* push_backhelp_stack */ #ifdef _NO_PROTO static void pop_backhelp_stack(s, el) BackHelpStack *s; BackHelpPtr *el; #else /* _NO_PROTO */ static void pop_backhelp_stack(BackHelpStack *s, BackHelpPtr *el) #endif { /* pop the element from the top of the backhelp stack s */ if(s->bottom != s->top) { s->top --; if(s->top < 0) s->top += BackHelpSTACK_SIZE; /* "cyclic" stack */ *el = s->el[s->top]; /* moved here to avoid checking twice for sign of s->topp -- */ } else *el = NULL; /* empty stack */ } /* pop_backhelp_stack */ #ifdef _NO_PROTO static void flush_backhelp_stack(s) BackHelpStack *s; #else /* _NO_PROTO */ static void flush_backhelp_stack(BackHelpStack *s) #endif { /* flush the backhelp stack pointed by s */ int i, j, n; n = s->top - s->bottom; if(n < 0) n += BackHelpSTACK_SIZE; /* "cyclic" stack */ for(i=0; itop -i -1; if(j < 0) j += BackHelpSTACK_SIZE; } s->top = s->bottom; } /* flush_backhelp_stack */ #ifdef _NO_PROTO static void reset_backhelp() #else /* _NO_PROTO */ static void reset_backhelp(void) #endif { /* resets backhelp flushing the bhelp stack */ flush_backhelp_stack(&bhelp_stack); } /* reset_backhelp */ #ifdef _NO_PROTO static void SaveBackHelpStep(topic) hypertext_topic *topic; #else /* _NO_PROTO */ static void SaveBackHelpStep(hypertext_topic *topic) #endif { /* save the important elements of hypertext topic for back help steps */ BackHelpPtr undo_el; /* el for undo operation */ /* to speed the process of going back we preallocate a buffer for backhelp_history, with exactly the same length as BackHelpSTACK_SIZE; this get rid of permanent allocation and deallocation of memory for every backhelp action; */ static BackHelpEl backhelp_history[BackHelpSTACK_SIZE]; static Boolean backhelp_history_initialized = False; int i; /* when you start backhelp allocate memory for elements (if any) in backhelp_history */ if(!backhelp_history_initialized) { backhelp_history_initialized = True; for(i=0; i < BackHelpSTACK_SIZE; i++) ; /* nothing to allocate in BackHelpEl-ements */ } /* the undo_el is a pointer to an element in backhelp_history, the element in backhelp_history has the same index as the top element on the bhelp_stack (which has a "cyclic" nature) */ undo_el = &backhelp_history[bhelp_stack.top]; /* exctract the data from hypertext topic and set the undo_el values */ undo_el->id = topic->id; undo_el->cursor_pos = topic->cursor_pos; undo_el->link_selected = topic->link_selected; undo_el->link_index = topic->link_index; /* push the undo data on the undo stack ... */ push_backhelp_stack(&bhelp_stack, undo_el); /* set sensitivity to the back_help button */ XtSetSensitive(back_help_button, True); } /* SaveBackHelpStep */ /***************************** HelpBackCB ************************************* ** ** Process help Back callbacks (makes one step back in hypertext help) */ #ifdef _NO_PROTO static void HelpBackCB (w, client_data, call_data) Widget w; XtPointer client_data; XtPointer call_data; #else /* _NO_PROTO */ static void HelpBackCB (Widget w, XtPointer client_data, XtPointer call_data) #endif { BackHelpPtr el=NULL; long position =0L; int id = get_id_from_asdat(client_data); aseditWindowStruct *win = get_win_from_asdat(client_data); /* in the current version we always pop one element from the backhelp stack; if we would like to implement showing back the topic which was closed probably the best idea would be to check if the help is hown or not; if not shown - just Manage the dialog; otherwise follow as below ... ***/ pop_backhelp_stack(&bhelp_stack, &el); if(bhelp_stack.bottom == bhelp_stack.top) /* last element was popped... */ { if(xmUseVersion >= 1002) /* in SGI IRIX5, Motif 1.2.2 you can't set the current default button to insensitive, so first traverse to the next button for each Motif 1.2 implementation !!!*/ XmProcessTraversal(back_help_button, XmTRAVERSE_NEXT); XtSetSensitive(back_help_button, False); XFlush(display); /* sensitivity must be set before next keys are processed, otherwise with a busy system we could "skip over" the bottom of the stack and all track would be lost !!! */ } if(el == NULL) return; /* should not happen, we are doing this just in case. If it did happen it would mean that the undo/redo was called when the appropriate stack was empty, i.e. the sensitivity of the appropriate button was set improperly (but now would set correctly) */ /* finally do the appropriate back help operation .... */ if(xmUseVersion < 1002) /* work around a bug in Motif before 1.2 */ hide_hypertext_links(hypertext_text, &htopic); get_hypertext_topic(el->id, &hth, &htopic); XmTextSetString(hypertext_text, (char *)htopic.buf); /* add the file string to the text widget */ show_hypertext_links(hypertext_text, &htopic); /* set the sensitivity of the Follow button */ XtSetSensitive(follow_help_button, False); /* now if the link was selected get the link_index to find the position of the link word (help keyword) and position there the insertion point; the HelpTextCB will highlight the keyword and set sensitivity to the follow button */ if(el->link_selected) { /* just in case check if everything is well (we don't go outside the legal range); I do not see when it could happen but ... */ if(el->link_index < htopic.links) position = htopic.link_pos[el->link_index]; XmTextSetInsertionPosition(hypertext_text, position); } /* finally position the cursor when it was just before the next Help call happened */ position = el->cursor_pos; /* ????? does this destroy the highlighting ???? */ XmTextSetInsertionPosition(hypertext_text, position); asManageDialog (help_dialog); /* ?? it's probably already managed !! */ } /* HelpBackCB */