Fix for SF bug #1122813: Tab-related crash on posting unload calltips file
[nedit.git] / source / help.c
blobf65c510213cc9987457dd0b1caf668fcbfe246fc
1 static const char CVSID[] = "$Id: help.c,v 1.104 2005/02/12 01:53:19 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. 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/motif.h"
49 #include "../util/misc.h"
50 #include "../util/DialogF.h"
51 #include "../util/system.h"
53 #include <locale.h>
54 #include <stdlib.h>
55 #include <stdio.h>
56 #include <string.h>
57 #ifdef VMS
58 #include "../util/VMSparam.h"
59 #else
60 #ifndef __MVS__
61 #include <sys/param.h>
62 #endif
63 #endif /*VMS*/
65 #include <Xm/Xm.h>
66 #include <Xm/XmP.h> /* These are for applying style info to help text */
67 #include <Xm/Form.h>
68 #include <Xm/PrimitiveP.h>
69 #include <Xm/ScrolledW.h>
70 #include <Xm/ScrollBar.h>
71 #include <Xm/PushB.h>
72 #ifdef EDITRES
73 #include <X11/Xmu/Editres.h>
74 /* extern void _XEditResCheckMessages(); */
75 #endif /* EDITRES */
77 #ifdef HAVE_DEBUG_H
78 #include "../debug.h"
79 #endif
81 /*============================================================================*/
82 /* SYMBOL DEFINITIONS */
83 /*============================================================================*/
85 #define EOS '\0' /* end-of-string character */
87 #define CLICK_THRESHOLD 5 /* number of pixels mouse may move from its */
88 /* pressed location for mouse-up to be */
89 /* considered a valid click (as opposed to */
90 /* a drag or mouse-pick error) */
92 /*============================================================================*/
93 /* VARIABLE DECLARATIONS */
94 /*============================================================================*/
96 static Widget HelpWindows[NUM_TOPICS] = {NULL};
97 static Widget HelpTextPanes[NUM_TOPICS] = {NULL};
98 static textBuffer *HelpStyleBuffers[NUM_TOPICS] = {NULL};
99 static int navHistForw[NUM_TOPICS];
100 static int navHistBack[NUM_TOPICS];
102 /* Information on the last search for search-again */
103 static char LastSearchString[DF_MAX_PROMPT_LENGTH] = "";
104 static int LastSearchTopic = -1;
105 static int LastSearchPos = 0;
106 static int LastSearchWasAllTopics = False;
108 /* Fonts for each help text style generated by the help generator (setext).
109 The NEdit text widget uses the first help style, 'A', to calculate window
110 width, so making 'A' a fixed font will yield window widths calibrated to
111 match width-dependent fixed font layouts in the help text. */
112 static enum helpFonts StyleFonts[] =
114 /* Fixed fonts, styles: 'A', 'B', 'C', 'D' */
115 FIXED_HELP_FONT, BOLD_FIXED_HELP_FONT, BOLD_FIXED_HELP_FONT,
116 BOLD_ITALIC_FIXED_HELP_FONT,
118 /* Underlined fixed fonts, styles: 'E', 'F', 'G', 'H' */
119 FIXED_HELP_FONT, BOLD_FIXED_HELP_FONT, BOLD_FIXED_HELP_FONT,
120 BOLD_ITALIC_FIXED_HELP_FONT,
122 /* Normal (proportional) fonts, styles: 'I', 'J', 'K', 'L' */
123 HELP_FONT, BOLD_HELP_FONT, ITALIC_HELP_FONT, BOLD_ITALIC_HELP_FONT,
125 /* Underlined fonts, styles: 'M', 'N', 'O', 'P' */
126 HELP_FONT, BOLD_HELP_FONT, ITALIC_HELP_FONT, BOLD_ITALIC_HELP_FONT,
128 /* Link font, style: 'Q' */
129 HELP_FONT,
131 /* Heading fonts, styles: 'R', 'S', 'T' */
132 H1_HELP_FONT, H2_HELP_FONT, H3_HELP_FONT
135 static int StyleUnderlines[] =
137 /* Fixed fonts, styles: 'A', 'B', 'C', 'D' */
138 False, False, False, False,
140 /* Underlined fixed fonts, styles: 'E', 'F', 'G', 'H' */
141 True, True, True, True,
143 /* Normal (proportional) fonts, styles: 'I', 'J', 'K', 'L' */
144 False, False, False, False,
146 /* Underlined fonts, styles: 'M', 'N', 'O', 'P' */
147 True, True, True, True,
149 /* Link font, style: 'Q' */
150 True,
152 /* Heading fonts, styles: 'R', 'S', 'T' */
153 False, False, False
156 #define N_STYLES (XtNumber(StyleFonts))
158 static styleTableEntry HelpStyleInfo[ N_STYLES ];
160 /* Translation table for style codes (A, B, C, ...) to their ASCII codes.
161 For systems using ASCII, this is just a one-to-one mapping, but the
162 table makes it possible to use the style codes also on an EBCDIC system.
164 static unsigned char AlphabetToAsciiTable[256];
166 /* Macro that calculates the zero-based index for a given style, taking
167 into account that the character set may not use ASCII coding, but EBCDIC.
168 The "style" argument must be one of the characters A - Z.
169 In ASCII, this comes down to "style - STYLE_PLAIN". */
170 #define STYLE_INDEX(style) \
171 (AlphabetToAsciiTable[(unsigned char)style] - \
172 AlphabetToAsciiTable[(unsigned char)STYLE_PLAIN])
174 /*============================================================================*/
175 /* PROGRAM PROTOTYPES */
176 /*============================================================================*/
178 static Widget createHelpPanel(enum HelpTopic topic);
179 static void closeCB(Widget w, XtPointer clientData, XtPointer callData);
180 static void prevTopicCB(Widget w, XtPointer clientData, XtPointer callData);
181 static void nextTopicCB(Widget w, XtPointer clientData, XtPointer callData);
182 static void bwHistoryCB(Widget w, XtPointer clientData, XtPointer callData);
183 static void fwHistoryCB(Widget w, XtPointer clientData, XtPointer callData);
184 static void searchHelpCB(Widget w, XtPointer clientData, XtPointer callData);
185 static void searchHelpAgainCB(Widget w, XtPointer clientData,
186 XtPointer callData);
187 static void printCB(Widget w, XtPointer clientData, XtPointer callData);
188 static char *stitch(Widget parent, char **string_list,char **styleMap);
189 static void searchHelpText(Widget parent, int parentTopic,
190 const char *searchFor, int allSections, int startPos, int startTopic);
191 static void changeWindowTopic(int existingTopic, enum HelpTopic newTopic);
192 static int findTopicFromShellWidget(Widget shellWidget);
193 static void loadFontsAndColors(Widget parent, int style);
194 static void initNavigationHistory(void);
196 #ifdef HAVE__XMVERSIONSTRING
197 extern char _XmVersionString[];
198 #else
199 static char _XmVersionString[] = "unknown";
200 #endif
202 /*============================================================================*/
203 /*================================= PROGRAMS =================================*/
204 /*============================================================================*/
207 ** Create a string containing information on the build environment. Returned
208 ** string must NOT be freed by caller.
211 static char *bldInfoString = NULL;
213 static void freeBuildInfo(void)
215 /* This keeps memory leak detectors happy */
216 XtFree(bldInfoString);
219 static const char *const warning =
220 "\nThis NEdit was built with a known-bad version of Motif. Please "
221 "do not report any bugs you encounter unless you can reproduce "
222 "them with a known-good binary from the www.nedit.org website. "
223 "If this binary was supplied with your Linux distribution please "
224 "file a bug report with them asking them to build NEdit with a "
225 "known-good version of Motif.\n";
228 static const char *getBuildInfo(void)
230 static const char *bldFormat =
231 "%s\n"
232 " Built on: %s, %s, %s\n"
233 " Built at: %s, %s\n"
234 " With Motif: %s%d.%d.%d [%s]\n"
235 "Running Motif: %d.%d [%s]\n"
236 " Server: %s %d\n"
237 " Visual: %s\n"
238 " Locale: %s\n"
241 static const char *visualClass[] = {"StaticGray", "GrayScale",
242 "StaticColor", "PseudoColor",
243 "TrueColor", "DirectColor"};
245 static const char *const stabilities[] = {"", "(Known Bad) ", "(Untested) "};
247 if (bldInfoString == NULL)
249 const char *locale;
250 char visualStr[500] = "<unknown>";
251 const enum MotifStability stab = GetMotifStability();
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) + strlen(warning) + 1024);
268 locale = setlocale(LC_MESSAGES, "");
270 sprintf(bldInfoString, bldFormat,
271 NEditVersion,
272 COMPILE_OS, COMPILE_MACHINE, COMPILE_COMPILER,
273 linkdate, linktime,
274 stabilities[stab], XmVERSION, XmREVISION, XmUPDATE_LEVEL,
275 XmVERSION_STRING,
276 xmUseVersion/1000, xmUseVersion%1000,
277 _XmVersionString,
278 (NULL == TheDisplay ? "<unknown>" : ServerVendor(TheDisplay)),
279 (NULL == TheDisplay ? 0 : VendorRelease(TheDisplay)),
280 visualStr,
281 locale ? locale : "None");
283 if (stab == MotifKnownBad)
284 strcat(bldInfoString, warning);
286 atexit(freeBuildInfo);
289 return bldInfoString;
293 ** Initialization for help system data, needs to be done only once.
295 static void initHelpStyles (Widget parent)
297 static int styleTableInitialized = False;
299 if (! styleTableInitialized)
301 Pixel fg;
302 int styleIndex;
303 char ** line;
305 XtVaGetValues(parent, XtNforeground, &fg, NULL);
307 for (styleIndex = 0; styleIndex < STL_HD + MAX_HEADING; styleIndex++)
309 HelpStyleInfo[ styleIndex ].color = fg;
310 HelpStyleInfo[ styleIndex ].underline = StyleUnderlines[styleIndex];
311 HelpStyleInfo[ styleIndex ].font = NULL;
314 styleTableInitialized = True;
316 /*-------------------------------------------------------
317 * Only attempt to add build information to version text
318 * when string formatting symbols are present in the text.
319 * This special case is needed to incorporate this
320 * dynamically created information into the static help.
321 *-------------------------------------------------------*/
322 for (line = HelpText[ HELP_VERSION ]; *line != NULL; line++)
324 /*--------------------------------------------------
325 * If and when this printf format is found in the
326 * version help text, replace that line with the
327 * build information. Then stitching the help text
328 * will have the final count of characters to use.
329 *--------------------------------------------------*/
330 if (strstr (*line, "%s") != NULL)
332 const char * bldInfo = getBuildInfo();
333 char * text = XtMalloc (strlen (*line) + strlen (bldInfo));
334 sprintf (text, *line, bldInfo);
335 *line = text;
336 break;
340 /*---------------------------------------------------------
341 * Also initialize the alphabet-to-ASCII-code table (to
342 * make the style mapping also work on EBCDIC).
343 * DON'T use 'A' to initialize the table! 'A' != 65 in EBCDIC.
344 *--------------------------------------------------------*/
345 AlphabetToAsciiTable[(unsigned char)'A'] = ASCII_A + 0;
346 AlphabetToAsciiTable[(unsigned char)'B'] = ASCII_A + 1;
347 AlphabetToAsciiTable[(unsigned char)'C'] = ASCII_A + 2;
348 AlphabetToAsciiTable[(unsigned char)'D'] = ASCII_A + 3;
349 AlphabetToAsciiTable[(unsigned char)'E'] = ASCII_A + 4;
350 AlphabetToAsciiTable[(unsigned char)'F'] = ASCII_A + 5;
351 AlphabetToAsciiTable[(unsigned char)'G'] = ASCII_A + 6;
352 AlphabetToAsciiTable[(unsigned char)'H'] = ASCII_A + 7;
353 AlphabetToAsciiTable[(unsigned char)'I'] = ASCII_A + 8;
354 AlphabetToAsciiTable[(unsigned char)'J'] = ASCII_A + 9;
355 AlphabetToAsciiTable[(unsigned char)'K'] = ASCII_A + 10;
356 AlphabetToAsciiTable[(unsigned char)'L'] = ASCII_A + 11;
357 AlphabetToAsciiTable[(unsigned char)'M'] = ASCII_A + 12;
358 AlphabetToAsciiTable[(unsigned char)'N'] = ASCII_A + 13;
359 AlphabetToAsciiTable[(unsigned char)'O'] = ASCII_A + 14;
360 AlphabetToAsciiTable[(unsigned char)'P'] = ASCII_A + 15;
361 AlphabetToAsciiTable[(unsigned char)'Q'] = ASCII_A + 16;
362 AlphabetToAsciiTable[(unsigned char)'R'] = ASCII_A + 17;
363 AlphabetToAsciiTable[(unsigned char)'S'] = ASCII_A + 18;
364 AlphabetToAsciiTable[(unsigned char)'T'] = ASCII_A + 19;
365 AlphabetToAsciiTable[(unsigned char)'U'] = ASCII_A + 20;
366 AlphabetToAsciiTable[(unsigned char)'V'] = ASCII_A + 21;
367 AlphabetToAsciiTable[(unsigned char)'W'] = ASCII_A + 22;
368 AlphabetToAsciiTable[(unsigned char)'X'] = ASCII_A + 23;
369 AlphabetToAsciiTable[(unsigned char)'Y'] = ASCII_A + 24;
370 AlphabetToAsciiTable[(unsigned char)'Z'] = ASCII_A + 25;
375 ** Help fonts are not loaded until they're actually needed. This function
376 ** checks if the style's font is loaded, and loads it if it's not.
378 static void loadFontsAndColors(Widget parent, int style)
380 XFontStruct *font;
381 int r,g,b;
382 if (HelpStyleInfo[STYLE_INDEX(style)].font == NULL)
384 font = XLoadQueryFont(XtDisplay(parent),
385 GetPrefHelpFontName(StyleFonts[STYLE_INDEX(style)]));
386 if (font == NULL)
388 fprintf(stderr, "NEdit: help font, %s, not available\n",
389 GetPrefHelpFontName(StyleFonts[STYLE_INDEX(style)]));
390 font = XLoadQueryFont(XtDisplay(parent), "fixed");
391 if (font == NULL)
393 fprintf(stderr, "NEdit: fallback help font, \"fixed\", not "
394 "available, cannot continue\n");
395 exit(EXIT_FAILURE);
398 HelpStyleInfo[STYLE_INDEX(style)].font = font;
400 if (style == STL_NM_LINK)
401 HelpStyleInfo[STYLE_INDEX(style)].color =
402 AllocColor(parent, GetPrefHelpLinkColor(), &r, &g, &b);
406 static void adaptNavigationButtons(int topic) {
407 Widget btn;
409 if(HelpWindows[topic] == NULL)
410 return; /* Shouldn't happen */
412 btn=XtNameToWidget(HelpWindows[topic], "helpForm.prevTopic");
413 if(btn)
415 if(topic > 0)
416 XtSetSensitive(btn, True);
417 else
418 XtSetSensitive(btn, False);
421 btn=XtNameToWidget(HelpWindows[topic], "helpForm.nextTopic");
422 if(btn)
424 if(topic < (NUM_TOPICS - 1))
425 XtSetSensitive(btn, True);
426 else
427 XtSetSensitive(btn, False);
430 btn=XtNameToWidget(HelpWindows[topic], "helpForm.histBack");
431 if(btn)
433 if(navHistBack[topic] != -1)
434 XtSetSensitive(btn, True);
435 else
436 XtSetSensitive(btn, False);
439 btn=XtNameToWidget(HelpWindows[topic], "helpForm.histForw");
440 if(btn)
442 if(navHistForw[topic] != -1)
443 XtSetSensitive(btn, True);
444 else
445 XtSetSensitive(btn, False);
451 ** Put together stored help strings to create the text and optionally the style
452 ** information for a given help topic.
454 static char * stitch (
456 Widget parent, /* used for dynamic font/color allocation */
457 char ** string_list, /* given help strings to stitch together */
458 char ** styleMap /* NULL, or a place to store help styles */
461 char * cp;
462 char * section, * sp; /* resulting help text section */
463 char * styleData, * sdp; /* resulting style data for text */
464 char style = STYLE_PLAIN; /* start off each section with this style */
465 int total_size = 0; /* help text section size */
466 char ** crnt_line;
468 /*----------------------------------------------------
469 * How many characters are there going to be displayed?
470 *----------------------------------------------------*/
471 for (crnt_line = string_list; *crnt_line != NULL; crnt_line++)
473 for (cp = *crnt_line; *cp != EOS; cp++)
475 /*---------------------------------------------
476 * The help text has embedded style information
477 * consisting of the style marker and a single
478 * character style, for a total of 2 characters.
479 * This style information is not to be included
480 * in the character counting below.
481 *---------------------------------------------*/
482 if (*cp != STYLE_MARKER) {
483 total_size++;
485 else {
486 cp++; /* skipping style marker, loop will handle style */
491 /*--------------------------------------------------------
492 * Get the needed space, one area for the help text being
493 * stitched together, another for the styles to be applied.
494 *--------------------------------------------------------*/
495 sp = section = XtMalloc (total_size +1);
496 sdp = styleData = (styleMap) ? XtMalloc (total_size +1) : NULL;
497 *sp = EOS;
499 /*--------------------------------------------
500 * Fill in the newly acquired contiguous space
501 * with help text and style information.
502 *--------------------------------------------*/
503 for (crnt_line = string_list; *crnt_line != NULL; crnt_line++)
505 for (cp = *crnt_line; *cp != EOS; cp++)
507 if (*cp == STYLE_MARKER)
509 style = *(++cp);
510 loadFontsAndColors(parent, style);
511 } else
513 *(sp++) = *cp;
515 /* Beware of possible EBCDIC coding! Use the mapping table. */
516 if (styleMap)
517 *(sdp++) = AlphabetToAsciiTable[(unsigned char)style];
522 *sp = EOS;
524 /*-----------------------------------------
525 * Only deal with style map, when available.
526 *-----------------------------------------*/
527 if (styleMap) {
528 *styleMap = styleData;
529 *sdp = EOS;
532 return section;
536 ** Display help for subject "topic". "parent" is a widget over which the help
537 ** dialog may be posted. Help dialogs are preserved when popped down by the
538 ** user, and may appear posted over a previous parent, regardless of the parent
539 ** argument.
541 void Help(enum HelpTopic topic)
543 if (HelpWindows[topic] != NULL)
544 RaiseShellWindow(HelpWindows[topic], True);
545 else
546 HelpWindows[topic] = createHelpPanel(topic);
547 adaptNavigationButtons(topic);
551 /* Setup Window/Icon title for the help window. */
552 static void setHelpWinTitle(Widget win, enum HelpTopic topic)
554 char * buf, *topStr=HelpTitles[topic];
556 buf=malloc(strlen(topStr) + 24);
557 topic++;
559 sprintf(buf, "NEdit Help (%d)", (int)topic);
560 XtVaSetValues(win, XmNiconName, buf, NULL);
562 sprintf(buf, "NEdit Help: %s (%d)", topStr, (int)topic);
563 XtVaSetValues(win, XmNtitle, buf, NULL);
565 free(buf);
569 ** Create a new help window displaying a given subject, "topic"
571 ** Important hint: If this widget is restructured or the name of the text
572 ** subwidget is changed don't forget to adapt the default translations of the
573 ** help text. They are located in nedit.c, look for
574 ** static char *fallbackResources
575 ** (currently: nedit.helpForm.sw.helpText*translations...)
577 static Widget createHelpPanel(enum HelpTopic topic)
579 Arg al[50];
580 int ac;
581 Widget appShell, btn, closeBtn, form, btnFW;
582 Widget sw, hScrollBar, vScrollBar;
583 XmString st1;
584 char * helpText = NULL;
585 char * styleData = NULL;
587 ac = 0;
588 XtSetArg(al[ac], XmNdeleteResponse, XmDO_NOTHING); ac++;
589 appShell = CreateWidget(TheAppShell, "help",
590 topLevelShellWidgetClass, al, ac);
591 AddSmallIcon(appShell);
592 /* With openmotif 2.1.30, a crash may occur when the text widget of the
593 help window is (slowly) resized to a zero width. By imposing a
594 minimum _window_ width, we can work around this problem. The minimum
595 width should be larger than the width of the scrollbar. 50 is probably
596 a safe value; this leaves room for a few characters */
597 XtVaSetValues(appShell, XtNminWidth, 50, NULL);
598 form = XtVaCreateManagedWidget("helpForm", xmFormWidgetClass, appShell,
599 NULL);
600 XtVaSetValues(form, XmNshadowThickness, 0, NULL);
602 /* Create the bottom row of buttons */
603 btn = XtVaCreateManagedWidget("find", xmPushButtonWidgetClass, form,
604 XmNlabelString, st1=XmStringCreateSimple("Find..."),
605 XmNmnemonic, 'F',
606 XmNbottomAttachment, XmATTACH_FORM,
607 XmNleftAttachment, XmATTACH_POSITION,
608 XmNleftPosition, 3,
609 XmNrightAttachment, XmATTACH_POSITION,
610 XmNrightPosition, 25,
611 NULL);
612 XtAddCallback(btn, XmNactivateCallback, searchHelpCB, appShell);
613 XmStringFree(st1);
615 btn = XtVaCreateManagedWidget("findAgain", xmPushButtonWidgetClass, form,
616 XmNlabelString, st1=XmStringCreateSimple("Find Again"),
617 XmNmnemonic, 'A',
618 XmNbottomAttachment, XmATTACH_FORM,
619 XmNleftAttachment, XmATTACH_POSITION,
620 XmNleftPosition, 27,
621 XmNrightAttachment, XmATTACH_POSITION,
622 XmNrightPosition, 49,
623 NULL);
624 XtAddCallback(btn, XmNactivateCallback, searchHelpAgainCB, appShell);
625 XmStringFree(st1);
627 btn = XtVaCreateManagedWidget("print", xmPushButtonWidgetClass, form,
628 XmNlabelString, st1=XmStringCreateSimple("Print..."),
629 XmNmnemonic, 'P',
630 XmNbottomAttachment, XmATTACH_FORM,
631 XmNleftAttachment, XmATTACH_POSITION,
632 XmNleftPosition, 51,
633 XmNrightAttachment, XmATTACH_POSITION,
634 XmNrightPosition, 73,
635 NULL);
636 XtAddCallback(btn, XmNactivateCallback, printCB, appShell);
637 XmStringFree(st1);
639 closeBtn = XtVaCreateManagedWidget("close",
640 xmPushButtonWidgetClass, form,
641 XmNlabelString, st1=XmStringCreateSimple("Close"),
642 XmNbottomAttachment, XmATTACH_FORM,
643 XmNleftAttachment, XmATTACH_POSITION,
644 XmNleftPosition, 75,
645 XmNrightAttachment, XmATTACH_POSITION,
646 XmNrightPosition, 97,
647 NULL);
648 XtAddCallback(closeBtn, XmNactivateCallback, closeCB, appShell);
649 XmStringFree(st1);
651 /* Create the next row of buttons (for navigation) */
652 btn = XtVaCreateManagedWidget("prevTopic", xmPushButtonWidgetClass, form,
653 XmNlabelString, st1=XmStringCreateSimple("<< Browse"),
654 XmNmnemonic, 'o',
655 XmNbottomAttachment, XmATTACH_WIDGET,
656 XmNbottomWidget, closeBtn,
657 XmNleftAttachment, XmATTACH_POSITION,
658 XmNleftPosition, 51,
659 XmNrightAttachment, XmATTACH_POSITION,
660 XmNrightPosition, 73,
661 NULL);
662 XtAddCallback(btn, XmNactivateCallback, prevTopicCB, appShell);
663 XmStringFree(st1);
665 btn = XtVaCreateManagedWidget("nextTopic", xmPushButtonWidgetClass, form,
666 XmNlabelString, st1=XmStringCreateSimple("Browse >>"),
667 XmNmnemonic, 'e',
668 XmNbottomAttachment, XmATTACH_WIDGET,
669 XmNbottomWidget, closeBtn,
670 XmNleftAttachment, XmATTACH_POSITION,
671 XmNleftPosition, 75,
672 XmNrightAttachment, XmATTACH_POSITION,
673 XmNrightPosition, 97,
674 NULL);
675 XtAddCallback(btn, XmNactivateCallback, nextTopicCB, appShell);
676 XmStringFree(st1);
678 btn = XtVaCreateManagedWidget("histBack", xmPushButtonWidgetClass, form,
679 XmNlabelString, st1=XmStringCreateSimple("Back"),
680 XmNmnemonic, 'B',
681 XmNbottomAttachment, XmATTACH_WIDGET,
682 XmNbottomWidget, closeBtn,
683 XmNleftAttachment, XmATTACH_POSITION,
684 XmNleftPosition, 3,
685 XmNrightAttachment, XmATTACH_POSITION,
686 XmNrightPosition, 25,
687 NULL);
688 XtAddCallback(btn, XmNactivateCallback, bwHistoryCB, appShell);
689 XmStringFree(st1);
691 btnFW = XtVaCreateManagedWidget("histForw", xmPushButtonWidgetClass, form,
692 XmNlabelString, st1=XmStringCreateSimple("Forward"),
693 XmNmnemonic, 'w',
694 XmNbottomAttachment, XmATTACH_WIDGET,
695 XmNbottomWidget, closeBtn,
696 XmNleftAttachment, XmATTACH_POSITION,
697 XmNleftPosition, 27,
698 XmNrightAttachment, XmATTACH_POSITION,
699 XmNrightPosition, 49,
700 NULL);
701 XtAddCallback(btnFW, XmNactivateCallback, fwHistoryCB, appShell);
702 XmStringFree(st1);
704 /* Create a text widget inside of a scrolled window widget */
705 sw = XtVaCreateManagedWidget("sw", xmScrolledWindowWidgetClass, form,
706 XmNshadowThickness, 2,
707 XmNtopAttachment, XmATTACH_FORM,
708 XmNleftAttachment, XmATTACH_FORM,
709 XmNrightAttachment, XmATTACH_FORM,
710 XmNbottomAttachment, XmATTACH_WIDGET,
711 XmNbottomWidget, btnFW,
712 NULL);
713 hScrollBar = XtVaCreateManagedWidget("hScrollBar",
714 xmScrollBarWidgetClass, sw,
715 XmNorientation, XmHORIZONTAL,
716 XmNrepeatDelay, 10,
717 NULL);
718 vScrollBar = XtVaCreateManagedWidget("vScrollBar",
719 xmScrollBarWidgetClass, sw,
720 XmNorientation, XmVERTICAL,
721 XmNrepeatDelay, 10,
722 NULL);
723 /* Make sure the fixed size help font is loaded, such that we can base
724 our text widget size calculation on it. */
725 loadFontsAndColors(sw, 'A');
726 HelpTextPanes[topic] = XtVaCreateManagedWidget("helpText",
727 textWidgetClass, sw,
728 textNfont, HelpStyleInfo[0].font, /* MUST correspond to 'A' above */
729 textNrows, 30,
730 textNcolumns, 65,
731 textNbacklightCharTypes, NULL,
732 textNhScrollBar, hScrollBar,
733 textNvScrollBar, vScrollBar,
734 textNreadOnly, True,
735 textNcontinuousWrap, True,
736 textNautoShowInsertPos, True,
737 NULL);
738 XtVaSetValues(sw, XmNworkWindow, HelpTextPanes[topic],
739 XmNhorizontalScrollBar, hScrollBar,
740 XmNverticalScrollBar, vScrollBar,
741 NULL);
743 /* Initialize help style information, if it hasn't already been init'd */
744 initHelpStyles (HelpTextPanes[topic]);
746 /* Put together the text to display and separate it into parallel text
747 and style data for display by the widget */
748 helpText = stitch (HelpTextPanes[topic], HelpText[topic], &styleData);
750 /* Stuff the text into the widget's text buffer */
751 BufSetAll (TextGetBuffer (HelpTextPanes[topic]) , helpText);
752 XtFree (helpText);
754 /* Create a style buffer for the text widget and fill it with the style
755 data which was generated along with the text content */
756 HelpStyleBuffers[topic] = BufCreate();
757 BufSetAll(HelpStyleBuffers[topic], styleData);
758 XtFree (styleData);
759 TextDAttachHighlightData(((TextWidget)HelpTextPanes[topic])->text.textD,
760 HelpStyleBuffers[topic], HelpStyleInfo, N_STYLES, '\0', NULL, NULL);
762 /* This shouldn't be necessary (what's wrong in text.c?) */
763 HandleXSelections(HelpTextPanes[topic]);
765 /* Process dialog mnemonic keys */
766 AddDialogMnemonicHandler(form, FALSE);
768 /* Set the default button */
769 XtVaSetValues(form, XmNdefaultButton, closeBtn, NULL);
770 XtVaSetValues(form, XmNcancelButton, closeBtn, NULL);
772 /* realize all of the widgets in the new window */
773 RealizeWithoutForcingPosition(appShell);
775 /* Give the text pane the initial focus */
776 XmProcessTraversal(HelpTextPanes[topic], XmTRAVERSE_CURRENT);
778 /* Make close command in window menu gracefully prompt for close */
779 AddMotifCloseCallback(appShell, (XtCallbackProc)closeCB, appShell);
781 /* Initialize navigation information, if it hasn't already been init'd */
782 initNavigationHistory();
784 /* Set the window title */
785 setHelpWinTitle(appShell, topic);
788 #ifdef EDITRES
789 XtAddEventHandler (appShell, (EventMask)0, True,
790 (XtEventHandler)_XEditResCheckMessages, NULL);
791 #endif /* EDITRES */
793 return appShell;
796 static void changeTopicOrRaise(int existingTopic, int newTopic) {
797 if(HelpWindows[newTopic] == NULL)
799 changeWindowTopic(existingTopic, newTopic);
800 adaptNavigationButtons(newTopic);
801 } else
803 RaiseShellWindow(HelpWindows[newTopic], True);
804 adaptNavigationButtons(existingTopic);
805 adaptNavigationButtons(newTopic);
811 ** Callbacks for window controls
813 static void closeCB(Widget w, XtPointer clientData, XtPointer callData)
815 int topic;
817 if ((topic = findTopicFromShellWidget((Widget)clientData)) == -1)
818 return;
820 /* I don't understand the mechanism by which this can be called with
821 HelpWindows[topic] as NULL, but it has happened */
822 XtDestroyWidget(HelpWindows[topic]);
823 HelpWindows[topic] = NULL;
824 if (HelpStyleBuffers[topic] != NULL)
826 BufFree(HelpStyleBuffers[topic]);
827 HelpStyleBuffers[topic] = NULL;
831 static void prevTopicCB(Widget w, XtPointer clientData, XtPointer callData)
832 { int topic;
834 if ((topic = findTopicFromShellWidget((Widget)clientData)) == -1)
835 return; /* shouldn't happen */
837 topic--;
838 if(topic >= 0)
839 changeTopicOrRaise(topic+1, topic);
842 static void nextTopicCB(Widget w, XtPointer clientData, XtPointer callData)
843 { int topic;
845 if ((topic = findTopicFromShellWidget((Widget)clientData)) == -1)
846 return; /* shouldn't happen */
848 topic++;
849 if(topic < NUM_TOPICS)
850 changeTopicOrRaise(topic-1, topic);
853 static void bwHistoryCB(Widget w, XtPointer clientData, XtPointer callData)
854 { int topic, goTo;
856 if ((topic = findTopicFromShellWidget((Widget)clientData)) == -1)
857 return; /* shouldn't happen */
859 goTo=navHistBack[topic];
860 if(goTo >= 0 && goTo < NUM_TOPICS)
862 navHistForw[goTo]=topic;
863 changeTopicOrRaise(topic, goTo);
867 static void fwHistoryCB(Widget w, XtPointer clientData, XtPointer callData)
868 { int topic, goTo;
870 if ((topic = findTopicFromShellWidget((Widget)clientData)) == -1)
871 return; /* shouldn't happen */
873 goTo=navHistForw[topic];
874 if(goTo >= 0 && goTo < NUM_TOPICS)
876 navHistBack[goTo]=topic;
877 changeTopicOrRaise(topic, goTo);
881 static void searchHelpCB(Widget w, XtPointer clientData, XtPointer callData)
883 char promptText[DF_MAX_PROMPT_LENGTH];
884 int response, topic;
885 static char **searchHistory = NULL;
886 static int nHistoryStrings = 0;
888 if ((topic = findTopicFromShellWidget((Widget)clientData)) == -1)
889 return; /* shouldn't happen */
890 SetDialogFPromptHistory(searchHistory, nHistoryStrings);
891 response = DialogF(DF_PROMPT, HelpWindows[topic], 3, "Find",
892 "Search for: (use up arrow key to recall previous)", promptText,
893 "This Section", "All Sections", "Cancel");
894 if (response == 3)
895 return;
896 AddToHistoryList(promptText, &searchHistory, &nHistoryStrings);
897 searchHelpText(HelpWindows[topic], topic, promptText, response == 2, 0, 0);
900 static void searchHelpAgainCB(Widget w, XtPointer clientData,
901 XtPointer callData)
903 int topic;
905 if ((topic = findTopicFromShellWidget((Widget)clientData)) == -1)
906 return; /* shouldn't happen */
907 searchHelpText(HelpWindows[topic], topic, LastSearchString,
908 LastSearchWasAllTopics, LastSearchPos, LastSearchTopic);
911 static void printCB(Widget w, XtPointer clientData, XtPointer callData)
913 int topic, helpStringLen;
914 char *helpString;
916 if ((topic = findTopicFromShellWidget((Widget)clientData)) == -1)
917 return; /* shouldn't happen */
919 helpString = TextGetWrapped(HelpTextPanes[topic], 0,
920 TextGetBuffer(HelpTextPanes[topic])->length, &helpStringLen);
921 PrintString(helpString, helpStringLen, HelpWindows[topic],
922 HelpTitles[topic]);
923 XtFree(helpString);
928 ** Find the topic and text position within that topic targeted by a hyperlink
929 ** with name "link_name". Returns true if the link was successfully interpreted.
931 static int is_known_link(char *link_name, int *topic, int *textPosition)
933 Href * hypertext;
935 /*------------------------------
936 * Direct topic links found here.
937 *------------------------------*/
938 for (*topic=0; HelpTitles[*topic] != NULL; (*topic)++)
940 if (strcmp (link_name, HelpTitles[*topic]) == 0)
942 *textPosition = 0;
943 return 1;
947 /*------------------------------------
948 * Links internal to topics found here.
949 *------------------------------------*/
950 for (hypertext = &H_R[0]; hypertext != NULL; hypertext = hypertext->next)
952 if (strcmp (link_name, hypertext->source) == 0)
954 *topic = hypertext->topic;
955 *textPosition = hypertext->location;
956 return 1;
960 return 0;
964 ** Find the text of a hyperlink from a clicked character position somewhere
965 ** within the hyperlink text, and display the help that it links to.
967 static void followHyperlink(int topic, int charPosition, int newWindow)
969 textDisp *textD = ((TextWidget)HelpTextPanes[topic])->text.textD;
970 char * link_text;
971 int link_topic;
972 int link_pos;
973 int end = charPosition;
974 int begin = charPosition;
975 char whatStyle = BufGetCharacter(textD->styleBuffer, end);
977 /*--------------------------------------------------
978 * Locate beginning and ending of current text style.
979 *--------------------------------------------------*/
980 while (whatStyle == BufGetCharacter(textD->styleBuffer, ++end));
981 while (whatStyle == BufGetCharacter(textD->styleBuffer, begin-1)) begin--;
983 link_text = BufGetRange (textD->buffer, begin, end);
985 if (is_known_link (link_text, &link_topic, &link_pos) )
987 if (HelpWindows[link_topic] != NULL)
989 RaiseShellWindow(HelpWindows[link_topic], True);
990 } else
992 if (newWindow)
994 HelpWindows[link_topic] = createHelpPanel(link_topic);
995 } else
997 changeWindowTopic(topic, link_topic);
1000 navHistBack[link_topic] = topic;
1001 navHistForw[topic] = link_topic;
1002 TextSetCursorPos(HelpTextPanes[link_topic], link_pos);
1003 adaptNavigationButtons(link_topic);
1004 adaptNavigationButtons(topic);
1006 XtFree (link_text);
1009 static void helpFocusButtonsAP(Widget w, XEvent *event, String *args,
1010 Cardinal *nArgs)
1012 XmProcessTraversal(w, XmTRAVERSE_NEXT_TAB_GROUP);
1016 * handler for help-button-action(<button-name>)
1017 * Calls the activate callback for the named button widget of the help text win.
1019 static void helpButtonActionAP(Widget w, XEvent *event, String *args,
1020 Cardinal *nArgs)
1022 char buf[80];
1023 int topic;
1024 Widget btn;
1026 if(*nArgs != 1)
1028 fprintf(stderr, "help-button-action: requires exactly one argument.\n");
1029 return;
1032 /* Find the topic being displayed by this widget */
1033 for (topic = 0; topic < NUM_TOPICS; topic++)
1034 if (HelpTextPanes[topic] == w)
1035 break;
1037 if(topic == NUM_TOPICS || HelpWindows[topic] == NULL)
1038 return; /* Shouldn't happen */
1040 /* Compose the button widget name */
1041 strcpy(&buf[0], "helpForm.");
1042 if (strlen(args[0]) <= 70)
1044 strcat(&buf[0], args[0]);
1045 } else
1047 fprintf(stderr, "help-button-action: argument too long");
1048 return;
1051 btn=XtNameToWidget(HelpWindows[topic], buf);
1052 if (btn)
1054 XtCallCallbacks(btn, XmNactivateCallback, HelpWindows[topic]);
1055 } else
1057 fprintf(stderr, "help-button-action: invalid argument: %s\n", args[0]);
1062 * Handler for action help-hyperlink()
1063 * Arguments: none - init: record event position
1064 * "current": if clicked on a link, follow link in same window
1065 * "new": if clicked on a link, follow link in new window
1067 * With the 1st argument "current" or "new" this action can have two additional
1068 * arguments. These arguments must be valid names of XmText actions.
1069 * In this case, the action named in argument #2 is called if the action
1070 * help-hyperlink is about to follow the hyperlink. The Action in argument #3
1071 * is called if no hyperlink has been recognized at the current event position.
1073 static void helpHyperlinkAP(Widget w, XEvent *event, String *args,
1074 Cardinal *nArgs)
1076 XButtonEvent *e = (XButtonEvent *)event;
1077 int topic;
1078 textDisp *textD = ((TextWidget)w)->text.textD;
1079 int clickedPos, newWin;
1080 static int pressX=0, pressY=0;
1082 /* called without arguments we just record coordinates */
1083 if (*nArgs == 0)
1085 pressX = e->x;
1086 pressY = e->y;
1087 return;
1090 newWin = !strcmp(args[0], "new");
1092 if(!newWin && strcmp(args[0], "current")) {
1093 fprintf(stderr, "help-hyperlink: Unrecognized argument %s\n", args[0]);
1094 return;
1098 * If for any reason (pointer moved - drag!, no hyperlink found)
1099 * this action can't follow a hyperlink then execute the the action
1100 * named in arg #3 (if provided)
1102 if (abs(pressX - e->x) > CLICK_THRESHOLD
1103 || abs(pressY - e->y) > CLICK_THRESHOLD)
1105 if (*nArgs == 3)
1106 XtCallActionProc(w, args[2], event, NULL, 0);
1107 return;
1110 clickedPos = TextDXYToCharPos(textD, e->x, e->y);
1111 /* Beware of possible EBCDIC coding! Use the mapping table. */
1112 if (BufGetCharacter(textD->styleBuffer, clickedPos) !=
1113 (char)AlphabetToAsciiTable[(unsigned char)STL_NM_LINK])
1115 if (*nArgs == 3)
1116 XtCallActionProc(w, args[2], event, NULL, 0);
1117 return;
1120 /* Find the topic being displayed by this widget */
1121 for (topic = 0; topic < NUM_TOPICS; topic++)
1122 if (HelpTextPanes[topic] == w)
1123 break;
1125 if (topic == NUM_TOPICS)
1127 /* If we get here someone must have bound help-hyperlink to a non-help
1128 * text widget (!) Or some other really strange thing happened.
1130 if (*nArgs == 3)
1131 XtCallActionProc(w, args[2], event, NULL, 0);
1132 return;
1135 /* If the action help-hyperlink had 3 arguments execute the action
1136 * named in arg #2 before really following the link.
1138 if (*nArgs == 3)
1139 XtCallActionProc(w, args[1], event, NULL, 0);
1141 followHyperlink(topic, clickedPos, newWin);
1145 ** Install the action for following hyperlinks in the help window
1147 void InstallHelpLinkActions(XtAppContext context)
1149 static XtActionsRec Actions[] =
1151 {"help-hyperlink", helpHyperlinkAP},
1152 {"help-focus-buttons", helpFocusButtonsAP},
1153 {"help-button-action", helpButtonActionAP}
1156 XtAppAddActions(context, Actions, XtNumber(Actions));
1160 ** Search the help text. If allSections is true, searches all of the help
1161 ** text, otherwise searches only in parentTopic.
1163 static void searchHelpText(Widget parent, int parentTopic,
1164 const char *searchFor, int allSections, int startPos, int startTopic)
1166 int topic, beginMatch, endMatch;
1167 int found = False;
1168 char * helpText = NULL;
1170 /* Search for the string */
1171 for (topic=startTopic; topic<NUM_TOPICS; topic++)
1173 if (!allSections && topic != parentTopic)
1174 continue;
1175 helpText = stitch(parent, HelpText[topic], NULL);
1177 if (SearchString(helpText, searchFor, SEARCH_FORWARD, SEARCH_LITERAL,
1178 False, topic == startTopic ? startPos : 0, &beginMatch,
1179 &endMatch, NULL, NULL, GetPrefDelimiters()))
1181 found = True;
1182 XtFree(helpText);
1183 break;
1185 XtFree(helpText);
1188 if (!found)
1190 if (startPos != 0 || (allSections && startTopic != 0))
1192 /* Wrap search */
1193 searchHelpText(parent, parentTopic, searchFor, allSections, 0, 0);
1194 return;
1196 DialogF(DF_INF, parent, 1, "String Not Found", "String Not Found", "OK");
1197 return;
1200 /* update navigation history */
1201 if (parentTopic != topic)
1203 navHistForw[parentTopic]= topic;
1204 navHistBack[topic]= parentTopic;
1207 /* If the appropriate window is already up, bring it to the top, if not,
1208 make the parent window become this topic */
1209 changeTopicOrRaise(parentTopic, topic);
1210 BufSelect(TextGetBuffer(HelpTextPanes[topic]), beginMatch, endMatch);
1211 TextSetCursorPos(HelpTextPanes[topic], endMatch);
1213 /* Save the search information for search-again */
1214 strcpy(LastSearchString, searchFor);
1215 LastSearchTopic = topic;
1216 LastSearchPos = endMatch;
1217 LastSearchWasAllTopics = allSections;
1221 ** Change a help window to display a new topic. (Help window data is stored
1222 ** and indexed by topic so if a given topic is already displayed or has been
1223 ** positioned by the user, it can be found and popped back up in the same
1224 ** place.) To change the topic displayed, the stored data has to be relocated.
1226 static void changeWindowTopic(int existingTopic, enum HelpTopic newTopic)
1228 char *helpText, *styleData;
1230 /* Relocate the window/widget/buffer information */
1231 if (newTopic != existingTopic)
1233 HelpWindows[newTopic] = HelpWindows[existingTopic];
1234 HelpWindows[existingTopic] = NULL;
1235 HelpStyleBuffers[newTopic] = HelpStyleBuffers[existingTopic];
1236 HelpStyleBuffers[existingTopic] = NULL;
1237 HelpTextPanes[newTopic] = HelpTextPanes[existingTopic];
1238 HelpTextPanes[existingTopic] = NULL;
1239 setHelpWinTitle(HelpWindows[newTopic], newTopic);
1242 /* Set the existing text widget to display the new text. Because it's
1243 highlighted, we have to turn off highlighting before changing the
1244 displayed text to prevent the text widget from trying to apply the
1245 old, mismatched, highlighting to the new text */
1246 helpText = stitch(HelpTextPanes[newTopic], HelpText[newTopic], &styleData);
1247 TextDAttachHighlightData(((TextWidget)HelpTextPanes[newTopic])->text.textD,
1248 NULL, NULL, 0, '\0', NULL, NULL);
1249 BufSetAll(TextGetBuffer(HelpTextPanes[newTopic]), helpText);
1250 XtFree(helpText);
1251 BufSetAll(HelpStyleBuffers[newTopic], styleData);
1252 XtFree(styleData);
1253 TextDAttachHighlightData(((TextWidget)HelpTextPanes[newTopic])->text.textD,
1254 HelpStyleBuffers[newTopic], HelpStyleInfo, N_STYLES, '\0', NULL,
1255 NULL);
1258 static int findTopicFromShellWidget(Widget shellWidget)
1260 int i;
1262 for (i=0; i<NUM_TOPICS; i++)
1263 if (shellWidget == HelpWindows[i])
1264 return i;
1265 return -1;
1268 static void initNavigationHistory(void) {
1269 static int doInitNavigationHistory = True;
1270 int i;
1272 if (doInitNavigationHistory)
1274 for (i=0; i<NUM_TOPICS; i++)
1275 navHistBack[i] = navHistForw[i] = -1;
1277 doInitNavigationHistory = False;
1281 #if XmVersion == 2000
1282 /* amai: This function may be called before the Motif part
1283 is being initialized. The following, public interface
1284 is known to initialize at least xmUseVersion.
1285 That interface is declared in <Xm/Xm.h> in Motif 1.2 only.
1286 As for Motif 2.1 we don't need this call anymore.
1287 This also holds for the Motif 2.1 version of LessTif
1288 releases > 0.93.0. */
1289 extern void XmRegisterConverters(void);
1290 #endif
1293 /* Print version info to stdout */
1294 void PrintVersion(void)
1296 const char *text;
1298 #if XmVersion < 2001
1299 XmRegisterConverters(); /* see comment above */
1300 #endif
1301 text = getBuildInfo();
1302 puts (text);