Increased the help window size from 65 to 75 columns as a workaround for
[nedit.git] / source / help.c
blobd441b1d08f4ec4835199554322cdcb8f1eea2ed5
1 static const char CVSID[] = "$Id: help.c,v 1.97 2004/06/30 11:56:02 edg 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 <locale.h>
52 #include <stdlib.h>
53 #include <stdio.h>
54 #include <string.h>
55 #ifdef VMS
56 #include "../util/VMSparam.h"
57 #else
58 #ifndef __MVS__
59 #include <sys/param.h>
60 #endif
61 #endif /*VMS*/
63 #include <Xm/Xm.h>
64 #include <Xm/XmP.h> /* These are for applying style info to help text */
65 #include <Xm/Form.h>
66 #include <Xm/PrimitiveP.h>
67 #include <Xm/ScrolledW.h>
68 #include <Xm/ScrollBar.h>
69 #include <Xm/PushB.h>
70 #ifdef EDITRES
71 #include <X11/Xmu/Editres.h>
72 /* extern void _XEditResCheckMessages(); */
73 #endif /* EDITRES */
75 #ifdef HAVE_DEBUG_H
76 #include "../debug.h"
77 #endif
79 /*============================================================================*/
80 /* SYMBOL DEFINITIONS */
81 /*============================================================================*/
83 #define EOS '\0' /* end-of-string character */
85 #define CLICK_THRESHOLD 5 /* number of pixels mouse may move from its */
86 /* pressed location for mouse-up to be */
87 /* considered a valid click (as opposed to */
88 /* a drag or mouse-pick error) */
90 /*============================================================================*/
91 /* VARIABLE DECLARATIONS */
92 /*============================================================================*/
94 static Widget HelpWindows[NUM_TOPICS] = {NULL};
95 static Widget HelpTextPanes[NUM_TOPICS] = {NULL};
96 static textBuffer *HelpStyleBuffers[NUM_TOPICS] = {NULL};
97 static int navHistForw[NUM_TOPICS];
98 static int navHistBack[NUM_TOPICS];
100 /* Information on the last search for search-again */
101 static char LastSearchString[DF_MAX_PROMPT_LENGTH] = "";
102 static int LastSearchTopic = -1;
103 static int LastSearchPos = 0;
104 static int LastSearchWasAllTopics = False;
106 /* Fonts for each help text style generated by the help generator (setext).
107 The NEdit text widget uses the first help style, 'A', to calculate window
108 width, so making 'A' a fixed font will yield window widths calibrated to
109 match width-dependent fixed font layouts in the help text. */
110 static enum helpFonts StyleFonts[] =
112 /* Fixed fonts, styles: 'A', 'B', 'C', 'D' */
113 FIXED_HELP_FONT, BOLD_FIXED_HELP_FONT, BOLD_FIXED_HELP_FONT,
114 BOLD_ITALIC_FIXED_HELP_FONT,
116 /* Underlined fixed fonts, styles: 'E', 'F', 'G', 'H' */
117 FIXED_HELP_FONT, BOLD_FIXED_HELP_FONT, BOLD_FIXED_HELP_FONT,
118 BOLD_ITALIC_FIXED_HELP_FONT,
120 /* Normal (proportional) fonts, styles: 'I', 'J', 'K', 'L' */
121 HELP_FONT, BOLD_HELP_FONT, ITALIC_HELP_FONT, BOLD_ITALIC_HELP_FONT,
123 /* Underlined fonts, styles: 'M', 'N', 'O', 'P' */
124 HELP_FONT, BOLD_HELP_FONT, ITALIC_HELP_FONT, BOLD_ITALIC_HELP_FONT,
126 /* Link font, style: 'Q' */
127 HELP_FONT,
129 /* Heading fonts, styles: 'R', 'S', 'T' */
130 H1_HELP_FONT, H2_HELP_FONT, H3_HELP_FONT
133 static int StyleUnderlines[] =
135 /* Fixed fonts, styles: 'A', 'B', 'C', 'D' */
136 False, False, False, False,
138 /* Underlined fixed fonts, styles: 'E', 'F', 'G', 'H' */
139 True, True, True, True,
141 /* Normal (proportional) fonts, styles: 'I', 'J', 'K', 'L' */
142 False, False, False, False,
144 /* Underlined fonts, styles: 'M', 'N', 'O', 'P' */
145 True, True, True, True,
147 /* Link font, style: 'Q' */
148 True,
150 /* Heading fonts, styles: 'R', 'S', 'T' */
151 False, False, False
154 #define N_STYLES (XtNumber(StyleFonts))
156 static styleTableEntry HelpStyleInfo[ N_STYLES ];
158 /* Translation table for style codes (A, B, C, ...) to their ASCII codes.
159 For systems using ASCII, this is just a one-to-one mapping, but the
160 table makes it possible to use the style codes also on an EBCDIC system.
162 static unsigned char AlphabetToAsciiTable[256];
164 /* Macro that calculates the zero-based index for a given style, taking
165 into account that the character set may not use ASCII coding, but EBCDIC.
166 The "style" argument must be one of the characters A - Z.
167 In ASCII, this comes down to "style - STYLE_PLAIN". */
168 #define STYLE_INDEX(style) \
169 (AlphabetToAsciiTable[(unsigned char)style] - \
170 AlphabetToAsciiTable[(unsigned char)STYLE_PLAIN])
172 /*============================================================================*/
173 /* PROGRAM PROTOTYPES */
174 /*============================================================================*/
176 static Widget createHelpPanel(enum HelpTopic topic);
177 static void dismissCB(Widget w, XtPointer clientData, XtPointer callData);
178 static void prevTopicCB(Widget w, XtPointer clientData, XtPointer callData);
179 static void nextTopicCB(Widget w, XtPointer clientData, XtPointer callData);
180 static void bwHistoryCB(Widget w, XtPointer clientData, XtPointer callData);
181 static void fwHistoryCB(Widget w, XtPointer clientData, XtPointer callData);
182 static void searchHelpCB(Widget w, XtPointer clientData, XtPointer callData);
183 static void searchHelpAgainCB(Widget w, XtPointer clientData,
184 XtPointer callData);
185 static void printCB(Widget w, XtPointer clientData, XtPointer callData);
186 static char *stitch(Widget parent, char **string_list,char **styleMap);
187 static void searchHelpText(Widget parent, int parentTopic,
188 const char *searchFor, int allSections, int startPos, int startTopic);
189 static void changeWindowTopic(int existingTopic, enum HelpTopic newTopic);
190 static int findTopicFromShellWidget(Widget shellWidget);
191 static void loadFontsAndColors(Widget parent, int style);
192 static void initNavigationHistory(void);
194 #ifdef HAVE__XMVERSIONSTRING
195 extern char _XmVersionString[];
196 #else
197 static char _XmVersionString[] = "unknown";
198 #endif
200 /*============================================================================*/
201 /*================================= PROGRAMS =================================*/
202 /*============================================================================*/
205 ** Create a string containing information on the build environment. Returned
206 ** string must NOT be freed by caller.
209 static char *bldInfoString = NULL;
211 static void freeBuildInfo(void)
213 /* This keeps memory leak detectors happy */
214 XtFree(bldInfoString);
217 static const char *getBuildInfo(void)
219 const char * bldFormat =
220 "%s\n"
221 " Built on: %s, %s, %s\n"
222 " Built at: %s, %s\n"
223 " With Motif: %d.%d.%d [%s]"
224 #ifdef BUILD_BROKEN_NEDIT
225 " (KNOWN-BAD)\n"
226 #elif defined BUILD_UNTESTED_NEDIT
227 " (UNTESTED)\n"
228 #else
229 "\n"
230 #endif
231 "Running Motif: %d.%d [%s]\n"
232 " Server: %s %d\n"
233 " Visual: %s\n"
234 " Locale: %s\n"
235 #ifdef BUILD_BROKEN_NEDIT
236 "\nThis NEdit was built with a known-bad version of Motif. Please\n"
237 "do not report any bugs you encounter unless you can reproduce\n"
238 "them with a known-good binary from the www.nedit.org website.\n"
239 "If this binary was supplied with your Linux distribution please\n"
240 "file a bug report with them asking them to build NEdit with a\n"
241 "known-good version of Motif.\n"
242 #endif
244 const char * visualClass[] = {"StaticGray", "GrayScale",
245 "StaticColor", "PseudoColor",
246 "TrueColor", "DirectColor"};
247 const char *locale;
249 if (bldInfoString == NULL)
251 char visualStr[500] = "<unknown>";
253 if (TheDisplay) {
254 Visual *visual;
255 int depth;
256 Colormap map;
257 Boolean usingDefaultVisual = FindBestVisual(TheDisplay, APP_NAME,
258 APP_CLASS, &visual,
259 &depth, &map);
260 sprintf(visualStr,"%d-bit %s (ID %#lx%s)",
261 depth,
262 visualClass[visual->class],
263 visual->visualid,
264 usingDefaultVisual ? ", Default" : "");
267 bldInfoString = XtMalloc (strlen (bldFormat) + 1024);
268 locale = setlocale(LC_MESSAGES, "");
270 sprintf(bldInfoString, bldFormat,
271 NEditVersion,
272 COMPILE_OS, COMPILE_MACHINE, COMPILE_COMPILER,
273 linkdate, linktime,
274 XmVERSION, XmREVISION, XmUPDATE_LEVEL,
275 XmVERSION_STRING,
276 xmUseVersion/1000, xmUseVersion%1000,
277 _XmVersionString,
278 ServerVendor(TheDisplay), VendorRelease(TheDisplay),
279 visualStr,
280 locale ? locale : "None");
282 atexit(freeBuildInfo);
285 return bldInfoString;
289 ** Initialization for help system data, needs to be done only once.
291 static void initHelpStyles (Widget parent)
293 static int styleTableInitialized = False;
295 if (! styleTableInitialized)
297 Pixel fg;
298 int styleIndex;
299 char ** line;
301 XtVaGetValues(parent, XtNforeground, &fg, NULL);
303 for (styleIndex = 0; styleIndex < STL_HD + MAX_HEADING; styleIndex++)
305 HelpStyleInfo[ styleIndex ].color = fg;
306 HelpStyleInfo[ styleIndex ].underline = StyleUnderlines[styleIndex];
307 HelpStyleInfo[ styleIndex ].font = NULL;
310 styleTableInitialized = True;
312 /*-------------------------------------------------------
313 * Only attempt to add build information to version text
314 * when string formatting symbols are present in the text.
315 * This special case is needed to incorporate this
316 * dynamically created information into the static help.
317 *-------------------------------------------------------*/
318 for (line = HelpText[ HELP_VERSION ]; *line != NULL; line++)
320 /*--------------------------------------------------
321 * If and when this printf format is found in the
322 * version help text, replace that line with the
323 * build information. Then stitching the help text
324 * will have the final count of characters to use.
325 *--------------------------------------------------*/
326 if (strstr (*line, "%s") != NULL)
328 const char * bldInfo = getBuildInfo();
329 char * text = XtMalloc (strlen (*line) + strlen (bldInfo));
330 sprintf (text, *line, bldInfo);
331 *line = text;
332 break;
336 /*---------------------------------------------------------
337 * Also initialize the alphabet-to-ASCII-code table (to
338 * make the style mapping also work on EBCDIC).
339 * DON'T use 'A' to initialize the table! 'A' != 65 in EBCDIC.
340 *--------------------------------------------------------*/
341 AlphabetToAsciiTable[(unsigned char)'A'] = ASCII_A + 0;
342 AlphabetToAsciiTable[(unsigned char)'B'] = ASCII_A + 1;
343 AlphabetToAsciiTable[(unsigned char)'C'] = ASCII_A + 2;
344 AlphabetToAsciiTable[(unsigned char)'D'] = ASCII_A + 3;
345 AlphabetToAsciiTable[(unsigned char)'E'] = ASCII_A + 4;
346 AlphabetToAsciiTable[(unsigned char)'F'] = ASCII_A + 5;
347 AlphabetToAsciiTable[(unsigned char)'G'] = ASCII_A + 6;
348 AlphabetToAsciiTable[(unsigned char)'H'] = ASCII_A + 7;
349 AlphabetToAsciiTable[(unsigned char)'I'] = ASCII_A + 8;
350 AlphabetToAsciiTable[(unsigned char)'J'] = ASCII_A + 9;
351 AlphabetToAsciiTable[(unsigned char)'K'] = ASCII_A + 10;
352 AlphabetToAsciiTable[(unsigned char)'L'] = ASCII_A + 11;
353 AlphabetToAsciiTable[(unsigned char)'M'] = ASCII_A + 12;
354 AlphabetToAsciiTable[(unsigned char)'N'] = ASCII_A + 13;
355 AlphabetToAsciiTable[(unsigned char)'O'] = ASCII_A + 14;
356 AlphabetToAsciiTable[(unsigned char)'P'] = ASCII_A + 15;
357 AlphabetToAsciiTable[(unsigned char)'Q'] = ASCII_A + 16;
358 AlphabetToAsciiTable[(unsigned char)'R'] = ASCII_A + 17;
359 AlphabetToAsciiTable[(unsigned char)'S'] = ASCII_A + 18;
360 AlphabetToAsciiTable[(unsigned char)'T'] = ASCII_A + 19;
361 AlphabetToAsciiTable[(unsigned char)'U'] = ASCII_A + 20;
362 AlphabetToAsciiTable[(unsigned char)'V'] = ASCII_A + 21;
363 AlphabetToAsciiTable[(unsigned char)'W'] = ASCII_A + 22;
364 AlphabetToAsciiTable[(unsigned char)'X'] = ASCII_A + 23;
365 AlphabetToAsciiTable[(unsigned char)'Y'] = ASCII_A + 24;
366 AlphabetToAsciiTable[(unsigned char)'Z'] = ASCII_A + 25;
371 ** Help fonts are not loaded until they're actually needed. This function
372 ** checks if the style's font is loaded, and loads it if it's not.
374 static void loadFontsAndColors(Widget parent, int style)
376 XFontStruct *font;
377 int r,g,b;
378 if (HelpStyleInfo[STYLE_INDEX(style)].font == NULL)
380 font = XLoadQueryFont(XtDisplay(parent),
381 GetPrefHelpFontName(StyleFonts[STYLE_INDEX(style)]));
382 if (font == NULL)
384 fprintf(stderr, "NEdit: help font, %s, not available\n",
385 GetPrefHelpFontName(StyleFonts[STYLE_INDEX(style)]));
386 font = XLoadQueryFont(XtDisplay(parent), "fixed");
387 if (font == NULL)
389 fprintf(stderr, "NEdit: fallback help font, \"fixed\", not "
390 "available, cannot continue\n");
391 exit(EXIT_FAILURE);
394 HelpStyleInfo[STYLE_INDEX(style)].font = font;
396 if (style == STL_NM_LINK)
397 HelpStyleInfo[STYLE_INDEX(style)].color =
398 AllocColor(parent, GetPrefHelpLinkColor(), &r, &g, &b);
402 static void adaptNavigationButtons(int topic) {
403 Widget btn;
405 if(HelpWindows[topic] == NULL)
406 return; /* Shouldn't happen */
408 btn=XtNameToWidget(HelpWindows[topic], "helpForm.prevTopic");
409 if(btn)
411 if(topic > 0)
412 XtSetSensitive(btn, True);
413 else
414 XtSetSensitive(btn, False);
417 btn=XtNameToWidget(HelpWindows[topic], "helpForm.nextTopic");
418 if(btn)
420 if(topic < (NUM_TOPICS - 1))
421 XtSetSensitive(btn, True);
422 else
423 XtSetSensitive(btn, False);
426 btn=XtNameToWidget(HelpWindows[topic], "helpForm.histBack");
427 if(btn)
429 if(navHistBack[topic] != -1)
430 XtSetSensitive(btn, True);
431 else
432 XtSetSensitive(btn, False);
435 btn=XtNameToWidget(HelpWindows[topic], "helpForm.histForw");
436 if(btn)
438 if(navHistForw[topic] != -1)
439 XtSetSensitive(btn, True);
440 else
441 XtSetSensitive(btn, False);
447 ** Put together stored help strings to create the text and optionally the style
448 ** information for a given help topic.
450 static char * stitch (
452 Widget parent, /* used for dynamic font/color allocation */
453 char ** string_list, /* given help strings to stitch together */
454 char ** styleMap /* NULL, or a place to store help styles */
457 char * cp;
458 char * section, * sp; /* resulting help text section */
459 char * styleData, * sdp; /* resulting style data for text */
460 char style = STYLE_PLAIN; /* start off each section with this style */
461 int total_size = 0; /* help text section size */
462 char ** crnt_line;
464 /*----------------------------------------------------
465 * How many characters are there going to be displayed?
466 *----------------------------------------------------*/
467 for (crnt_line = string_list; *crnt_line != NULL; crnt_line++)
469 for (cp = *crnt_line; *cp != EOS; cp++)
471 /*---------------------------------------------
472 * The help text has embedded style information
473 * consisting of the style marker and a single
474 * character style, for a total of 2 characters.
475 * This style information is not to be included
476 * in the character counting below.
477 *---------------------------------------------*/
478 if (*cp != STYLE_MARKER) {
479 total_size++;
481 else {
482 cp++; /* skipping style marker, loop will handle style */
487 /*--------------------------------------------------------
488 * Get the needed space, one area for the help text being
489 * stitched together, another for the styles to be applied.
490 *--------------------------------------------------------*/
491 sp = section = XtMalloc (total_size +1);
492 sdp = styleData = (styleMap) ? XtMalloc (total_size +1) : NULL;
493 *sp = EOS;
495 /*--------------------------------------------
496 * Fill in the newly acquired contiguous space
497 * with help text and style information.
498 *--------------------------------------------*/
499 for (crnt_line = string_list; *crnt_line != NULL; crnt_line++)
501 for (cp = *crnt_line; *cp != EOS; cp++)
503 if (*cp == STYLE_MARKER)
505 style = *(++cp);
506 loadFontsAndColors(parent, style);
507 } else
509 *(sp++) = *cp;
511 /* Beware of possible EBCDIC coding! Use the mapping table. */
512 if (styleMap)
513 *(sdp++) = AlphabetToAsciiTable[(unsigned char)style];
518 *sp = EOS;
520 /*-----------------------------------------
521 * Only deal with style map, when available.
522 *-----------------------------------------*/
523 if (styleMap) {
524 *styleMap = styleData;
525 *sdp = EOS;
528 return section;
532 ** Display help for subject "topic". "parent" is a widget over which the help
533 ** dialog may be posted. Help dialogs are preserved when popped down by the
534 ** user, and may appear posted over a previous parent, regardless of the parent
535 ** argument.
537 void Help(enum HelpTopic topic)
539 if (HelpWindows[topic] != NULL)
540 RaiseShellWindow(HelpWindows[topic]);
541 else
542 HelpWindows[topic] = createHelpPanel(topic);
543 adaptNavigationButtons(topic);
547 /* Setup Window/Icon title for the help window. */
548 static void setHelpWinTitle(Widget win, enum HelpTopic topic)
550 char * buf, *topStr=HelpTitles[topic];
552 buf=malloc(strlen(topStr) + 24);
553 topic++;
555 sprintf(buf, "NEdit Help (%d)", (int)topic);
556 XtVaSetValues(win, XmNiconName, buf, NULL);
558 sprintf(buf, "NEdit Help: %s (%d)", topStr, (int)topic);
559 XtVaSetValues(win, XmNtitle, buf, NULL);
561 free(buf);
565 ** Create a new help window displaying a given subject, "topic"
567 ** Important hint: If this widget is restructured or the name of the text
568 ** subwidget is changed don't forget to adapt the default translations of the
569 ** help text. They are located in nedit.c, look for
570 ** static char *fallbackResources
571 ** (currently: nedit.helpForm.sw.helpText*translations...)
573 static Widget createHelpPanel(enum HelpTopic topic)
575 Arg al[50];
576 int ac;
577 Widget appShell, btn, dismissBtn, form, btnFW;
578 Widget sw, hScrollBar, vScrollBar;
579 XmString st1;
580 char * helpText = NULL;
581 char * styleData = NULL;
583 ac = 0;
584 XtSetArg(al[ac], XmNdeleteResponse, XmDO_NOTHING); ac++;
585 appShell = CreateWidget(TheAppShell, "help",
586 topLevelShellWidgetClass, al, ac);
587 AddSmallIcon(appShell);
588 /* With openmotif 2.1.30, a crash may occur when the text widget of the
589 help window is (slowly) resized to a zero width. By imposing a
590 minimum _window_ width, we can work around this problem. The minimum
591 width should be larger than the width of the scrollbar. 50 is probably
592 a safe value; this leaves room for a few characters */
593 XtVaSetValues(appShell, XtNminWidth, 50, NULL);
594 form = XtVaCreateManagedWidget("helpForm", xmFormWidgetClass, appShell,
595 NULL);
596 XtVaSetValues(form, XmNshadowThickness, 0, NULL);
598 /* Create the bottom row of buttons */
599 btn = XtVaCreateManagedWidget("find", xmPushButtonWidgetClass, form,
600 XmNlabelString, st1=XmStringCreateSimple("Find..."),
601 XmNmnemonic, 'F',
602 XmNbottomAttachment, XmATTACH_FORM,
603 XmNleftAttachment, XmATTACH_POSITION,
604 XmNleftPosition, 3,
605 XmNrightAttachment, XmATTACH_POSITION,
606 XmNrightPosition, 25,
607 NULL);
608 XtAddCallback(btn, XmNactivateCallback, searchHelpCB, appShell);
609 XmStringFree(st1);
611 btn = XtVaCreateManagedWidget("findAgain", xmPushButtonWidgetClass, form,
612 XmNlabelString, st1=XmStringCreateSimple("Find Again"),
613 XmNmnemonic, 'A',
614 XmNbottomAttachment, XmATTACH_FORM,
615 XmNleftAttachment, XmATTACH_POSITION,
616 XmNleftPosition, 27,
617 XmNrightAttachment, XmATTACH_POSITION,
618 XmNrightPosition, 49,
619 NULL);
620 XtAddCallback(btn, XmNactivateCallback, searchHelpAgainCB, appShell);
621 XmStringFree(st1);
623 btn = XtVaCreateManagedWidget("print", xmPushButtonWidgetClass, form,
624 XmNlabelString, st1=XmStringCreateSimple("Print..."),
625 XmNmnemonic, 'P',
626 XmNbottomAttachment, XmATTACH_FORM,
627 XmNleftAttachment, XmATTACH_POSITION,
628 XmNleftPosition, 51,
629 XmNrightAttachment, XmATTACH_POSITION,
630 XmNrightPosition, 73,
631 NULL);
632 XtAddCallback(btn, XmNactivateCallback, printCB, appShell);
633 XmStringFree(st1);
635 dismissBtn = XtVaCreateManagedWidget("dismiss",
636 xmPushButtonWidgetClass, form,
637 XmNlabelString, st1=XmStringCreateSimple("Dismiss"),
638 XmNbottomAttachment, XmATTACH_FORM,
639 XmNleftAttachment, XmATTACH_POSITION,
640 XmNleftPosition, 75,
641 XmNrightAttachment, XmATTACH_POSITION,
642 XmNrightPosition, 97,
643 NULL);
644 XtAddCallback(dismissBtn, XmNactivateCallback, dismissCB, appShell);
645 XmStringFree(st1);
647 /* Create the next row of buttons (for navigation) */
648 btn = XtVaCreateManagedWidget("prevTopic", xmPushButtonWidgetClass, form,
649 XmNlabelString, st1=XmStringCreateSimple("<< Browse"),
650 XmNmnemonic, 'o',
651 XmNbottomAttachment, XmATTACH_WIDGET,
652 XmNbottomWidget, dismissBtn,
653 XmNleftAttachment, XmATTACH_POSITION,
654 XmNleftPosition, 51,
655 XmNrightAttachment, XmATTACH_POSITION,
656 XmNrightPosition, 73,
657 NULL);
658 XtAddCallback(btn, XmNactivateCallback, prevTopicCB, appShell);
659 XmStringFree(st1);
661 btn = XtVaCreateManagedWidget("nextTopic", xmPushButtonWidgetClass, form,
662 XmNlabelString, st1=XmStringCreateSimple("Browse >>"),
663 XmNmnemonic, 'e',
664 XmNbottomAttachment, XmATTACH_WIDGET,
665 XmNbottomWidget, dismissBtn,
666 XmNleftAttachment, XmATTACH_POSITION,
667 XmNleftPosition, 75,
668 XmNrightAttachment, XmATTACH_POSITION,
669 XmNrightPosition, 97,
670 NULL);
671 XtAddCallback(btn, XmNactivateCallback, nextTopicCB, appShell);
672 XmStringFree(st1);
674 btn = XtVaCreateManagedWidget("histBack", xmPushButtonWidgetClass, form,
675 XmNlabelString, st1=XmStringCreateSimple("Back"),
676 XmNmnemonic, 'B',
677 XmNbottomAttachment, XmATTACH_WIDGET,
678 XmNbottomWidget, dismissBtn,
679 XmNleftAttachment, XmATTACH_POSITION,
680 XmNleftPosition, 3,
681 XmNrightAttachment, XmATTACH_POSITION,
682 XmNrightPosition, 25,
683 NULL);
684 XtAddCallback(btn, XmNactivateCallback, bwHistoryCB, appShell);
685 XmStringFree(st1);
687 btnFW = XtVaCreateManagedWidget("histForw", xmPushButtonWidgetClass, form,
688 XmNlabelString, st1=XmStringCreateSimple("Forward"),
689 XmNmnemonic, 'w',
690 XmNbottomAttachment, XmATTACH_WIDGET,
691 XmNbottomWidget, dismissBtn,
692 XmNleftAttachment, XmATTACH_POSITION,
693 XmNleftPosition, 27,
694 XmNrightAttachment, XmATTACH_POSITION,
695 XmNrightPosition, 49,
696 NULL);
697 XtAddCallback(btnFW, XmNactivateCallback, fwHistoryCB, appShell);
698 XmStringFree(st1);
700 /* Create a text widget inside of a scrolled window widget */
701 sw = XtVaCreateManagedWidget("sw", xmScrolledWindowWidgetClass, form,
702 XmNshadowThickness, 2,
703 XmNtopAttachment, XmATTACH_FORM,
704 XmNleftAttachment, XmATTACH_FORM,
705 XmNrightAttachment, XmATTACH_FORM,
706 XmNbottomAttachment, XmATTACH_WIDGET,
707 XmNbottomWidget, btnFW,
708 NULL);
709 hScrollBar = XtVaCreateManagedWidget("hScrollBar",
710 xmScrollBarWidgetClass, sw,
711 XmNorientation, XmHORIZONTAL,
712 XmNrepeatDelay, 10,
713 NULL);
714 vScrollBar = XtVaCreateManagedWidget("vScrollBar",
715 xmScrollBarWidgetClass, sw,
716 XmNorientation, XmVERTICAL,
717 XmNrepeatDelay, 10,
718 NULL);
719 HelpTextPanes[topic] = XtVaCreateManagedWidget("helpText",
720 textWidgetClass, sw,
721 textNrows, 30,
722 textNcolumns, 75,
723 textNbacklightCharTypes, NULL,
724 textNhScrollBar, hScrollBar,
725 textNvScrollBar, vScrollBar,
726 textNreadOnly, True,
727 textNcontinuousWrap, True,
728 textNautoShowInsertPos, True,
729 NULL);
730 XtVaSetValues(sw, XmNworkWindow, HelpTextPanes[topic],
731 XmNhorizontalScrollBar, hScrollBar,
732 XmNverticalScrollBar, vScrollBar,
733 NULL);
735 /* Initialize help style information, if it hasn't already been init'd */
736 initHelpStyles (HelpTextPanes[topic]);
738 /* Put together the text to display and separate it into parallel text
739 and style data for display by the widget */
740 helpText = stitch (HelpTextPanes[topic], HelpText[topic], &styleData);
742 /* Stuff the text into the widget's text buffer */
743 BufSetAll (TextGetBuffer (HelpTextPanes[topic]) , helpText);
744 XtFree (helpText);
746 /* Create a style buffer for the text widget and fill it with the style
747 data which was generated along with the text content */
748 HelpStyleBuffers[topic] = BufCreate();
749 BufSetAll(HelpStyleBuffers[topic], styleData);
750 XtFree (styleData);
751 TextDAttachHighlightData(((TextWidget)HelpTextPanes[topic])->text.textD,
752 HelpStyleBuffers[topic], HelpStyleInfo, N_STYLES, '\0', NULL, NULL);
754 /* This shouldn't be necessary (what's wrong in text.c?) */
755 HandleXSelections(HelpTextPanes[topic]);
757 /* Process dialog mnemonic keys */
758 AddDialogMnemonicHandler(form, FALSE);
760 /* Set the default button */
761 XtVaSetValues(form, XmNdefaultButton, dismissBtn, NULL);
762 XtVaSetValues(form, XmNcancelButton, dismissBtn, NULL);
764 /* realize all of the widgets in the new window */
765 RealizeWithoutForcingPosition(appShell);
767 /* Give the text pane the initial focus */
768 XmProcessTraversal(HelpTextPanes[topic], XmTRAVERSE_CURRENT);
770 /* Make close command in window menu gracefully prompt for close */
771 AddMotifCloseCallback(appShell, (XtCallbackProc)dismissCB, appShell);
773 /* Initialize navigation information, if it hasn't already been init'd */
774 initNavigationHistory();
776 /* Set the window title */
777 setHelpWinTitle(appShell, topic);
780 #ifdef EDITRES
781 XtAddEventHandler (appShell, (EventMask)0, True,
782 (XtEventHandler)_XEditResCheckMessages, NULL);
783 #endif /* EDITRES */
785 return appShell;
788 static void changeTopicOrRaise(int existingTopic, int newTopic) {
789 if(HelpWindows[newTopic] == NULL)
791 changeWindowTopic(existingTopic, newTopic);
792 adaptNavigationButtons(newTopic);
793 } else
795 RaiseShellWindow(HelpWindows[newTopic]);
796 adaptNavigationButtons(existingTopic);
797 adaptNavigationButtons(newTopic);
803 ** Callbacks for window controls
805 static void dismissCB(Widget w, XtPointer clientData, XtPointer callData)
807 int topic;
809 if ((topic = findTopicFromShellWidget((Widget)clientData)) == -1)
810 return;
812 /* I don't understand the mechanism by which this can be called with
813 HelpWindows[topic] as NULL, but it has happened */
814 XtDestroyWidget(HelpWindows[topic]);
815 HelpWindows[topic] = NULL;
816 if (HelpStyleBuffers[topic] != NULL)
818 BufFree(HelpStyleBuffers[topic]);
819 HelpStyleBuffers[topic] = NULL;
823 static void prevTopicCB(Widget w, XtPointer clientData, XtPointer callData)
824 { int topic;
826 if ((topic = findTopicFromShellWidget((Widget)clientData)) == -1)
827 return; /* shouldn't happen */
829 topic--;
830 if(topic >= 0)
831 changeTopicOrRaise(topic+1, topic);
834 static void nextTopicCB(Widget w, XtPointer clientData, XtPointer callData)
835 { int topic;
837 if ((topic = findTopicFromShellWidget((Widget)clientData)) == -1)
838 return; /* shouldn't happen */
840 topic++;
841 if(topic < NUM_TOPICS)
842 changeTopicOrRaise(topic-1, topic);
845 static void bwHistoryCB(Widget w, XtPointer clientData, XtPointer callData)
846 { int topic, goTo;
848 if ((topic = findTopicFromShellWidget((Widget)clientData)) == -1)
849 return; /* shouldn't happen */
851 goTo=navHistBack[topic];
852 if(goTo >= 0 && goTo < NUM_TOPICS)
854 navHistForw[goTo]=topic;
855 changeTopicOrRaise(topic, goTo);
859 static void fwHistoryCB(Widget w, XtPointer clientData, XtPointer callData)
860 { int topic, goTo;
862 if ((topic = findTopicFromShellWidget((Widget)clientData)) == -1)
863 return; /* shouldn't happen */
865 goTo=navHistForw[topic];
866 if(goTo >= 0 && goTo < NUM_TOPICS)
868 navHistBack[goTo]=topic;
869 changeTopicOrRaise(topic, goTo);
873 static void searchHelpCB(Widget w, XtPointer clientData, XtPointer callData)
875 char promptText[DF_MAX_PROMPT_LENGTH];
876 int response, topic;
877 static char **searchHistory = NULL;
878 static int nHistoryStrings = 0;
880 if ((topic = findTopicFromShellWidget((Widget)clientData)) == -1)
881 return; /* shouldn't happen */
882 SetDialogFPromptHistory(searchHistory, nHistoryStrings);
883 response = DialogF(DF_PROMPT, HelpWindows[topic], 3, "Find",
884 "Search for: (use up arrow key to recall previous)", promptText,
885 "This Section", "All Sections", "Cancel");
886 if (response == 3)
887 return;
888 AddToHistoryList(promptText, &searchHistory, &nHistoryStrings);
889 searchHelpText(HelpWindows[topic], topic, promptText, response == 2, 0, 0);
892 static void searchHelpAgainCB(Widget w, XtPointer clientData,
893 XtPointer callData)
895 int topic;
897 if ((topic = findTopicFromShellWidget((Widget)clientData)) == -1)
898 return; /* shouldn't happen */
899 searchHelpText(HelpWindows[topic], topic, LastSearchString,
900 LastSearchWasAllTopics, LastSearchPos, LastSearchTopic);
903 static void printCB(Widget w, XtPointer clientData, XtPointer callData)
905 int topic, helpStringLen;
906 char *helpString;
908 if ((topic = findTopicFromShellWidget((Widget)clientData)) == -1)
909 return; /* shouldn't happen */
911 helpString = TextGetWrapped(HelpTextPanes[topic], 0,
912 TextGetBuffer(HelpTextPanes[topic])->length, &helpStringLen);
913 PrintString(helpString, helpStringLen, HelpWindows[topic],
914 HelpTitles[topic]);
915 XtFree(helpString);
920 ** Find the topic and text position within that topic targeted by a hyperlink
921 ** with name "link_name". Returns true if the link was successfully interpreted.
923 static int is_known_link(char *link_name, int *topic, int *textPosition)
925 Href * hypertext;
927 /*------------------------------
928 * Direct topic links found here.
929 *------------------------------*/
930 for (*topic=0; HelpTitles[*topic] != NULL; (*topic)++)
932 if (strcmp (link_name, HelpTitles[*topic]) == 0)
934 *textPosition = 0;
935 return 1;
939 /*------------------------------------
940 * Links internal to topics found here.
941 *------------------------------------*/
942 for (hypertext = &H_R[0]; hypertext != NULL; hypertext = hypertext->next)
944 if (strcmp (link_name, hypertext->source) == 0)
946 *topic = hypertext->topic;
947 *textPosition = hypertext->location;
948 return 1;
952 return 0;
956 ** Find the text of a hyperlink from a clicked character position somewhere
957 ** within the hyperlink text, and display the help that it links to.
959 static void followHyperlink(int topic, int charPosition, int newWindow)
961 textDisp *textD = ((TextWidget)HelpTextPanes[topic])->text.textD;
962 char * link_text;
963 int link_topic;
964 int link_pos;
965 int end = charPosition;
966 int begin = charPosition;
967 char whatStyle = BufGetCharacter(textD->styleBuffer, end);
969 /*--------------------------------------------------
970 * Locate beginning and ending of current text style.
971 *--------------------------------------------------*/
972 while (whatStyle == BufGetCharacter(textD->styleBuffer, ++end));
973 while (whatStyle == BufGetCharacter(textD->styleBuffer, begin-1)) begin--;
975 link_text = BufGetRange (textD->buffer, begin, end);
977 if (is_known_link (link_text, &link_topic, &link_pos) )
979 if (HelpWindows[link_topic] != NULL)
981 RaiseShellWindow(HelpWindows[link_topic]);
982 } else
984 if (newWindow)
986 HelpWindows[link_topic] = createHelpPanel(link_topic);
987 } else
989 changeWindowTopic(topic, link_topic);
992 navHistBack[link_topic] = topic;
993 navHistForw[topic] = link_topic;
994 TextSetCursorPos(HelpTextPanes[link_topic], link_pos);
995 adaptNavigationButtons(link_topic);
996 adaptNavigationButtons(topic);
998 XtFree (link_text);
1001 static void helpFocusButtonsAP(Widget w, XEvent *event, String *args,
1002 Cardinal *nArgs)
1004 XmProcessTraversal(w, XmTRAVERSE_NEXT_TAB_GROUP);
1008 * handler for help-button-action(<button-name>)
1009 * Calls the activate callback for the named button widget of the help text win.
1011 static void helpButtonActionAP(Widget w, XEvent *event, String *args,
1012 Cardinal *nArgs)
1014 char buf[80];
1015 int topic;
1016 Widget btn;
1018 if(*nArgs != 1)
1020 fprintf(stderr, "help-button-action: requires exactly one argument.\n");
1021 return;
1024 /* Find the topic being displayed by this widget */
1025 for (topic = 0; topic < NUM_TOPICS; topic++)
1026 if (HelpTextPanes[topic] == w)
1027 break;
1029 if(topic == NUM_TOPICS || HelpWindows[topic] == NULL)
1030 return; /* Shouldn't happen */
1032 /* Compose the button widget name */
1033 strcpy(&buf[0], "helpForm.");
1034 if (strlen(args[0]) <= 70)
1036 strcat(&buf[0], args[0]);
1037 } else
1039 fprintf(stderr, "help-button-action: argument too long");
1040 return;
1043 btn=XtNameToWidget(HelpWindows[topic], buf);
1044 if (btn)
1046 XtCallCallbacks(btn, XmNactivateCallback, HelpWindows[topic]);
1047 } else
1049 fprintf(stderr, "help-button-action: invalid argument: %s\n", args[0]);
1054 * Handler for action help-hyperlink()
1055 * Arguments: none - init: record event position
1056 * "current": if clicked on a link, follow link in same window
1057 * "new": if clicked on a link, follow link in new window
1059 * With the 1st argument "current" or "new" this action can have two additional
1060 * arguments. These arguments must be valid names of XmText actions.
1061 * In this case, the action named in argument #2 is called if the action
1062 * help-hyperlink is about to follow the hyperlink. The Action in argument #3
1063 * is called if no hyperlink has been recognized at the current event position.
1065 static void helpHyperlinkAP(Widget w, XEvent *event, String *args,
1066 Cardinal *nArgs)
1068 XButtonEvent *e = (XButtonEvent *)event;
1069 int topic;
1070 textDisp *textD = ((TextWidget)w)->text.textD;
1071 int clickedPos, newWin;
1072 static int pressX=0, pressY=0;
1074 /* called without arguments we just record coordinates */
1075 if (*nArgs == 0)
1077 pressX = e->x;
1078 pressY = e->y;
1079 return;
1082 newWin = !strcmp(args[0], "new");
1084 if(!newWin && strcmp(args[0], "current")) {
1085 fprintf(stderr, "help-hyperlink: Unrecognized argument %s\n", args[0]);
1086 return;
1090 * If for any reason (pointer moved - drag!, no hyperlink found)
1091 * this action can't follow a hyperlink then execute the the action
1092 * named in arg #3 (if provided)
1094 if (abs(pressX - e->x) > CLICK_THRESHOLD
1095 || abs(pressY - e->y) > CLICK_THRESHOLD)
1097 if (*nArgs == 3)
1098 XtCallActionProc(w, args[2], event, NULL, 0);
1099 return;
1102 clickedPos = TextDXYToCharPos(textD, e->x, e->y);
1103 /* Beware of possible EBCDIC coding! Use the mapping table. */
1104 if (BufGetCharacter(textD->styleBuffer, clickedPos) !=
1105 (char)AlphabetToAsciiTable[(unsigned char)STL_NM_LINK])
1107 if (*nArgs == 3)
1108 XtCallActionProc(w, args[2], event, NULL, 0);
1109 return;
1112 /* Find the topic being displayed by this widget */
1113 for (topic = 0; topic < NUM_TOPICS; topic++)
1114 if (HelpTextPanes[topic] == w)
1115 break;
1117 if (topic == NUM_TOPICS)
1119 /* If we get here someone must have bound help-hyperlink to a non-help
1120 * text widget (!) Or some other really strange thing happened.
1122 if (*nArgs == 3)
1123 XtCallActionProc(w, args[2], event, NULL, 0);
1124 return;
1127 /* If the action help-hyperlink had 3 arguments execute the action
1128 * named in arg #2 before really following the link.
1130 if (*nArgs == 3)
1131 XtCallActionProc(w, args[1], event, NULL, 0);
1133 followHyperlink(topic, clickedPos, newWin);
1137 ** Install the action for following hyperlinks in the help window
1139 void InstallHelpLinkActions(XtAppContext context)
1141 static XtActionsRec Actions[] =
1143 {"help-hyperlink", helpHyperlinkAP},
1144 {"help-focus-buttons", helpFocusButtonsAP},
1145 {"help-button-action", helpButtonActionAP}
1148 XtAppAddActions(context, Actions, XtNumber(Actions));
1152 ** Search the help text. If allSections is true, searches all of the help
1153 ** text, otherwise searches only in parentTopic.
1155 static void searchHelpText(Widget parent, int parentTopic,
1156 const char *searchFor, int allSections, int startPos, int startTopic)
1158 int topic, beginMatch, endMatch;
1159 int found = False;
1160 char * helpText = NULL;
1162 /* Search for the string */
1163 for (topic=startTopic; topic<NUM_TOPICS; topic++)
1165 if (!allSections && topic != parentTopic)
1166 continue;
1167 helpText = stitch(parent, HelpText[topic], NULL);
1169 if (SearchString(helpText, searchFor, SEARCH_FORWARD, SEARCH_LITERAL,
1170 False, topic == startTopic ? startPos : 0, &beginMatch,
1171 &endMatch, NULL, NULL, GetPrefDelimiters()))
1173 found = True;
1174 XtFree(helpText);
1175 break;
1177 XtFree(helpText);
1180 if (!found)
1182 if (startPos != 0 || (allSections && startTopic != 0))
1184 /* Wrap search */
1185 searchHelpText(parent, parentTopic, searchFor, allSections, 0, 0);
1186 return;
1188 DialogF(DF_INF, parent, 1, "String Not Found", "String Not Found", "Dismiss");
1189 return;
1192 /* update navigation history */
1193 if (parentTopic != topic)
1195 navHistForw[parentTopic]= topic;
1196 navHistBack[topic]= parentTopic;
1199 /* If the appropriate window is already up, bring it to the top, if not,
1200 make the parent window become this topic */
1201 changeTopicOrRaise(parentTopic, topic);
1202 BufSelect(TextGetBuffer(HelpTextPanes[topic]), beginMatch, endMatch);
1203 TextSetCursorPos(HelpTextPanes[topic], endMatch);
1205 /* Save the search information for search-again */
1206 strcpy(LastSearchString, searchFor);
1207 LastSearchTopic = topic;
1208 LastSearchPos = endMatch;
1209 LastSearchWasAllTopics = allSections;
1213 ** Change a help window to display a new topic. (Help window data is stored
1214 ** and indexed by topic so if a given topic is already displayed or has been
1215 ** positioned by the user, it can be found and popped back up in the same
1216 ** place.) To change the topic displayed, the stored data has to be relocated.
1218 static void changeWindowTopic(int existingTopic, enum HelpTopic newTopic)
1220 char *helpText, *styleData;
1222 /* Relocate the window/widget/buffer information */
1223 if (newTopic != existingTopic)
1225 HelpWindows[newTopic] = HelpWindows[existingTopic];
1226 HelpWindows[existingTopic] = NULL;
1227 HelpStyleBuffers[newTopic] = HelpStyleBuffers[existingTopic];
1228 HelpStyleBuffers[existingTopic] = NULL;
1229 HelpTextPanes[newTopic] = HelpTextPanes[existingTopic];
1230 HelpTextPanes[existingTopic] = NULL;
1231 setHelpWinTitle(HelpWindows[newTopic], newTopic);
1234 /* Set the existing text widget to display the new text. Because it's
1235 highlighted, we have to turn off highlighting before changing the
1236 displayed text to prevent the text widget from trying to apply the
1237 old, mismatched, highlighting to the new text */
1238 helpText = stitch(HelpTextPanes[newTopic], HelpText[newTopic], &styleData);
1239 TextDAttachHighlightData(((TextWidget)HelpTextPanes[newTopic])->text.textD,
1240 NULL, NULL, 0, '\0', NULL, NULL);
1241 BufSetAll(TextGetBuffer(HelpTextPanes[newTopic]), helpText);
1242 XtFree(helpText);
1243 BufSetAll(HelpStyleBuffers[newTopic], styleData);
1244 XtFree(styleData);
1245 TextDAttachHighlightData(((TextWidget)HelpTextPanes[newTopic])->text.textD,
1246 HelpStyleBuffers[newTopic], HelpStyleInfo, N_STYLES, '\0', NULL,
1247 NULL);
1250 static int findTopicFromShellWidget(Widget shellWidget)
1252 int i;
1254 for (i=0; i<NUM_TOPICS; i++)
1255 if (shellWidget == HelpWindows[i])
1256 return i;
1257 return -1;
1260 static void initNavigationHistory(void) {
1261 static int doInitNavigationHistory = True;
1262 int i;
1264 if (doInitNavigationHistory)
1266 for (i=0; i<NUM_TOPICS; i++)
1267 navHistBack[i] = navHistForw[i] = -1;
1269 doInitNavigationHistory = False;
1273 #if XmVersion == 2000
1274 /* amai: This function may be called before the Motif part
1275 is being initialized. The following, public interface
1276 is known to initialize at least xmUseVersion.
1277 That interface is declared in <Xm/Xm.h> in Motif 1.2 only.
1278 As for Motif 2.1 we don't need this call anymore.
1279 This also holds for the Motif 2.1 version of LessTif
1280 releases > 0.93.0. */
1281 extern void XmRegisterConverters(void);
1282 #endif
1285 /* Print version info to stdout */
1286 void PrintVersion(void)
1288 const char *text;
1290 #if XmVersion < 2001
1291 XmRegisterConverters(); /* see comment above */
1292 #endif
1293 text = getBuildInfo();
1294 puts (text);