Shell command dialog: renamed window to document.
[nedit.git] / source / help.c
blobdd0b3510c769b032d64dd329afe1d99859ba35ef
1 static const char CVSID[] = "$Id: help.c,v 1.102 2004/08/01 10:06:10 yooden 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. In addition, you may distribute version of this program linked to *
12 * Motif or Open Motif. See README for details. *
13 * *
14 * This software is distributed in the hope that it will be useful, but WITHOUT *
15 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or *
16 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License *
17 * for more details. *
18 * *
19 * You should have received a copy of the GNU General Public License along with *
20 * software; if not, write to the Free Software Foundation, Inc., 59 Temple *
21 * Place, Suite 330, Boston, MA 02111-1307 USA *
22 * *
23 * Nirvana Text Editor *
24 * September 10, 1991 *
25 * *
26 * Written by Mark Edel, mostly rewritten by Steve Haehn for new help system, *
27 * December, 2001 *
28 * *
29 *******************************************************************************/
31 #ifdef HAVE_CONFIG_H
32 #include "../config.h"
33 #endif
35 #include "help.h"
36 #include "textBuf.h"
37 #include "text.h"
38 #include "textP.h"
39 #include "textDisp.h"
40 #include "textSel.h"
41 #include "nedit.h"
42 #include "search.h"
43 #include "window.h"
44 #include "preferences.h"
45 #include "help_data.h"
46 #include "file.h"
47 #include "highlight.h"
48 #include "../util/misc.h"
49 #include "../util/DialogF.h"
50 #include "../util/system.h"
52 #include <locale.h>
53 #include <stdlib.h>
54 #include <stdio.h>
55 #include <string.h>
56 #ifdef VMS
57 #include "../util/VMSparam.h"
58 #else
59 #ifndef __MVS__
60 #include <sys/param.h>
61 #endif
62 #endif /*VMS*/
64 #include <Xm/Xm.h>
65 #include <Xm/XmP.h> /* These are for applying style info to help text */
66 #include <Xm/Form.h>
67 #include <Xm/PrimitiveP.h>
68 #include <Xm/ScrolledW.h>
69 #include <Xm/ScrollBar.h>
70 #include <Xm/PushB.h>
71 #ifdef EDITRES
72 #include <X11/Xmu/Editres.h>
73 /* extern void _XEditResCheckMessages(); */
74 #endif /* EDITRES */
76 #ifdef HAVE_DEBUG_H
77 #include "../debug.h"
78 #endif
80 /*============================================================================*/
81 /* SYMBOL DEFINITIONS */
82 /*============================================================================*/
84 #define EOS '\0' /* end-of-string character */
86 #define CLICK_THRESHOLD 5 /* number of pixels mouse may move from its */
87 /* pressed location for mouse-up to be */
88 /* considered a valid click (as opposed to */
89 /* a drag or mouse-pick error) */
91 /*============================================================================*/
92 /* VARIABLE DECLARATIONS */
93 /*============================================================================*/
95 static Widget HelpWindows[NUM_TOPICS] = {NULL};
96 static Widget HelpTextPanes[NUM_TOPICS] = {NULL};
97 static textBuffer *HelpStyleBuffers[NUM_TOPICS] = {NULL};
98 static int navHistForw[NUM_TOPICS];
99 static int navHistBack[NUM_TOPICS];
101 /* Information on the last search for search-again */
102 static char LastSearchString[DF_MAX_PROMPT_LENGTH] = "";
103 static int LastSearchTopic = -1;
104 static int LastSearchPos = 0;
105 static int LastSearchWasAllTopics = False;
107 /* Fonts for each help text style generated by the help generator (setext).
108 The NEdit text widget uses the first help style, 'A', to calculate window
109 width, so making 'A' a fixed font will yield window widths calibrated to
110 match width-dependent fixed font layouts in the help text. */
111 static enum helpFonts StyleFonts[] =
113 /* Fixed fonts, styles: 'A', 'B', 'C', 'D' */
114 FIXED_HELP_FONT, BOLD_FIXED_HELP_FONT, BOLD_FIXED_HELP_FONT,
115 BOLD_ITALIC_FIXED_HELP_FONT,
117 /* Underlined fixed fonts, styles: 'E', 'F', 'G', 'H' */
118 FIXED_HELP_FONT, BOLD_FIXED_HELP_FONT, BOLD_FIXED_HELP_FONT,
119 BOLD_ITALIC_FIXED_HELP_FONT,
121 /* Normal (proportional) fonts, styles: 'I', 'J', 'K', 'L' */
122 HELP_FONT, BOLD_HELP_FONT, ITALIC_HELP_FONT, BOLD_ITALIC_HELP_FONT,
124 /* Underlined fonts, styles: 'M', 'N', 'O', 'P' */
125 HELP_FONT, BOLD_HELP_FONT, ITALIC_HELP_FONT, BOLD_ITALIC_HELP_FONT,
127 /* Link font, style: 'Q' */
128 HELP_FONT,
130 /* Heading fonts, styles: 'R', 'S', 'T' */
131 H1_HELP_FONT, H2_HELP_FONT, H3_HELP_FONT
134 static int StyleUnderlines[] =
136 /* Fixed fonts, styles: 'A', 'B', 'C', 'D' */
137 False, False, False, False,
139 /* Underlined fixed fonts, styles: 'E', 'F', 'G', 'H' */
140 True, True, True, True,
142 /* Normal (proportional) fonts, styles: 'I', 'J', 'K', 'L' */
143 False, False, False, False,
145 /* Underlined fonts, styles: 'M', 'N', 'O', 'P' */
146 True, True, True, True,
148 /* Link font, style: 'Q' */
149 True,
151 /* Heading fonts, styles: 'R', 'S', 'T' */
152 False, False, False
155 #define N_STYLES (XtNumber(StyleFonts))
157 static styleTableEntry HelpStyleInfo[ N_STYLES ];
159 /* Translation table for style codes (A, B, C, ...) to their ASCII codes.
160 For systems using ASCII, this is just a one-to-one mapping, but the
161 table makes it possible to use the style codes also on an EBCDIC system.
163 static unsigned char AlphabetToAsciiTable[256];
165 /* Macro that calculates the zero-based index for a given style, taking
166 into account that the character set may not use ASCII coding, but EBCDIC.
167 The "style" argument must be one of the characters A - Z.
168 In ASCII, this comes down to "style - STYLE_PLAIN". */
169 #define STYLE_INDEX(style) \
170 (AlphabetToAsciiTable[(unsigned char)style] - \
171 AlphabetToAsciiTable[(unsigned char)STYLE_PLAIN])
173 /*============================================================================*/
174 /* PROGRAM PROTOTYPES */
175 /*============================================================================*/
177 static Widget createHelpPanel(enum HelpTopic topic);
178 static void closeCB(Widget w, XtPointer clientData, XtPointer callData);
179 static void prevTopicCB(Widget w, XtPointer clientData, XtPointer callData);
180 static void nextTopicCB(Widget w, XtPointer clientData, XtPointer callData);
181 static void bwHistoryCB(Widget w, XtPointer clientData, XtPointer callData);
182 static void fwHistoryCB(Widget w, XtPointer clientData, XtPointer callData);
183 static void searchHelpCB(Widget w, XtPointer clientData, XtPointer callData);
184 static void searchHelpAgainCB(Widget w, XtPointer clientData,
185 XtPointer callData);
186 static void printCB(Widget w, XtPointer clientData, XtPointer callData);
187 static char *stitch(Widget parent, char **string_list,char **styleMap);
188 static void searchHelpText(Widget parent, int parentTopic,
189 const char *searchFor, int allSections, int startPos, int startTopic);
190 static void changeWindowTopic(int existingTopic, enum HelpTopic newTopic);
191 static int findTopicFromShellWidget(Widget shellWidget);
192 static void loadFontsAndColors(Widget parent, int style);
193 static void initNavigationHistory(void);
195 #ifdef HAVE__XMVERSIONSTRING
196 extern char _XmVersionString[];
197 #else
198 static char _XmVersionString[] = "unknown";
199 #endif
201 /*============================================================================*/
202 /*================================= PROGRAMS =================================*/
203 /*============================================================================*/
206 ** Create a string containing information on the build environment. Returned
207 ** string must NOT be freed by caller.
210 static char *bldInfoString = NULL;
212 static void freeBuildInfo(void)
214 /* This keeps memory leak detectors happy */
215 XtFree(bldInfoString);
218 static const char *getBuildInfo(void)
220 const char * bldFormat =
221 "%s\n"
222 " Built on: %s, %s, %s\n"
223 " Built at: %s, %s\n"
224 " With Motif: %d.%d.%d [%s]"
225 #ifdef BUILD_BROKEN_NEDIT
226 " (KNOWN-BAD)\n"
227 #elif defined BUILD_UNTESTED_NEDIT
228 " (UNTESTED)\n"
229 #else
230 "\n"
231 #endif
232 "Running Motif: %d.%d [%s]\n"
233 " Server: %s %d\n"
234 " Visual: %s\n"
235 " Locale: %s\n"
236 #ifdef BUILD_BROKEN_NEDIT
237 "\nThis NEdit was built with a known-bad version of Motif. Please\n"
238 "do not report any bugs you encounter unless you can reproduce\n"
239 "them with a known-good binary from the www.nedit.org website.\n"
240 "If this binary was supplied with your Linux distribution please\n"
241 "file a bug report with them asking them to build NEdit with a\n"
242 "known-good version of Motif.\n"
243 #endif
245 const char * visualClass[] = {"StaticGray", "GrayScale",
246 "StaticColor", "PseudoColor",
247 "TrueColor", "DirectColor"};
248 const char *locale;
250 if (bldInfoString == NULL)
252 char visualStr[500] = "<unknown>";
254 if (TheDisplay) {
255 Visual *visual;
256 int depth;
257 Colormap map;
258 Boolean usingDefaultVisual = FindBestVisual(TheDisplay, APP_NAME,
259 APP_CLASS, &visual,
260 &depth, &map);
261 sprintf(visualStr,"%d-bit %s (ID %#lx%s)",
262 depth,
263 visualClass[visual->class],
264 visual->visualid,
265 usingDefaultVisual ? ", Default" : "");
268 bldInfoString = XtMalloc (strlen (bldFormat) + 1024);
269 locale = setlocale(LC_MESSAGES, "");
271 sprintf(bldInfoString, bldFormat,
272 NEditVersion,
273 COMPILE_OS, COMPILE_MACHINE, COMPILE_COMPILER,
274 linkdate, linktime,
275 XmVERSION, XmREVISION, XmUPDATE_LEVEL,
276 XmVERSION_STRING,
277 xmUseVersion/1000, xmUseVersion%1000,
278 _XmVersionString,
279 (NULL == TheDisplay ? "<unknown>" : ServerVendor(TheDisplay)),
280 (NULL == TheDisplay ? 0 : VendorRelease(TheDisplay)),
281 visualStr,
282 locale ? locale : "None");
284 atexit(freeBuildInfo);
287 return bldInfoString;
291 ** Initialization for help system data, needs to be done only once.
293 static void initHelpStyles (Widget parent)
295 static int styleTableInitialized = False;
297 if (! styleTableInitialized)
299 Pixel fg;
300 int styleIndex;
301 char ** line;
303 XtVaGetValues(parent, XtNforeground, &fg, NULL);
305 for (styleIndex = 0; styleIndex < STL_HD + MAX_HEADING; styleIndex++)
307 HelpStyleInfo[ styleIndex ].color = fg;
308 HelpStyleInfo[ styleIndex ].underline = StyleUnderlines[styleIndex];
309 HelpStyleInfo[ styleIndex ].font = NULL;
312 styleTableInitialized = True;
314 /*-------------------------------------------------------
315 * Only attempt to add build information to version text
316 * when string formatting symbols are present in the text.
317 * This special case is needed to incorporate this
318 * dynamically created information into the static help.
319 *-------------------------------------------------------*/
320 for (line = HelpText[ HELP_VERSION ]; *line != NULL; line++)
322 /*--------------------------------------------------
323 * If and when this printf format is found in the
324 * version help text, replace that line with the
325 * build information. Then stitching the help text
326 * will have the final count of characters to use.
327 *--------------------------------------------------*/
328 if (strstr (*line, "%s") != NULL)
330 const char * bldInfo = getBuildInfo();
331 char * text = XtMalloc (strlen (*line) + strlen (bldInfo));
332 sprintf (text, *line, bldInfo);
333 *line = text;
334 break;
338 /*---------------------------------------------------------
339 * Also initialize the alphabet-to-ASCII-code table (to
340 * make the style mapping also work on EBCDIC).
341 * DON'T use 'A' to initialize the table! 'A' != 65 in EBCDIC.
342 *--------------------------------------------------------*/
343 AlphabetToAsciiTable[(unsigned char)'A'] = ASCII_A + 0;
344 AlphabetToAsciiTable[(unsigned char)'B'] = ASCII_A + 1;
345 AlphabetToAsciiTable[(unsigned char)'C'] = ASCII_A + 2;
346 AlphabetToAsciiTable[(unsigned char)'D'] = ASCII_A + 3;
347 AlphabetToAsciiTable[(unsigned char)'E'] = ASCII_A + 4;
348 AlphabetToAsciiTable[(unsigned char)'F'] = ASCII_A + 5;
349 AlphabetToAsciiTable[(unsigned char)'G'] = ASCII_A + 6;
350 AlphabetToAsciiTable[(unsigned char)'H'] = ASCII_A + 7;
351 AlphabetToAsciiTable[(unsigned char)'I'] = ASCII_A + 8;
352 AlphabetToAsciiTable[(unsigned char)'J'] = ASCII_A + 9;
353 AlphabetToAsciiTable[(unsigned char)'K'] = ASCII_A + 10;
354 AlphabetToAsciiTable[(unsigned char)'L'] = ASCII_A + 11;
355 AlphabetToAsciiTable[(unsigned char)'M'] = ASCII_A + 12;
356 AlphabetToAsciiTable[(unsigned char)'N'] = ASCII_A + 13;
357 AlphabetToAsciiTable[(unsigned char)'O'] = ASCII_A + 14;
358 AlphabetToAsciiTable[(unsigned char)'P'] = ASCII_A + 15;
359 AlphabetToAsciiTable[(unsigned char)'Q'] = ASCII_A + 16;
360 AlphabetToAsciiTable[(unsigned char)'R'] = ASCII_A + 17;
361 AlphabetToAsciiTable[(unsigned char)'S'] = ASCII_A + 18;
362 AlphabetToAsciiTable[(unsigned char)'T'] = ASCII_A + 19;
363 AlphabetToAsciiTable[(unsigned char)'U'] = ASCII_A + 20;
364 AlphabetToAsciiTable[(unsigned char)'V'] = ASCII_A + 21;
365 AlphabetToAsciiTable[(unsigned char)'W'] = ASCII_A + 22;
366 AlphabetToAsciiTable[(unsigned char)'X'] = ASCII_A + 23;
367 AlphabetToAsciiTable[(unsigned char)'Y'] = ASCII_A + 24;
368 AlphabetToAsciiTable[(unsigned char)'Z'] = ASCII_A + 25;
373 ** Help fonts are not loaded until they're actually needed. This function
374 ** checks if the style's font is loaded, and loads it if it's not.
376 static void loadFontsAndColors(Widget parent, int style)
378 XFontStruct *font;
379 int r,g,b;
380 if (HelpStyleInfo[STYLE_INDEX(style)].font == NULL)
382 font = XLoadQueryFont(XtDisplay(parent),
383 GetPrefHelpFontName(StyleFonts[STYLE_INDEX(style)]));
384 if (font == NULL)
386 fprintf(stderr, "NEdit: help font, %s, not available\n",
387 GetPrefHelpFontName(StyleFonts[STYLE_INDEX(style)]));
388 font = XLoadQueryFont(XtDisplay(parent), "fixed");
389 if (font == NULL)
391 fprintf(stderr, "NEdit: fallback help font, \"fixed\", not "
392 "available, cannot continue\n");
393 exit(EXIT_FAILURE);
396 HelpStyleInfo[STYLE_INDEX(style)].font = font;
398 if (style == STL_NM_LINK)
399 HelpStyleInfo[STYLE_INDEX(style)].color =
400 AllocColor(parent, GetPrefHelpLinkColor(), &r, &g, &b);
404 static void adaptNavigationButtons(int topic) {
405 Widget btn;
407 if(HelpWindows[topic] == NULL)
408 return; /* Shouldn't happen */
410 btn=XtNameToWidget(HelpWindows[topic], "helpForm.prevTopic");
411 if(btn)
413 if(topic > 0)
414 XtSetSensitive(btn, True);
415 else
416 XtSetSensitive(btn, False);
419 btn=XtNameToWidget(HelpWindows[topic], "helpForm.nextTopic");
420 if(btn)
422 if(topic < (NUM_TOPICS - 1))
423 XtSetSensitive(btn, True);
424 else
425 XtSetSensitive(btn, False);
428 btn=XtNameToWidget(HelpWindows[topic], "helpForm.histBack");
429 if(btn)
431 if(navHistBack[topic] != -1)
432 XtSetSensitive(btn, True);
433 else
434 XtSetSensitive(btn, False);
437 btn=XtNameToWidget(HelpWindows[topic], "helpForm.histForw");
438 if(btn)
440 if(navHistForw[topic] != -1)
441 XtSetSensitive(btn, True);
442 else
443 XtSetSensitive(btn, False);
449 ** Put together stored help strings to create the text and optionally the style
450 ** information for a given help topic.
452 static char * stitch (
454 Widget parent, /* used for dynamic font/color allocation */
455 char ** string_list, /* given help strings to stitch together */
456 char ** styleMap /* NULL, or a place to store help styles */
459 char * cp;
460 char * section, * sp; /* resulting help text section */
461 char * styleData, * sdp; /* resulting style data for text */
462 char style = STYLE_PLAIN; /* start off each section with this style */
463 int total_size = 0; /* help text section size */
464 char ** crnt_line;
466 /*----------------------------------------------------
467 * How many characters are there going to be displayed?
468 *----------------------------------------------------*/
469 for (crnt_line = string_list; *crnt_line != NULL; crnt_line++)
471 for (cp = *crnt_line; *cp != EOS; cp++)
473 /*---------------------------------------------
474 * The help text has embedded style information
475 * consisting of the style marker and a single
476 * character style, for a total of 2 characters.
477 * This style information is not to be included
478 * in the character counting below.
479 *---------------------------------------------*/
480 if (*cp != STYLE_MARKER) {
481 total_size++;
483 else {
484 cp++; /* skipping style marker, loop will handle style */
489 /*--------------------------------------------------------
490 * Get the needed space, one area for the help text being
491 * stitched together, another for the styles to be applied.
492 *--------------------------------------------------------*/
493 sp = section = XtMalloc (total_size +1);
494 sdp = styleData = (styleMap) ? XtMalloc (total_size +1) : NULL;
495 *sp = EOS;
497 /*--------------------------------------------
498 * Fill in the newly acquired contiguous space
499 * with help text and style information.
500 *--------------------------------------------*/
501 for (crnt_line = string_list; *crnt_line != NULL; crnt_line++)
503 for (cp = *crnt_line; *cp != EOS; cp++)
505 if (*cp == STYLE_MARKER)
507 style = *(++cp);
508 loadFontsAndColors(parent, style);
509 } else
511 *(sp++) = *cp;
513 /* Beware of possible EBCDIC coding! Use the mapping table. */
514 if (styleMap)
515 *(sdp++) = AlphabetToAsciiTable[(unsigned char)style];
520 *sp = EOS;
522 /*-----------------------------------------
523 * Only deal with style map, when available.
524 *-----------------------------------------*/
525 if (styleMap) {
526 *styleMap = styleData;
527 *sdp = EOS;
530 return section;
534 ** Display help for subject "topic". "parent" is a widget over which the help
535 ** dialog may be posted. Help dialogs are preserved when popped down by the
536 ** user, and may appear posted over a previous parent, regardless of the parent
537 ** argument.
539 void Help(enum HelpTopic topic)
541 if (HelpWindows[topic] != NULL)
542 RaiseShellWindow(HelpWindows[topic]);
543 else
544 HelpWindows[topic] = createHelpPanel(topic);
545 adaptNavigationButtons(topic);
549 /* Setup Window/Icon title for the help window. */
550 static void setHelpWinTitle(Widget win, enum HelpTopic topic)
552 char * buf, *topStr=HelpTitles[topic];
554 buf=malloc(strlen(topStr) + 24);
555 topic++;
557 sprintf(buf, "NEdit Help (%d)", (int)topic);
558 XtVaSetValues(win, XmNiconName, buf, NULL);
560 sprintf(buf, "NEdit Help: %s (%d)", topStr, (int)topic);
561 XtVaSetValues(win, XmNtitle, buf, NULL);
563 free(buf);
567 ** Create a new help window displaying a given subject, "topic"
569 ** Important hint: If this widget is restructured or the name of the text
570 ** subwidget is changed don't forget to adapt the default translations of the
571 ** help text. They are located in nedit.c, look for
572 ** static char *fallbackResources
573 ** (currently: nedit.helpForm.sw.helpText*translations...)
575 static Widget createHelpPanel(enum HelpTopic topic)
577 Arg al[50];
578 int ac;
579 Widget appShell, btn, closeBtn, form, btnFW;
580 Widget sw, hScrollBar, vScrollBar;
581 XmString st1;
582 char * helpText = NULL;
583 char * styleData = NULL;
585 ac = 0;
586 XtSetArg(al[ac], XmNdeleteResponse, XmDO_NOTHING); ac++;
587 appShell = CreateWidget(TheAppShell, "help",
588 topLevelShellWidgetClass, al, ac);
589 AddSmallIcon(appShell);
590 /* With openmotif 2.1.30, a crash may occur when the text widget of the
591 help window is (slowly) resized to a zero width. By imposing a
592 minimum _window_ width, we can work around this problem. The minimum
593 width should be larger than the width of the scrollbar. 50 is probably
594 a safe value; this leaves room for a few characters */
595 XtVaSetValues(appShell, XtNminWidth, 50, NULL);
596 form = XtVaCreateManagedWidget("helpForm", xmFormWidgetClass, appShell,
597 NULL);
598 XtVaSetValues(form, XmNshadowThickness, 0, NULL);
600 /* Create the bottom row of buttons */
601 btn = XtVaCreateManagedWidget("find", xmPushButtonWidgetClass, form,
602 XmNlabelString, st1=XmStringCreateSimple("Find..."),
603 XmNmnemonic, 'F',
604 XmNbottomAttachment, XmATTACH_FORM,
605 XmNleftAttachment, XmATTACH_POSITION,
606 XmNleftPosition, 3,
607 XmNrightAttachment, XmATTACH_POSITION,
608 XmNrightPosition, 25,
609 NULL);
610 XtAddCallback(btn, XmNactivateCallback, searchHelpCB, appShell);
611 XmStringFree(st1);
613 btn = XtVaCreateManagedWidget("findAgain", xmPushButtonWidgetClass, form,
614 XmNlabelString, st1=XmStringCreateSimple("Find Again"),
615 XmNmnemonic, 'A',
616 XmNbottomAttachment, XmATTACH_FORM,
617 XmNleftAttachment, XmATTACH_POSITION,
618 XmNleftPosition, 27,
619 XmNrightAttachment, XmATTACH_POSITION,
620 XmNrightPosition, 49,
621 NULL);
622 XtAddCallback(btn, XmNactivateCallback, searchHelpAgainCB, appShell);
623 XmStringFree(st1);
625 btn = XtVaCreateManagedWidget("print", xmPushButtonWidgetClass, form,
626 XmNlabelString, st1=XmStringCreateSimple("Print..."),
627 XmNmnemonic, 'P',
628 XmNbottomAttachment, XmATTACH_FORM,
629 XmNleftAttachment, XmATTACH_POSITION,
630 XmNleftPosition, 51,
631 XmNrightAttachment, XmATTACH_POSITION,
632 XmNrightPosition, 73,
633 NULL);
634 XtAddCallback(btn, XmNactivateCallback, printCB, appShell);
635 XmStringFree(st1);
637 closeBtn = XtVaCreateManagedWidget("close",
638 xmPushButtonWidgetClass, form,
639 XmNlabelString, st1=XmStringCreateSimple("Close"),
640 XmNbottomAttachment, XmATTACH_FORM,
641 XmNleftAttachment, XmATTACH_POSITION,
642 XmNleftPosition, 75,
643 XmNrightAttachment, XmATTACH_POSITION,
644 XmNrightPosition, 97,
645 NULL);
646 XtAddCallback(closeBtn, XmNactivateCallback, closeCB, appShell);
647 XmStringFree(st1);
649 /* Create the next row of buttons (for navigation) */
650 btn = XtVaCreateManagedWidget("prevTopic", xmPushButtonWidgetClass, form,
651 XmNlabelString, st1=XmStringCreateSimple("<< Browse"),
652 XmNmnemonic, 'o',
653 XmNbottomAttachment, XmATTACH_WIDGET,
654 XmNbottomWidget, closeBtn,
655 XmNleftAttachment, XmATTACH_POSITION,
656 XmNleftPosition, 51,
657 XmNrightAttachment, XmATTACH_POSITION,
658 XmNrightPosition, 73,
659 NULL);
660 XtAddCallback(btn, XmNactivateCallback, prevTopicCB, appShell);
661 XmStringFree(st1);
663 btn = XtVaCreateManagedWidget("nextTopic", xmPushButtonWidgetClass, form,
664 XmNlabelString, st1=XmStringCreateSimple("Browse >>"),
665 XmNmnemonic, 'e',
666 XmNbottomAttachment, XmATTACH_WIDGET,
667 XmNbottomWidget, closeBtn,
668 XmNleftAttachment, XmATTACH_POSITION,
669 XmNleftPosition, 75,
670 XmNrightAttachment, XmATTACH_POSITION,
671 XmNrightPosition, 97,
672 NULL);
673 XtAddCallback(btn, XmNactivateCallback, nextTopicCB, appShell);
674 XmStringFree(st1);
676 btn = XtVaCreateManagedWidget("histBack", xmPushButtonWidgetClass, form,
677 XmNlabelString, st1=XmStringCreateSimple("Back"),
678 XmNmnemonic, 'B',
679 XmNbottomAttachment, XmATTACH_WIDGET,
680 XmNbottomWidget, closeBtn,
681 XmNleftAttachment, XmATTACH_POSITION,
682 XmNleftPosition, 3,
683 XmNrightAttachment, XmATTACH_POSITION,
684 XmNrightPosition, 25,
685 NULL);
686 XtAddCallback(btn, XmNactivateCallback, bwHistoryCB, appShell);
687 XmStringFree(st1);
689 btnFW = XtVaCreateManagedWidget("histForw", xmPushButtonWidgetClass, form,
690 XmNlabelString, st1=XmStringCreateSimple("Forward"),
691 XmNmnemonic, 'w',
692 XmNbottomAttachment, XmATTACH_WIDGET,
693 XmNbottomWidget, closeBtn,
694 XmNleftAttachment, XmATTACH_POSITION,
695 XmNleftPosition, 27,
696 XmNrightAttachment, XmATTACH_POSITION,
697 XmNrightPosition, 49,
698 NULL);
699 XtAddCallback(btnFW, XmNactivateCallback, fwHistoryCB, appShell);
700 XmStringFree(st1);
702 /* Create a text widget inside of a scrolled window widget */
703 sw = XtVaCreateManagedWidget("sw", xmScrolledWindowWidgetClass, form,
704 XmNshadowThickness, 2,
705 XmNtopAttachment, XmATTACH_FORM,
706 XmNleftAttachment, XmATTACH_FORM,
707 XmNrightAttachment, XmATTACH_FORM,
708 XmNbottomAttachment, XmATTACH_WIDGET,
709 XmNbottomWidget, btnFW,
710 NULL);
711 hScrollBar = XtVaCreateManagedWidget("hScrollBar",
712 xmScrollBarWidgetClass, sw,
713 XmNorientation, XmHORIZONTAL,
714 XmNrepeatDelay, 10,
715 NULL);
716 vScrollBar = XtVaCreateManagedWidget("vScrollBar",
717 xmScrollBarWidgetClass, sw,
718 XmNorientation, XmVERTICAL,
719 XmNrepeatDelay, 10,
720 NULL);
721 /* Make sure the fixed size help font is loaded, such that we can base
722 our text widget size calculation on it. */
723 loadFontsAndColors(sw, 'A');
724 HelpTextPanes[topic] = XtVaCreateManagedWidget("helpText",
725 textWidgetClass, sw,
726 textNfont, HelpStyleInfo[0].font, /* MUST correspond to 'A' above */
727 textNrows, 30,
728 textNcolumns, 65,
729 textNbacklightCharTypes, NULL,
730 textNhScrollBar, hScrollBar,
731 textNvScrollBar, vScrollBar,
732 textNreadOnly, True,
733 textNcontinuousWrap, True,
734 textNautoShowInsertPos, True,
735 NULL);
736 XtVaSetValues(sw, XmNworkWindow, HelpTextPanes[topic],
737 XmNhorizontalScrollBar, hScrollBar,
738 XmNverticalScrollBar, vScrollBar,
739 NULL);
741 /* Initialize help style information, if it hasn't already been init'd */
742 initHelpStyles (HelpTextPanes[topic]);
744 /* Put together the text to display and separate it into parallel text
745 and style data for display by the widget */
746 helpText = stitch (HelpTextPanes[topic], HelpText[topic], &styleData);
748 /* Stuff the text into the widget's text buffer */
749 BufSetAll (TextGetBuffer (HelpTextPanes[topic]) , helpText);
750 XtFree (helpText);
752 /* Create a style buffer for the text widget and fill it with the style
753 data which was generated along with the text content */
754 HelpStyleBuffers[topic] = BufCreate();
755 BufSetAll(HelpStyleBuffers[topic], styleData);
756 XtFree (styleData);
757 TextDAttachHighlightData(((TextWidget)HelpTextPanes[topic])->text.textD,
758 HelpStyleBuffers[topic], HelpStyleInfo, N_STYLES, '\0', NULL, NULL);
760 /* This shouldn't be necessary (what's wrong in text.c?) */
761 HandleXSelections(HelpTextPanes[topic]);
763 /* Process dialog mnemonic keys */
764 AddDialogMnemonicHandler(form, FALSE);
766 /* Set the default button */
767 XtVaSetValues(form, XmNdefaultButton, closeBtn, NULL);
768 XtVaSetValues(form, XmNcancelButton, closeBtn, NULL);
770 /* realize all of the widgets in the new window */
771 RealizeWithoutForcingPosition(appShell);
773 /* Give the text pane the initial focus */
774 XmProcessTraversal(HelpTextPanes[topic], XmTRAVERSE_CURRENT);
776 /* Make close command in window menu gracefully prompt for close */
777 AddMotifCloseCallback(appShell, (XtCallbackProc)closeCB, appShell);
779 /* Initialize navigation information, if it hasn't already been init'd */
780 initNavigationHistory();
782 /* Set the window title */
783 setHelpWinTitle(appShell, topic);
786 #ifdef EDITRES
787 XtAddEventHandler (appShell, (EventMask)0, True,
788 (XtEventHandler)_XEditResCheckMessages, NULL);
789 #endif /* EDITRES */
791 return appShell;
794 static void changeTopicOrRaise(int existingTopic, int newTopic) {
795 if(HelpWindows[newTopic] == NULL)
797 changeWindowTopic(existingTopic, newTopic);
798 adaptNavigationButtons(newTopic);
799 } else
801 RaiseShellWindow(HelpWindows[newTopic]);
802 adaptNavigationButtons(existingTopic);
803 adaptNavigationButtons(newTopic);
809 ** Callbacks for window controls
811 static void closeCB(Widget w, XtPointer clientData, XtPointer callData)
813 int topic;
815 if ((topic = findTopicFromShellWidget((Widget)clientData)) == -1)
816 return;
818 /* I don't understand the mechanism by which this can be called with
819 HelpWindows[topic] as NULL, but it has happened */
820 XtDestroyWidget(HelpWindows[topic]);
821 HelpWindows[topic] = NULL;
822 if (HelpStyleBuffers[topic] != NULL)
824 BufFree(HelpStyleBuffers[topic]);
825 HelpStyleBuffers[topic] = NULL;
829 static void prevTopicCB(Widget w, XtPointer clientData, XtPointer callData)
830 { int topic;
832 if ((topic = findTopicFromShellWidget((Widget)clientData)) == -1)
833 return; /* shouldn't happen */
835 topic--;
836 if(topic >= 0)
837 changeTopicOrRaise(topic+1, topic);
840 static void nextTopicCB(Widget w, XtPointer clientData, XtPointer callData)
841 { int topic;
843 if ((topic = findTopicFromShellWidget((Widget)clientData)) == -1)
844 return; /* shouldn't happen */
846 topic++;
847 if(topic < NUM_TOPICS)
848 changeTopicOrRaise(topic-1, topic);
851 static void bwHistoryCB(Widget w, XtPointer clientData, XtPointer callData)
852 { int topic, goTo;
854 if ((topic = findTopicFromShellWidget((Widget)clientData)) == -1)
855 return; /* shouldn't happen */
857 goTo=navHistBack[topic];
858 if(goTo >= 0 && goTo < NUM_TOPICS)
860 navHistForw[goTo]=topic;
861 changeTopicOrRaise(topic, goTo);
865 static void fwHistoryCB(Widget w, XtPointer clientData, XtPointer callData)
866 { int topic, goTo;
868 if ((topic = findTopicFromShellWidget((Widget)clientData)) == -1)
869 return; /* shouldn't happen */
871 goTo=navHistForw[topic];
872 if(goTo >= 0 && goTo < NUM_TOPICS)
874 navHistBack[goTo]=topic;
875 changeTopicOrRaise(topic, goTo);
879 static void searchHelpCB(Widget w, XtPointer clientData, XtPointer callData)
881 char promptText[DF_MAX_PROMPT_LENGTH];
882 int response, topic;
883 static char **searchHistory = NULL;
884 static int nHistoryStrings = 0;
886 if ((topic = findTopicFromShellWidget((Widget)clientData)) == -1)
887 return; /* shouldn't happen */
888 SetDialogFPromptHistory(searchHistory, nHistoryStrings);
889 response = DialogF(DF_PROMPT, HelpWindows[topic], 3, "Find",
890 "Search for: (use up arrow key to recall previous)", promptText,
891 "This Section", "All Sections", "Cancel");
892 if (response == 3)
893 return;
894 AddToHistoryList(promptText, &searchHistory, &nHistoryStrings);
895 searchHelpText(HelpWindows[topic], topic, promptText, response == 2, 0, 0);
898 static void searchHelpAgainCB(Widget w, XtPointer clientData,
899 XtPointer callData)
901 int topic;
903 if ((topic = findTopicFromShellWidget((Widget)clientData)) == -1)
904 return; /* shouldn't happen */
905 searchHelpText(HelpWindows[topic], topic, LastSearchString,
906 LastSearchWasAllTopics, LastSearchPos, LastSearchTopic);
909 static void printCB(Widget w, XtPointer clientData, XtPointer callData)
911 int topic, helpStringLen;
912 char *helpString;
914 if ((topic = findTopicFromShellWidget((Widget)clientData)) == -1)
915 return; /* shouldn't happen */
917 helpString = TextGetWrapped(HelpTextPanes[topic], 0,
918 TextGetBuffer(HelpTextPanes[topic])->length, &helpStringLen);
919 PrintString(helpString, helpStringLen, HelpWindows[topic],
920 HelpTitles[topic]);
921 XtFree(helpString);
926 ** Find the topic and text position within that topic targeted by a hyperlink
927 ** with name "link_name". Returns true if the link was successfully interpreted.
929 static int is_known_link(char *link_name, int *topic, int *textPosition)
931 Href * hypertext;
933 /*------------------------------
934 * Direct topic links found here.
935 *------------------------------*/
936 for (*topic=0; HelpTitles[*topic] != NULL; (*topic)++)
938 if (strcmp (link_name, HelpTitles[*topic]) == 0)
940 *textPosition = 0;
941 return 1;
945 /*------------------------------------
946 * Links internal to topics found here.
947 *------------------------------------*/
948 for (hypertext = &H_R[0]; hypertext != NULL; hypertext = hypertext->next)
950 if (strcmp (link_name, hypertext->source) == 0)
952 *topic = hypertext->topic;
953 *textPosition = hypertext->location;
954 return 1;
958 return 0;
962 ** Find the text of a hyperlink from a clicked character position somewhere
963 ** within the hyperlink text, and display the help that it links to.
965 static void followHyperlink(int topic, int charPosition, int newWindow)
967 textDisp *textD = ((TextWidget)HelpTextPanes[topic])->text.textD;
968 char * link_text;
969 int link_topic;
970 int link_pos;
971 int end = charPosition;
972 int begin = charPosition;
973 char whatStyle = BufGetCharacter(textD->styleBuffer, end);
975 /*--------------------------------------------------
976 * Locate beginning and ending of current text style.
977 *--------------------------------------------------*/
978 while (whatStyle == BufGetCharacter(textD->styleBuffer, ++end));
979 while (whatStyle == BufGetCharacter(textD->styleBuffer, begin-1)) begin--;
981 link_text = BufGetRange (textD->buffer, begin, end);
983 if (is_known_link (link_text, &link_topic, &link_pos) )
985 if (HelpWindows[link_topic] != NULL)
987 RaiseShellWindow(HelpWindows[link_topic]);
988 } else
990 if (newWindow)
992 HelpWindows[link_topic] = createHelpPanel(link_topic);
993 } else
995 changeWindowTopic(topic, link_topic);
998 navHistBack[link_topic] = topic;
999 navHistForw[topic] = link_topic;
1000 TextSetCursorPos(HelpTextPanes[link_topic], link_pos);
1001 adaptNavigationButtons(link_topic);
1002 adaptNavigationButtons(topic);
1004 XtFree (link_text);
1007 static void helpFocusButtonsAP(Widget w, XEvent *event, String *args,
1008 Cardinal *nArgs)
1010 XmProcessTraversal(w, XmTRAVERSE_NEXT_TAB_GROUP);
1014 * handler for help-button-action(<button-name>)
1015 * Calls the activate callback for the named button widget of the help text win.
1017 static void helpButtonActionAP(Widget w, XEvent *event, String *args,
1018 Cardinal *nArgs)
1020 char buf[80];
1021 int topic;
1022 Widget btn;
1024 if(*nArgs != 1)
1026 fprintf(stderr, "help-button-action: requires exactly one argument.\n");
1027 return;
1030 /* Find the topic being displayed by this widget */
1031 for (topic = 0; topic < NUM_TOPICS; topic++)
1032 if (HelpTextPanes[topic] == w)
1033 break;
1035 if(topic == NUM_TOPICS || HelpWindows[topic] == NULL)
1036 return; /* Shouldn't happen */
1038 /* Compose the button widget name */
1039 strcpy(&buf[0], "helpForm.");
1040 if (strlen(args[0]) <= 70)
1042 strcat(&buf[0], args[0]);
1043 } else
1045 fprintf(stderr, "help-button-action: argument too long");
1046 return;
1049 btn=XtNameToWidget(HelpWindows[topic], buf);
1050 if (btn)
1052 XtCallCallbacks(btn, XmNactivateCallback, HelpWindows[topic]);
1053 } else
1055 fprintf(stderr, "help-button-action: invalid argument: %s\n", args[0]);
1060 * Handler for action help-hyperlink()
1061 * Arguments: none - init: record event position
1062 * "current": if clicked on a link, follow link in same window
1063 * "new": if clicked on a link, follow link in new window
1065 * With the 1st argument "current" or "new" this action can have two additional
1066 * arguments. These arguments must be valid names of XmText actions.
1067 * In this case, the action named in argument #2 is called if the action
1068 * help-hyperlink is about to follow the hyperlink. The Action in argument #3
1069 * is called if no hyperlink has been recognized at the current event position.
1071 static void helpHyperlinkAP(Widget w, XEvent *event, String *args,
1072 Cardinal *nArgs)
1074 XButtonEvent *e = (XButtonEvent *)event;
1075 int topic;
1076 textDisp *textD = ((TextWidget)w)->text.textD;
1077 int clickedPos, newWin;
1078 static int pressX=0, pressY=0;
1080 /* called without arguments we just record coordinates */
1081 if (*nArgs == 0)
1083 pressX = e->x;
1084 pressY = e->y;
1085 return;
1088 newWin = !strcmp(args[0], "new");
1090 if(!newWin && strcmp(args[0], "current")) {
1091 fprintf(stderr, "help-hyperlink: Unrecognized argument %s\n", args[0]);
1092 return;
1096 * If for any reason (pointer moved - drag!, no hyperlink found)
1097 * this action can't follow a hyperlink then execute the the action
1098 * named in arg #3 (if provided)
1100 if (abs(pressX - e->x) > CLICK_THRESHOLD
1101 || abs(pressY - e->y) > CLICK_THRESHOLD)
1103 if (*nArgs == 3)
1104 XtCallActionProc(w, args[2], event, NULL, 0);
1105 return;
1108 clickedPos = TextDXYToCharPos(textD, e->x, e->y);
1109 /* Beware of possible EBCDIC coding! Use the mapping table. */
1110 if (BufGetCharacter(textD->styleBuffer, clickedPos) !=
1111 (char)AlphabetToAsciiTable[(unsigned char)STL_NM_LINK])
1113 if (*nArgs == 3)
1114 XtCallActionProc(w, args[2], event, NULL, 0);
1115 return;
1118 /* Find the topic being displayed by this widget */
1119 for (topic = 0; topic < NUM_TOPICS; topic++)
1120 if (HelpTextPanes[topic] == w)
1121 break;
1123 if (topic == NUM_TOPICS)
1125 /* If we get here someone must have bound help-hyperlink to a non-help
1126 * text widget (!) Or some other really strange thing happened.
1128 if (*nArgs == 3)
1129 XtCallActionProc(w, args[2], event, NULL, 0);
1130 return;
1133 /* If the action help-hyperlink had 3 arguments execute the action
1134 * named in arg #2 before really following the link.
1136 if (*nArgs == 3)
1137 XtCallActionProc(w, args[1], event, NULL, 0);
1139 followHyperlink(topic, clickedPos, newWin);
1143 ** Install the action for following hyperlinks in the help window
1145 void InstallHelpLinkActions(XtAppContext context)
1147 static XtActionsRec Actions[] =
1149 {"help-hyperlink", helpHyperlinkAP},
1150 {"help-focus-buttons", helpFocusButtonsAP},
1151 {"help-button-action", helpButtonActionAP}
1154 XtAppAddActions(context, Actions, XtNumber(Actions));
1158 ** Search the help text. If allSections is true, searches all of the help
1159 ** text, otherwise searches only in parentTopic.
1161 static void searchHelpText(Widget parent, int parentTopic,
1162 const char *searchFor, int allSections, int startPos, int startTopic)
1164 int topic, beginMatch, endMatch;
1165 int found = False;
1166 char * helpText = NULL;
1168 /* Search for the string */
1169 for (topic=startTopic; topic<NUM_TOPICS; topic++)
1171 if (!allSections && topic != parentTopic)
1172 continue;
1173 helpText = stitch(parent, HelpText[topic], NULL);
1175 if (SearchString(helpText, searchFor, SEARCH_FORWARD, SEARCH_LITERAL,
1176 False, topic == startTopic ? startPos : 0, &beginMatch,
1177 &endMatch, NULL, NULL, GetPrefDelimiters()))
1179 found = True;
1180 XtFree(helpText);
1181 break;
1183 XtFree(helpText);
1186 if (!found)
1188 if (startPos != 0 || (allSections && startTopic != 0))
1190 /* Wrap search */
1191 searchHelpText(parent, parentTopic, searchFor, allSections, 0, 0);
1192 return;
1194 DialogF(DF_INF, parent, 1, "String Not Found", "String Not Found", "OK");
1195 return;
1198 /* update navigation history */
1199 if (parentTopic != topic)
1201 navHistForw[parentTopic]= topic;
1202 navHistBack[topic]= parentTopic;
1205 /* If the appropriate window is already up, bring it to the top, if not,
1206 make the parent window become this topic */
1207 changeTopicOrRaise(parentTopic, topic);
1208 BufSelect(TextGetBuffer(HelpTextPanes[topic]), beginMatch, endMatch);
1209 TextSetCursorPos(HelpTextPanes[topic], endMatch);
1211 /* Save the search information for search-again */
1212 strcpy(LastSearchString, searchFor);
1213 LastSearchTopic = topic;
1214 LastSearchPos = endMatch;
1215 LastSearchWasAllTopics = allSections;
1219 ** Change a help window to display a new topic. (Help window data is stored
1220 ** and indexed by topic so if a given topic is already displayed or has been
1221 ** positioned by the user, it can be found and popped back up in the same
1222 ** place.) To change the topic displayed, the stored data has to be relocated.
1224 static void changeWindowTopic(int existingTopic, enum HelpTopic newTopic)
1226 char *helpText, *styleData;
1228 /* Relocate the window/widget/buffer information */
1229 if (newTopic != existingTopic)
1231 HelpWindows[newTopic] = HelpWindows[existingTopic];
1232 HelpWindows[existingTopic] = NULL;
1233 HelpStyleBuffers[newTopic] = HelpStyleBuffers[existingTopic];
1234 HelpStyleBuffers[existingTopic] = NULL;
1235 HelpTextPanes[newTopic] = HelpTextPanes[existingTopic];
1236 HelpTextPanes[existingTopic] = NULL;
1237 setHelpWinTitle(HelpWindows[newTopic], newTopic);
1240 /* Set the existing text widget to display the new text. Because it's
1241 highlighted, we have to turn off highlighting before changing the
1242 displayed text to prevent the text widget from trying to apply the
1243 old, mismatched, highlighting to the new text */
1244 helpText = stitch(HelpTextPanes[newTopic], HelpText[newTopic], &styleData);
1245 TextDAttachHighlightData(((TextWidget)HelpTextPanes[newTopic])->text.textD,
1246 NULL, NULL, 0, '\0', NULL, NULL);
1247 BufSetAll(TextGetBuffer(HelpTextPanes[newTopic]), helpText);
1248 XtFree(helpText);
1249 BufSetAll(HelpStyleBuffers[newTopic], styleData);
1250 XtFree(styleData);
1251 TextDAttachHighlightData(((TextWidget)HelpTextPanes[newTopic])->text.textD,
1252 HelpStyleBuffers[newTopic], HelpStyleInfo, N_STYLES, '\0', NULL,
1253 NULL);
1256 static int findTopicFromShellWidget(Widget shellWidget)
1258 int i;
1260 for (i=0; i<NUM_TOPICS; i++)
1261 if (shellWidget == HelpWindows[i])
1262 return i;
1263 return -1;
1266 static void initNavigationHistory(void) {
1267 static int doInitNavigationHistory = True;
1268 int i;
1270 if (doInitNavigationHistory)
1272 for (i=0; i<NUM_TOPICS; i++)
1273 navHistBack[i] = navHistForw[i] = -1;
1275 doInitNavigationHistory = False;
1279 #if XmVersion == 2000
1280 /* amai: This function may be called before the Motif part
1281 is being initialized. The following, public interface
1282 is known to initialize at least xmUseVersion.
1283 That interface is declared in <Xm/Xm.h> in Motif 1.2 only.
1284 As for Motif 2.1 we don't need this call anymore.
1285 This also holds for the Motif 2.1 version of LessTif
1286 releases > 0.93.0. */
1287 extern void XmRegisterConverters(void);
1288 #endif
1291 /* Print version info to stdout */
1292 void PrintVersion(void)
1294 const char *text;
1296 #if XmVersion < 2001
1297 XmRegisterConverters(); /* see comment above */
1298 #endif
1299 text = getBuildInfo();
1300 puts (text);