- Change help version to 5.4DEV
[nedit.git] / source / help.c
blob289cd6c07a2657ae6cda2d3f1b74b1ee8b7ce1e3
1 static const char CVSID[] = "$Id: help.c,v 1.87 2002/11/13 21:58:24 tringali Exp $";
2 /*******************************************************************************
3 * *
4 * help.c -- Nirvana Editor help display *
5 * *
6 * Copyright (C) 1999 Mark Edel *
7 * *
8 * This is free software; you can redistribute it and/or modify it under the *
9 * terms of the GNU General Public License as published by the Free Software *
10 * Foundation; either version 2 of the License, or (at your option) any later *
11 * version. *
12 * *
13 * This software is distributed in the hope that it will be useful, but WITHOUT *
14 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or *
15 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License *
16 * for more details. *
17 * *
18 * You should have received a copy of the GNU General Public License along with *
19 * software; if not, write to the Free Software Foundation, Inc., 59 Temple *
20 * Place, Suite 330, Boston, MA 02111-1307 USA *
21 * *
22 * Nirvana Text Editor *
23 * September 10, 1991 *
24 * *
25 * Written by Mark Edel, mostly rewritten by Steve Haehn for new help system, *
26 * December, 2001 *
27 * *
28 *******************************************************************************/
30 #ifdef HAVE_CONFIG_H
31 #include "../config.h"
32 #endif
34 #include "help.h"
35 #include "textBuf.h"
36 #include "text.h"
37 #include "textP.h"
38 #include "textDisp.h"
39 #include "textSel.h"
40 #include "nedit.h"
41 #include "search.h"
42 #include "window.h"
43 #include "preferences.h"
44 #include "help_data.h"
45 #include "file.h"
46 #include "highlight.h"
47 #include "../util/misc.h"
48 #include "../util/DialogF.h"
49 #include "../util/system.h"
51 #include <stdlib.h>
52 #include <stdio.h>
53 #include <string.h>
54 #ifdef VMS
55 #include "../util/VMSparam.h"
56 #else
57 #ifndef __MVS__
58 #include <sys/param.h>
59 #endif
60 #endif /*VMS*/
62 #include <Xm/Xm.h>
63 #include <Xm/XmP.h> /* These are for applying style info to help text */
64 #include <Xm/Form.h>
65 #include <Xm/PrimitiveP.h>
66 #include <Xm/ScrolledW.h>
67 #include <Xm/ScrollBar.h>
68 #include <Xm/PushB.h>
69 #ifdef EDITRES
70 #include <X11/Xmu/Editres.h>
71 /* extern void _XEditResCheckMessages(); */
72 #endif /* EDITRES */
74 #ifdef HAVE_DEBUG_H
75 #include "../debug.h"
76 #endif
78 /*============================================================================*/
79 /* SYMBOL DEFINITIONS */
80 /*============================================================================*/
82 #define EOS '\0' /* end-of-string character */
84 #define CLICK_THRESHOLD 5 /* number of pixels mouse may move from its */
85 /* pressed location for mouse-up to be */
86 /* considered a valid click (as opposed to */
87 /* a drag or mouse-pick error) */
89 /*============================================================================*/
90 /* VARIABLE DECLARATIONS */
91 /*============================================================================*/
93 static Widget HelpWindows[NUM_TOPICS] = {NULL};
94 static Widget HelpTextPanes[NUM_TOPICS] = {NULL};
95 static textBuffer *HelpStyleBuffers[NUM_TOPICS] = {NULL};
96 static int navHistForw[NUM_TOPICS];
97 static int navHistBack[NUM_TOPICS];
99 /* Information on the last search for search-again */
100 static char LastSearchString[DF_MAX_PROMPT_LENGTH] = "";
101 static int LastSearchTopic = -1;
102 static int LastSearchPos = 0;
103 static int LastSearchWasAllTopics = False;
105 /* Fonts for each help text style generated by the help generator (setext).
106 The NEdit text widget uses the first help style, 'A', to calculate window
107 width, so making 'A' a fixed font will yield window widths calibrated to
108 match width-dependent fixed font layouts in the help text. */
109 static enum helpFonts StyleFonts[] =
111 /* Fixed fonts, styles: 'A', 'B', 'C', 'D' */
112 FIXED_HELP_FONT, BOLD_FIXED_HELP_FONT, BOLD_FIXED_HELP_FONT,
113 BOLD_ITALIC_FIXED_HELP_FONT,
115 /* Underlined fixed fonts, styles: 'E', 'F', 'G', 'H' */
116 FIXED_HELP_FONT, BOLD_FIXED_HELP_FONT, BOLD_FIXED_HELP_FONT,
117 BOLD_ITALIC_FIXED_HELP_FONT,
119 /* Normal (proportional) fonts, styles: 'I', 'J', 'K', 'L' */
120 HELP_FONT, BOLD_HELP_FONT, ITALIC_HELP_FONT, BOLD_ITALIC_HELP_FONT,
122 /* Underlined fonts, styles: 'M', 'N', 'O', 'P' */
123 HELP_FONT, BOLD_HELP_FONT, ITALIC_HELP_FONT, BOLD_ITALIC_HELP_FONT,
125 /* Link font, style: 'Q' */
126 HELP_FONT,
128 /* Heading fonts, styles: 'R', 'S', 'T' */
129 H1_HELP_FONT, H2_HELP_FONT, H3_HELP_FONT
132 static int StyleUnderlines[] =
134 /* Fixed fonts, styles: 'A', 'B', 'C', 'D' */
135 False, False, False, False,
137 /* Underlined fixed fonts, styles: 'E', 'F', 'G', 'H' */
138 True, True, True, True,
140 /* Normal (proportional) fonts, styles: 'I', 'J', 'K', 'L' */
141 False, False, False, False,
143 /* Underlined fonts, styles: 'M', 'N', 'O', 'P' */
144 True, True, True, True,
146 /* Link font, style: 'Q' */
147 True,
149 /* Heading fonts, styles: 'R', 'S', 'T' */
150 False, False, False
153 #define N_STYLES (XtNumber(StyleFonts))
155 static styleTableEntry HelpStyleInfo[ N_STYLES ];
157 /*============================================================================*/
158 /* PROGRAM PROTOTYPES */
159 /*============================================================================*/
161 static Widget createHelpPanel(Widget parent, int topic);
162 static void dismissCB(Widget w, XtPointer clientData, XtPointer callData);
163 static void prevTopicCB(Widget w, XtPointer clientData, XtPointer callData);
164 static void nextTopicCB(Widget w, XtPointer clientData, XtPointer callData);
165 static void bwHistoryCB(Widget w, XtPointer clientData, XtPointer callData);
166 static void fwHistoryCB(Widget w, XtPointer clientData, XtPointer callData);
167 static void searchHelpCB(Widget w, XtPointer clientData, XtPointer callData);
168 static void searchHelpAgainCB(Widget w, XtPointer clientData,
169 XtPointer callData);
170 static void printCB(Widget w, XtPointer clientData, XtPointer callData);
171 static char *stitch(Widget parent, char **string_list,char **styleMap);
172 static void searchHelpText(Widget parent, int parentTopic,
173 const char *searchFor, int allSections, int startPos, int startTopic);
174 static void changeWindowTopic(int existingTopic, int newTopic);
175 static int findTopicFromShellWidget(Widget shellWidget);
176 static void loadFontsAndColors(Widget parent, int style);
177 static void initNavigationHistory(void);
179 #ifdef HAVE__XMVERSIONSTRING
180 extern char _XmVersionString[];
181 #else
182 static char _XmVersionString[] = "unknown";
183 #endif
185 /*============================================================================*/
186 /*================================= PROGRAMS =================================*/
187 /*============================================================================*/
190 ** Create a string containing information on the build environment. Returned
191 ** string must NOT be freed by caller.
194 static char *bldInfoString = NULL;
196 static void freeBuildInfo(void)
198 /* This keeps memory leak detectors happy */
199 XtFree(bldInfoString);
202 static const char *getBuildInfo(void)
204 const char * bldFormat =
205 "%s\n"
206 " Built on: %s, %s, %s\n"
207 " Built at: %s, %s\n"
208 " With Motif: %d.%d.%d [%s]\n"
209 "Running Motif: %d.%d [%s]\n"
210 " Server: %s %d\n"
211 " Visual: %s\n"
213 const char * visualClass[] = {"StaticGray", "GrayScale",
214 "StaticColor", "PseudoColor",
215 "TrueColor", "DirectColor"};
216 if (bldInfoString==NULL)
218 char visualStr[30]="";
219 if (TheDisplay) {
220 Visual *visual;
221 int depth;
222 Colormap map;
223 Boolean usingDefaultVisual = FindBestVisual(TheDisplay, APP_NAME,
224 APP_CLASS, &visual,
225 &depth, &map);
226 sprintf(visualStr,"%d-bit %s (ID %#lx%s)",
227 depth,
228 visualClass[visual->class],
229 visual->visualid,
230 usingDefaultVisual ? ", Default" : "");
232 bldInfoString = XtMalloc (strlen (bldFormat) + 1024);
233 sprintf(bldInfoString, bldFormat,
234 NEditVersion,
235 COMPILE_OS, COMPILE_MACHINE, COMPILE_COMPILER,
236 linkdate, linktime,
237 XmVERSION, XmREVISION, XmUPDATE_LEVEL,
238 XmVERSION_STRING,
239 xmUseVersion/1000, xmUseVersion%1000,
240 _XmVersionString,
241 ServerVendor(TheDisplay), VendorRelease(TheDisplay),
242 visualStr);
244 atexit(freeBuildInfo);
247 return bldInfoString;
251 ** Initialization for help system data, needs to be done only once.
253 static void initHelpStyles (Widget parent)
255 static int styleTableInitialized = False;
257 if (! styleTableInitialized)
259 Pixel black = BlackPixelOfScreen(XtScreen(parent));
260 int styleIndex;
261 char ** line;
263 for (styleIndex = 0; styleIndex < STL_HD + MAX_HEADING; styleIndex++)
265 HelpStyleInfo[ styleIndex ].color = black;
266 HelpStyleInfo[ styleIndex ].underline = StyleUnderlines[styleIndex];
267 HelpStyleInfo[ styleIndex ].font = NULL;
270 styleTableInitialized = True;
272 /*-------------------------------------------------------
273 * Only attempt to add build information to version text
274 * when string formatting symbols are present in the text.
275 * This special case is needed to incorporate this
276 * dynamically created information into the static help.
277 *-------------------------------------------------------*/
278 for (line = HelpText[ HELP_VERSION ]; *line != NULL; line++)
280 /*--------------------------------------------------
281 * If and when this printf format is found in the
282 * version help text, replace that line with the
283 * build information. Then stitching the help text
284 * will have the final count of characters to use.
285 *--------------------------------------------------*/
286 if (strstr (*line, "%s") != NULL)
288 const char * bldInfo = getBuildInfo();
289 char * text = XtMalloc (strlen (*line) + strlen (bldInfo));
290 sprintf (text, *line, bldInfo);
291 *line = text;
292 break;
299 ** Help fonts are not loaded until they're actually needed. This function
300 ** checks if the style's font is loaded, and loads it if it's not.
302 static void loadFontsAndColors(Widget parent, int style)
304 XFontStruct *font;
305 int r,g,b;
306 if (HelpStyleInfo[style - STYLE_PLAIN].font == NULL) {
307 font = XLoadQueryFont(XtDisplay(parent),
308 GetPrefHelpFontName(StyleFonts[style - STYLE_PLAIN]));
309 if (font == NULL) {
310 fprintf(stderr, "NEdit: help font, %s, not available\n",
311 GetPrefHelpFontName(StyleFonts[style - STYLE_PLAIN]));
312 font = XLoadQueryFont(XtDisplay(parent), "fixed");
313 if (font == NULL) {
314 fprintf(stderr, "NEdit: fallback help font, \"fixed\", not "
315 "available, cannot continue\n");
316 exit(EXIT_FAILURE);
319 HelpStyleInfo[style - STYLE_PLAIN].font = font;
320 if (style == STL_NM_LINK)
321 HelpStyleInfo[style - STYLE_PLAIN].color =
322 AllocColor(parent, GetPrefHelpLinkColor(), &r, &g, &b);
326 static void adaptNavigationButtons(int topic) {
327 Widget btn;
329 if(HelpWindows[topic] == NULL)
330 return; /* Shouldn't happen */
332 btn=XtNameToWidget(HelpWindows[topic], "helpForm.prevTopic");
333 if(btn) {
334 if(topic > 0)
335 XtSetSensitive(btn, True);
336 else
337 XtSetSensitive(btn, False);
340 btn=XtNameToWidget(HelpWindows[topic], "helpForm.nextTopic");
341 if(btn) {
342 if(topic < (NUM_TOPICS - 1))
343 XtSetSensitive(btn, True);
344 else
345 XtSetSensitive(btn, False);
348 btn=XtNameToWidget(HelpWindows[topic], "helpForm.histBack");
349 if(btn) {
350 if(navHistBack[topic] != -1)
351 XtSetSensitive(btn, True);
352 else
353 XtSetSensitive(btn, False);
356 btn=XtNameToWidget(HelpWindows[topic], "helpForm.histForw");
357 if(btn) {
358 if(navHistForw[topic] != -1)
359 XtSetSensitive(btn, True);
360 else
361 XtSetSensitive(btn, False);
367 ** Put together stored help strings to create the text and optionally the style
368 ** information for a given help topic.
370 static char * stitch (
372 Widget parent, /* used for dynamic font/color allocation */
373 char ** string_list, /* given help strings to stitch together */
374 char ** styleMap /* NULL, or a place to store help styles */
377 char * cp;
378 char * section, * sp; /* resulting help text section */
379 char * styleData, * sdp; /* resulting style data for text */
380 char style = STYLE_PLAIN; /* start off each section with this style */
381 int total_size = 0; /* help text section size */
382 char ** crnt_line;
384 /*----------------------------------------------------
385 * How many characters are there going to be displayed?
386 *----------------------------------------------------*/
387 for (crnt_line = string_list; *crnt_line != NULL; crnt_line++)
389 for (cp = *crnt_line; *cp != EOS; cp++)
391 /*---------------------------------------------
392 * The help text has embedded style information
393 * consisting of the style marker and a single
394 * character style, for a total of 2 characters.
395 * This style information is not to be included
396 * in the character counting below.
397 *---------------------------------------------*/
398 if (*cp != STYLE_MARKER) {
399 total_size++;
401 else {
402 cp++; /* skipping style marker, loop will handle style */
407 /*--------------------------------------------------------
408 * Get the needed space, one area for the help text being
409 * stitched together, another for the styles to be applied.
410 *--------------------------------------------------------*/
411 sp = section = XtMalloc (total_size +1);
412 sdp = styleData = (styleMap) ? XtMalloc (total_size +1) : NULL;
413 *sp = EOS;
415 /*--------------------------------------------
416 * Fill in the newly acquired contiguous space
417 * with help text and style information.
418 *--------------------------------------------*/
419 for (crnt_line = string_list; *crnt_line != NULL; crnt_line++)
421 for (cp = *crnt_line; *cp != EOS; cp++)
423 if (*cp == STYLE_MARKER) {
424 style = *(++cp);
425 loadFontsAndColors(parent, style);
427 else {
428 *(sp++) = *cp;
430 if (styleMap)
431 *(sdp++) = style;
436 *sp = EOS;
438 /*-----------------------------------------
439 * Only deal with style map, when available.
440 *-----------------------------------------*/
441 if (styleMap) {
442 *styleMap = styleData;
443 *sdp = EOS;
446 return section;
450 ** Display help for subject "topic". "parent" is a widget over which the help
451 ** dialog may be posted. Help dialogs are preserved when popped down by the
452 ** user, and may appear posted over a previous parent, regardless of the parent
453 ** argument.
455 void Help(Widget parent, enum HelpTopic topic)
457 if (HelpWindows[topic] != NULL)
458 RaiseShellWindow(HelpWindows[topic]);
459 else
460 HelpWindows[topic] = createHelpPanel(parent, topic);
461 adaptNavigationButtons(topic);
465 /* Setup Window/Icon title for the help window. */
466 static void setHelpWinTitle(Widget win, enum HelpTopic topic)
468 char * buf, *topStr=HelpTitles[topic];
470 buf=malloc(strlen(topStr) + 24);
471 topic++;
473 sprintf(buf, "NEdit Help (%d)", (int)topic);
474 XtVaSetValues(win, XmNiconName, buf, NULL);
476 sprintf(buf, "NEdit Help: %s (%d)", topStr, (int)topic);
477 XtVaSetValues(win, XmNtitle, buf, NULL);
479 free(buf);
483 ** Create a new help window displaying a given subject, "topic"
485 ** Importand hint: If this widget is restructured or the name of the text
486 ** subwidget is changed don't forget to adapt the default translations of the
487 ** help text. They are located in nedit.c, look for
488 ** static char *fallbackResources
489 ** (currently: nedit.helpForm.sw.helpText*translations...)
491 static Widget createHelpPanel(Widget parent, int topic)
493 Arg al[50];
494 int ac;
495 Widget appShell, btn, dismissBtn, form, btnFW;
496 Widget sw, hScrollBar, vScrollBar;
497 XmString st1;
498 char * helpText = NULL;
499 char * styleData = NULL;
501 ac = 0;
502 XtSetArg(al[ac], XmNdeleteResponse, XmDO_NOTHING); ac++;
503 appShell = CreateShellWithBestVis(APP_NAME, APP_CLASS,
504 applicationShellWidgetClass, TheDisplay, al, ac);
505 AddSmallIcon(appShell);
506 /* With openmotif 2.1.30, a crash may occur when the text widget of the
507 help window is (slowly) resized to a zero width. By imposing a
508 minimum _window_ width, we can work around this problem. The minimum
509 width should be larger than the width of the scrollbar. 50 is probably
510 a safe value; this leaves room for a few characters */
511 XtVaSetValues(appShell, XtNminWidth, 50, NULL);
512 form = XtVaCreateManagedWidget("helpForm", xmFormWidgetClass, appShell,
513 NULL);
514 XtVaSetValues(form, XmNshadowThickness, 0, NULL);
516 /* Create the bottom row of buttons */
517 btn = XtVaCreateManagedWidget("find", xmPushButtonWidgetClass, form,
518 XmNlabelString, st1=XmStringCreateSimple("Find..."),
519 XmNmnemonic, 'F',
520 XmNbottomAttachment, XmATTACH_FORM,
521 XmNleftAttachment, XmATTACH_POSITION,
522 XmNleftPosition, 3,
523 XmNrightAttachment, XmATTACH_POSITION,
524 XmNrightPosition, 25, NULL);
525 XtAddCallback(btn, XmNactivateCallback, searchHelpCB, appShell);
526 XmStringFree(st1);
528 btn = XtVaCreateManagedWidget("findAgain", xmPushButtonWidgetClass, form,
529 XmNlabelString, st1=XmStringCreateSimple("Find Again"),
530 XmNmnemonic, 'A',
531 XmNbottomAttachment, XmATTACH_FORM,
532 XmNleftAttachment, XmATTACH_POSITION,
533 XmNleftPosition, 27,
534 XmNrightAttachment, XmATTACH_POSITION,
535 XmNrightPosition, 49, NULL);
536 XtAddCallback(btn, XmNactivateCallback, searchHelpAgainCB, appShell);
537 XmStringFree(st1);
539 btn = XtVaCreateManagedWidget("print", xmPushButtonWidgetClass, form,
540 XmNlabelString, st1=XmStringCreateSimple("Print..."),
541 XmNmnemonic, 'P',
542 XmNbottomAttachment, XmATTACH_FORM,
543 XmNleftAttachment, XmATTACH_POSITION,
544 XmNleftPosition, 51,
545 XmNrightAttachment, XmATTACH_POSITION,
546 XmNrightPosition, 73, NULL);
547 XtAddCallback(btn, XmNactivateCallback, printCB, appShell);
548 XmStringFree(st1);
550 dismissBtn = XtVaCreateManagedWidget("dismiss", xmPushButtonWidgetClass,
551 form, XmNlabelString, st1=XmStringCreateSimple("Dismiss"),
552 XmNbottomAttachment, XmATTACH_FORM,
553 XmNleftAttachment, XmATTACH_POSITION,
554 XmNleftPosition, 75,
555 XmNrightAttachment, XmATTACH_POSITION,
556 XmNrightPosition, 97, NULL);
557 XtAddCallback(dismissBtn, XmNactivateCallback, dismissCB, appShell);
558 XmStringFree(st1);
560 /* Create the next row of buttons (for navigation) */
561 btn = XtVaCreateManagedWidget("prevTopic", xmPushButtonWidgetClass,
562 form, XmNlabelString, st1=XmStringCreateSimple("<< Browse"),
563 XmNmnemonic, 'o',
564 XmNbottomAttachment, XmATTACH_WIDGET,
565 XmNbottomWidget, dismissBtn,
566 XmNleftAttachment, XmATTACH_POSITION,
567 XmNleftPosition, 51,
568 XmNrightAttachment, XmATTACH_POSITION,
569 XmNrightPosition, 73, NULL);
570 XtAddCallback(btn, XmNactivateCallback, prevTopicCB, appShell);
571 XmStringFree(st1);
573 btn = XtVaCreateManagedWidget("nextTopic", xmPushButtonWidgetClass, form,
574 XmNlabelString, st1=XmStringCreateSimple("Browse >>"),
575 XmNmnemonic, 'e',
576 XmNbottomAttachment, XmATTACH_WIDGET,
577 XmNbottomWidget, dismissBtn,
578 XmNleftAttachment, XmATTACH_POSITION,
579 XmNleftPosition, 75,
580 XmNrightAttachment, XmATTACH_POSITION,
581 XmNrightPosition, 97, NULL);
582 XtAddCallback(btn, XmNactivateCallback, nextTopicCB, appShell);
583 XmStringFree(st1);
585 btn = XtVaCreateManagedWidget("histBack", xmPushButtonWidgetClass, form,
586 XmNlabelString, st1=XmStringCreateSimple("Back"),
587 XmNmnemonic, 'B',
588 XmNbottomAttachment, XmATTACH_WIDGET,
589 XmNbottomWidget, dismissBtn,
590 XmNleftAttachment, XmATTACH_POSITION,
591 XmNleftPosition, 3,
592 XmNrightAttachment, XmATTACH_POSITION,
593 XmNrightPosition, 25, NULL);
594 XtAddCallback(btn, XmNactivateCallback, bwHistoryCB, appShell);
595 XmStringFree(st1);
597 btnFW = XtVaCreateManagedWidget("histForw", xmPushButtonWidgetClass, form,
598 XmNlabelString, st1=XmStringCreateSimple("Forward"),
599 XmNmnemonic, 'w',
600 XmNbottomAttachment, XmATTACH_WIDGET,
601 XmNbottomWidget, dismissBtn,
602 XmNleftAttachment, XmATTACH_POSITION,
603 XmNleftPosition, 27,
604 XmNrightAttachment, XmATTACH_POSITION,
605 XmNrightPosition, 49, NULL);
606 XtAddCallback(btnFW, XmNactivateCallback, fwHistoryCB, appShell);
607 XmStringFree(st1);
609 /* Create a text widget inside of a scrolled window widget */
610 sw = XtVaCreateManagedWidget("sw", xmScrolledWindowWidgetClass, form,
611 XmNshadowThickness, 2,
612 XmNtopAttachment, XmATTACH_FORM,
613 XmNleftAttachment, XmATTACH_FORM,
614 XmNrightAttachment, XmATTACH_FORM,
615 XmNbottomAttachment, XmATTACH_WIDGET,
616 XmNbottomWidget, btnFW, NULL);
617 hScrollBar = XtVaCreateManagedWidget("hScrollBar",
618 xmScrollBarWidgetClass, sw, XmNorientation, XmHORIZONTAL,
619 XmNrepeatDelay, 10, NULL);
620 vScrollBar = XtVaCreateManagedWidget("vScrollBar",
621 xmScrollBarWidgetClass, sw, XmNorientation, XmVERTICAL,
622 XmNrepeatDelay, 10, NULL);
623 HelpTextPanes[topic] = XtVaCreateManagedWidget("helpText",
624 textWidgetClass, sw, textNrows, 30, textNcolumns, 65,
625 textNbacklightCharTypes, NULL,
626 textNhScrollBar, hScrollBar, textNvScrollBar, vScrollBar,
627 textNreadOnly, True, textNcontinuousWrap, True,
628 textNautoShowInsertPos, True, NULL);
629 XtVaSetValues(sw, XmNworkWindow, HelpTextPanes[topic],
630 XmNhorizontalScrollBar, hScrollBar,
631 XmNverticalScrollBar, vScrollBar, NULL);
633 /* Initialize help style information, if it hasn't already been init'd */
634 initHelpStyles (parent);
636 /* Put together the text to display and separate it into parallel text
637 and style data for display by the widget */
638 helpText = stitch (parent, HelpText[topic], &styleData);
640 /* Stuff the text into the widget's text buffer */
641 BufSetAll (TextGetBuffer (HelpTextPanes[topic]) , helpText);
642 XtFree (helpText);
644 /* Create a style buffer for the text widget and fill it with the style
645 data which was generated along with the text content */
646 HelpStyleBuffers[topic] = BufCreate();
647 BufSetAll(HelpStyleBuffers[topic], styleData);
648 XtFree (styleData);
649 TextDAttachHighlightData(((TextWidget)HelpTextPanes[topic])->text.textD,
650 HelpStyleBuffers[topic], HelpStyleInfo, N_STYLES, '\0', NULL, NULL);
652 /* This shouldn't be necessary (what's wrong in text.c?) */
653 HandleXSelections(HelpTextPanes[topic]);
655 /* Process dialog mnemonic keys */
656 AddDialogMnemonicHandler(form, FALSE);
658 /* Set the default button */
659 XtVaSetValues(form, XmNdefaultButton, dismissBtn, NULL);
660 XtVaSetValues(form, XmNcancelButton, dismissBtn, NULL);
662 /* realize all of the widgets in the new window */
663 RealizeWithoutForcingPosition(appShell);
665 /* Give the text pane the initial focus */
666 XmProcessTraversal(HelpTextPanes[topic], XmTRAVERSE_CURRENT);
668 /* Make close command in window menu gracefully prompt for close */
669 AddMotifCloseCallback(appShell, (XtCallbackProc)dismissCB, appShell);
671 /* Initialize navigation information, if it hasn't already been init'd */
672 initNavigationHistory();
674 /* Set the window title */
675 setHelpWinTitle(appShell, topic);
678 #ifdef EDITRES
679 XtAddEventHandler (appShell, (EventMask)0, True,
680 (XtEventHandler)_XEditResCheckMessages, NULL);
681 #endif /* EDITRES */
683 return appShell;
686 static void changeTopicOrRaise(int existingTopic, int newTopic) {
687 if(HelpWindows[newTopic] == NULL) {
688 changeWindowTopic(existingTopic, newTopic);
689 adaptNavigationButtons(newTopic);
690 } else {
691 RaiseShellWindow(HelpWindows[newTopic]);
692 adaptNavigationButtons(existingTopic);
693 adaptNavigationButtons(newTopic);
699 ** Callbacks for window controls
701 static void dismissCB(Widget w, XtPointer clientData, XtPointer callData)
703 int topic;
705 if ((topic = findTopicFromShellWidget((Widget)clientData)) == -1)
706 return;
708 /* I don't understand the mechanism by which this can be called with
709 HelpWindows[topic] as NULL, but it has happened */
710 XtDestroyWidget(HelpWindows[topic]);
711 HelpWindows[topic] = NULL;
712 if (HelpStyleBuffers[topic] != NULL) {
713 BufFree(HelpStyleBuffers[topic]);
714 HelpStyleBuffers[topic] = NULL;
718 static void prevTopicCB(Widget w, XtPointer clientData, XtPointer callData)
719 { int topic;
721 if ((topic = findTopicFromShellWidget((Widget)clientData)) == -1)
722 return; /* shouldn't happen */
724 topic--;
725 if(topic >= 0)
726 changeTopicOrRaise(topic+1, topic);
729 static void nextTopicCB(Widget w, XtPointer clientData, XtPointer callData)
730 { int topic;
732 if ((topic = findTopicFromShellWidget((Widget)clientData)) == -1)
733 return; /* shouldn't happen */
735 topic++;
736 if(topic < NUM_TOPICS)
737 changeTopicOrRaise(topic-1, topic);
740 static void bwHistoryCB(Widget w, XtPointer clientData, XtPointer callData)
741 { int topic, goTo;
743 if ((topic = findTopicFromShellWidget((Widget)clientData)) == -1)
744 return; /* shouldn't happen */
746 goTo=navHistBack[topic];
747 if(goTo >= 0 && goTo < NUM_TOPICS) {
748 navHistForw[goTo]=topic;
749 changeTopicOrRaise(topic, goTo);
753 static void fwHistoryCB(Widget w, XtPointer clientData, XtPointer callData)
754 { int topic, goTo;
756 if ((topic = findTopicFromShellWidget((Widget)clientData)) == -1)
757 return; /* shouldn't happen */
759 goTo=navHistForw[topic];
760 if(goTo >= 0 && goTo < NUM_TOPICS) {
761 navHistBack[goTo]=topic;
762 changeTopicOrRaise(topic, goTo);
766 static void searchHelpCB(Widget w, XtPointer clientData, XtPointer callData)
768 char promptText[DF_MAX_PROMPT_LENGTH];
769 int response, topic;
770 static char **searchHistory = NULL;
771 static int nHistoryStrings = 0;
773 if ((topic = findTopicFromShellWidget((Widget)clientData)) == -1)
774 return; /* shouldn't happen */
775 SetDialogFPromptHistory(searchHistory, nHistoryStrings);
776 response = DialogF(DF_PROMPT, HelpWindows[topic], 3,
777 "Search for: (use up arrow key to recall previous)",
778 promptText, "This Section", "All Sections", "Cancel");
779 if (response == 3)
780 return;
781 AddToHistoryList(promptText, &searchHistory, &nHistoryStrings);
782 searchHelpText(HelpWindows[topic], topic, promptText, response == 2, 0, 0);
785 static void searchHelpAgainCB(Widget w, XtPointer clientData,
786 XtPointer callData)
788 int topic;
790 if ((topic = findTopicFromShellWidget((Widget)clientData)) == -1)
791 return; /* shouldn't happen */
792 searchHelpText(HelpWindows[topic], topic, LastSearchString,
793 LastSearchWasAllTopics, LastSearchPos, LastSearchTopic);
796 static void printCB(Widget w, XtPointer clientData, XtPointer callData)
798 int topic, helpStringLen;
799 char *helpString;
801 if ((topic = findTopicFromShellWidget((Widget)clientData)) == -1)
802 return; /* shouldn't happen */
803 helpString = TextGetWrapped(HelpTextPanes[topic], 0,
804 TextGetBuffer(HelpTextPanes[topic])->length, &helpStringLen);
805 PrintString(helpString, helpStringLen, HelpWindows[topic],
806 HelpTitles[topic]);
807 XtFree(helpString);
812 ** Find the topic and text position within that topic targeted by a hyperlink
813 ** with name "link_name". Returns true if the link was successfully interpreted.
815 static int is_known_link(char *link_name, int *topic, int *textPosition)
817 Href * hypertext;
819 /*------------------------------
820 * Direct topic links found here.
821 *------------------------------*/
822 for (*topic=0; HelpTitles[*topic] != NULL; (*topic)++)
824 if (strcmp (link_name, HelpTitles[*topic]) == 0)
826 *textPosition = 0;
827 return 1;
831 /*------------------------------------
832 * Links internal to topics found here.
833 *------------------------------------*/
834 for (hypertext = &H_R[0]; hypertext != NULL; hypertext = hypertext->next)
836 if (strcmp (link_name, hypertext->source) == 0)
838 *topic = hypertext->topic;
839 *textPosition = hypertext->location;
840 return 1;
844 return 0;
848 ** Find the text of a hyperlink from a clicked character position somewhere
849 ** within the hyperlink text, and display the help that it links to.
851 static void follow_hyperlink(int topic, int charPosition, int newWindow)
853 textDisp *textD = ((TextWidget)HelpTextPanes[topic])->text.textD;
854 char * link_text;
855 int link_topic;
856 int link_pos;
857 int end = charPosition;
858 int begin = charPosition;
859 char whatStyle = BufGetCharacter(textD->styleBuffer, end);
861 /*--------------------------------------------------
862 * Locate beginning and ending of current text style.
863 *--------------------------------------------------*/
864 while (whatStyle == BufGetCharacter(textD->styleBuffer, ++end));
865 while (whatStyle == BufGetCharacter(textD->styleBuffer, begin-1)) begin--;
867 link_text = BufGetRange (textD->buffer, begin, end);
869 if (is_known_link (link_text, &link_topic, &link_pos) )
871 if (HelpWindows[link_topic] != NULL) {
872 RaiseShellWindow(HelpWindows[link_topic]);
873 } else {
874 if(newWindow) {
875 HelpWindows[link_topic] = createHelpPanel(HelpWindows[topic],
876 link_topic);
878 } else {
879 changeWindowTopic(topic, link_topic);
882 navHistBack[link_topic] = topic;
883 navHistForw[topic] = link_topic;
884 TextSetCursorPos(HelpTextPanes[link_topic], link_pos);
885 adaptNavigationButtons(link_topic);
886 adaptNavigationButtons(topic);
888 XtFree (link_text);
891 static void helpFocusButtonsAP(Widget w, XEvent *event, String *args,
892 Cardinal *nArgs)
894 XmProcessTraversal(w, XmTRAVERSE_NEXT_TAB_GROUP);
898 * handler for help-button-action(<button-name>)
899 * Calls the activate callback for the named button widget of the help text win.
901 static void helpButtonActionAP(Widget w, XEvent *event, String *args,
902 Cardinal *nArgs)
904 char buf[80];
905 int topic;
906 Widget btn;
908 if(*nArgs != 1) {
909 fprintf(stderr, "help-button-action: requires exactly one argument.\n");
910 return;
913 /* Find the topic being displayed by this widget */
914 for (topic = 0; topic < NUM_TOPICS; topic++)
915 if (HelpTextPanes[topic] == w)
916 break;
918 if(topic == NUM_TOPICS || HelpWindows[topic] == NULL)
919 return; /* Shouldn't happen */
921 /* Compose the button widget name */
922 strcpy(&buf[0], "helpForm.");
923 if (strlen(args[0]) <= 70) {
924 strcat(&buf[0], args[0]);
926 else {
927 fprintf(stderr, "help-button-action: argument too long");
928 return;
931 btn=XtNameToWidget(HelpWindows[topic], buf);
932 if(btn) {
933 XtCallCallbacks(btn, XmNactivateCallback, HelpWindows[topic]);
934 } else {
935 fprintf(stderr, "help-button-action: invalid argument: %s\n", args[0]);
940 * Handler for action help-hyperlink()
941 * Arguments: none - init: record event position
942 * "current": if clicked on a link, follow link in same window
943 * "new": if clicked on a link, follow link in new window
945 * With the 1st argument "current" or "new" this action can have two additional
946 * arguments. These arguments must be valid names of XmText actions.
947 * In this case, the action named in argument #2 is called if the action
948 * help-hyperlink is about to follow the hyperlink. The Action in argument #3
949 * is called if no hyperlink has been recognized at the current event position.
951 static void helpHyperlinkAP(Widget w, XEvent *event, String *args,
952 Cardinal *nArgs)
954 XButtonEvent *e = (XButtonEvent *)event;
955 int topic;
956 textDisp *textD = ((TextWidget)w)->text.textD;
957 int clickedPos, newWin;
958 static int pressX=0, pressY=0;
960 /* called without arguments we just record coordinates */
961 if(*nArgs == 0) {
962 pressX = e->x;
963 pressY = e->y;
964 return;
967 newWin = !strcmp(args[0], "new");
969 if(!newWin && strcmp(args[0], "current")) {
970 fprintf(stderr, "help-hyperlink: Unrecognized argument %s\n", args[0]);
971 return;
975 * If for any reason (pointer moved - drag!, no hyperlink found)
976 * this action can't follow a hyperlink then execute the the action
977 * named in arg #3 (if provided)
979 if (abs(pressX - e->x) > CLICK_THRESHOLD ||
980 abs(pressY - e->y) > CLICK_THRESHOLD) {
981 if(*nArgs == 3)
982 XtCallActionProc(w, args[2], event, NULL, 0);
983 return;
986 clickedPos = TextDXYToCharPos(textD, e->x, e->y);
987 if (BufGetCharacter(textD->styleBuffer, clickedPos) != STL_NM_LINK){
988 if(*nArgs == 3)
989 XtCallActionProc(w, args[2], event, NULL, 0);
990 return;
993 /* Find the topic being displayed by this widget */
994 for (topic = 0; topic < NUM_TOPICS; topic++)
995 if (HelpTextPanes[topic] == w)
996 break;
998 if (topic == NUM_TOPICS){
999 /* If we get here someone must have bound help-hyperlink to a non-help
1000 * text widget (!) Or some other really strange thing happened.
1002 if(*nArgs == 3)
1003 XtCallActionProc(w, args[2], event, NULL, 0);
1004 return;
1007 /* If the action help-hyperlink had 3 arguments execute the action
1008 * named in arg #2 before really following the link.
1010 if(*nArgs == 3)
1011 XtCallActionProc(w, args[1], event, NULL, 0);
1013 follow_hyperlink(topic, clickedPos, newWin);
1017 ** Install the action for following hyperlinks in the help window
1019 void InstallHelpLinkActions(XtAppContext context)
1021 static XtActionsRec Actions[] = {
1022 {"help-hyperlink", helpHyperlinkAP},
1023 {"help-focus-buttons", helpFocusButtonsAP},
1024 {"help-button-action", helpButtonActionAP}
1027 XtAppAddActions(context, Actions, XtNumber(Actions));
1031 ** Search the help text. If allSections is true, searches all of the help
1032 ** text, otherwise searches only in parentTopic.
1034 static void searchHelpText(Widget parent, int parentTopic,
1035 const char *searchFor, int allSections, int startPos, int startTopic)
1037 int topic, beginMatch, endMatch;
1038 int found = False;
1039 char * helpText = NULL;
1041 /* Search for the string */
1042 for (topic=startTopic; topic<NUM_TOPICS; topic++) {
1043 if (!allSections && topic != parentTopic)
1044 continue;
1045 helpText = stitch(parent, HelpText[topic], NULL);
1046 if (SearchString(helpText, searchFor, SEARCH_FORWARD,
1047 SEARCH_LITERAL, False, topic == startTopic ? startPos : 0,
1048 &beginMatch, &endMatch, NULL, NULL, GetPrefDelimiters())) {
1049 found = True;
1050 XtFree(helpText);
1051 break;
1053 XtFree(helpText);
1055 if (!found) {
1056 if (startPos != 0 || (allSections && startTopic != 0)) {
1057 /* Wrap search */
1058 searchHelpText(parent, parentTopic, searchFor, allSections, 0, 0);
1059 return;
1061 DialogF(DF_INF, parent, 1, "String Not Found", "Dismiss");
1062 return;
1065 /* update navigation history */
1066 if(parentTopic != topic) {
1067 navHistForw[parentTopic]= topic;
1068 navHistBack[topic]= parentTopic;
1071 /* If the appropriate window is already up, bring it to the top, if not,
1072 make the parent window become this topic */
1073 changeTopicOrRaise(parentTopic, topic);
1074 BufSelect(TextGetBuffer(HelpTextPanes[topic]), beginMatch, endMatch);
1075 TextSetCursorPos(HelpTextPanes[topic], endMatch);
1077 /* Save the search information for search-again */
1078 strcpy(LastSearchString, searchFor);
1079 LastSearchTopic = topic;
1080 LastSearchPos = endMatch;
1081 LastSearchWasAllTopics = allSections;
1085 ** Change a help window to display a new topic. (Help window data is stored
1086 ** and indexed by topic so if a given topic is already displayed or has been
1087 ** positioned by the user, it can be found and popped back up in the same
1088 ** place.) To change the topic displayed, the stored data has to be relocated.
1090 static void changeWindowTopic(int existingTopic, int newTopic)
1092 char *helpText, *styleData;
1094 /* Relocate the window/widget/buffer information */
1095 if(newTopic != existingTopic) {
1096 HelpWindows[newTopic] = HelpWindows[existingTopic];
1097 HelpWindows[existingTopic] = NULL;
1098 HelpStyleBuffers[newTopic] = HelpStyleBuffers[existingTopic];
1099 HelpStyleBuffers[existingTopic] = NULL;
1100 HelpTextPanes[newTopic] = HelpTextPanes[existingTopic];
1101 HelpTextPanes[existingTopic] = NULL;
1102 setHelpWinTitle(HelpWindows[newTopic], newTopic);
1105 /* Set the existing text widget to display the new text. Because it's
1106 highlighted, we have to turn off highlighting before changing the
1107 displayed text to prevent the text widget from trying to apply the
1108 old, mismatched, highlighting to the new text */
1109 helpText = stitch(HelpTextPanes[newTopic], HelpText[newTopic], &styleData);
1110 TextDAttachHighlightData(((TextWidget)HelpTextPanes[newTopic])->text.textD,
1111 NULL, NULL, 0, '\0', NULL, NULL);
1112 BufSetAll(TextGetBuffer(HelpTextPanes[newTopic]), helpText);
1113 XtFree(helpText);
1114 BufSetAll(HelpStyleBuffers[newTopic], styleData);
1115 XtFree(styleData);
1116 TextDAttachHighlightData(((TextWidget)HelpTextPanes[newTopic])->text.textD,
1117 HelpStyleBuffers[newTopic], HelpStyleInfo, N_STYLES, '\0',
1118 NULL, NULL);
1121 static int findTopicFromShellWidget(Widget shellWidget)
1123 int i;
1125 for (i=0; i<NUM_TOPICS; i++)
1126 if (shellWidget == HelpWindows[i])
1127 return i;
1128 return -1;
1131 static void initNavigationHistory(void) {
1132 static int doInitNavigationHistory = True;
1133 int i;
1135 if(doInitNavigationHistory) {
1136 for(i=0; i<NUM_TOPICS; i++)
1137 navHistBack[i] = navHistForw[i] = -1;
1139 doInitNavigationHistory = False;
1143 #if XmVersion == 2000
1144 /* amai: This function may be called before the Motif part
1145 is being initialized. The following, public interface
1146 is known to initialize at least xmUseVersion.
1147 That interface is declared in <Xm/Xm.h> in Motif 1.2 only.
1148 As for Motif 2.1 we don't need this call anymore.
1149 This also holds for the Motif 2.1 version of LessTif
1150 releases > 0.93.0. */
1151 extern void XmRegisterConverters(void);
1152 #endif
1155 /* Print version info to stdout */
1156 void PrintVersion(void)
1158 const char *text;
1160 #if XmVersion < 2001
1161 XmRegisterConverters(); /* see comment above */
1162 #endif
1163 text = getBuildInfo();
1164 puts (text);