1 static const char CVSID
[] = "$Id: help.c,v 1.102 2004/08/01 10:06:10 yooden Exp $";
2 /*******************************************************************************
4 * help.c -- Nirvana Editor help display *
6 * Copyright (C) 1999 Mark Edel *
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. *
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 *
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 *
23 * Nirvana Text Editor *
24 * September 10, 1991 *
26 * Written by Mark Edel, mostly rewritten by Steve Haehn for new help system, *
29 *******************************************************************************/
32 #include "../config.h"
44 #include "preferences.h"
45 #include "help_data.h"
47 #include "highlight.h"
48 #include "../util/misc.h"
49 #include "../util/DialogF.h"
50 #include "../util/system.h"
57 #include "../util/VMSparam.h"
60 #include <sys/param.h>
65 #include <Xm/XmP.h> /* These are for applying style info to help text */
67 #include <Xm/PrimitiveP.h>
68 #include <Xm/ScrolledW.h>
69 #include <Xm/ScrollBar.h>
72 #include <X11/Xmu/Editres.h>
73 /* extern void _XEditResCheckMessages(); */
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' */
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' */
151 /* Heading fonts, styles: 'R', 'S', 'T' */
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
,
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
[];
198 static char _XmVersionString
[] = "unknown";
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
=
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
227 #elif defined BUILD_UNTESTED_NEDIT
232 "Running Motif: %d.%d [%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"
245 const char * visualClass
[] = {"StaticGray", "GrayScale",
246 "StaticColor", "PseudoColor",
247 "TrueColor", "DirectColor"};
250 if (bldInfoString
== NULL
)
252 char visualStr
[500] = "<unknown>";
258 Boolean usingDefaultVisual
= FindBestVisual(TheDisplay
, APP_NAME
,
261 sprintf(visualStr
,"%d-bit %s (ID %#lx%s)",
263 visualClass
[visual
->class],
265 usingDefaultVisual
? ", Default" : "");
268 bldInfoString
= XtMalloc (strlen (bldFormat
) + 1024);
269 locale
= setlocale(LC_MESSAGES
, "");
271 sprintf(bldInfoString
, bldFormat
,
273 COMPILE_OS
, COMPILE_MACHINE
, COMPILE_COMPILER
,
275 XmVERSION
, XmREVISION
, XmUPDATE_LEVEL
,
277 xmUseVersion
/1000, xmUseVersion
%1000,
279 (NULL
== TheDisplay
? "<unknown>" : ServerVendor(TheDisplay
)),
280 (NULL
== TheDisplay
? 0 : VendorRelease(TheDisplay
)),
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
)
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
);
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
)
380 if (HelpStyleInfo
[STYLE_INDEX(style
)].font
== NULL
)
382 font
= XLoadQueryFont(XtDisplay(parent
),
383 GetPrefHelpFontName(StyleFonts
[STYLE_INDEX(style
)]));
386 fprintf(stderr
, "NEdit: help font, %s, not available\n",
387 GetPrefHelpFontName(StyleFonts
[STYLE_INDEX(style
)]));
388 font
= XLoadQueryFont(XtDisplay(parent
), "fixed");
391 fprintf(stderr
, "NEdit: fallback help font, \"fixed\", not "
392 "available, cannot continue\n");
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
) {
407 if(HelpWindows
[topic
] == NULL
)
408 return; /* Shouldn't happen */
410 btn
=XtNameToWidget(HelpWindows
[topic
], "helpForm.prevTopic");
414 XtSetSensitive(btn
, True
);
416 XtSetSensitive(btn
, False
);
419 btn
=XtNameToWidget(HelpWindows
[topic
], "helpForm.nextTopic");
422 if(topic
< (NUM_TOPICS
- 1))
423 XtSetSensitive(btn
, True
);
425 XtSetSensitive(btn
, False
);
428 btn
=XtNameToWidget(HelpWindows
[topic
], "helpForm.histBack");
431 if(navHistBack
[topic
] != -1)
432 XtSetSensitive(btn
, True
);
434 XtSetSensitive(btn
, False
);
437 btn
=XtNameToWidget(HelpWindows
[topic
], "helpForm.histForw");
440 if(navHistForw
[topic
] != -1)
441 XtSetSensitive(btn
, True
);
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 */
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 */
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
) {
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
;
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
)
508 loadFontsAndColors(parent
, style
);
513 /* Beware of possible EBCDIC coding! Use the mapping table. */
515 *(sdp
++) = AlphabetToAsciiTable
[(unsigned char)style
];
522 /*-----------------------------------------
523 * Only deal with style map, when available.
524 *-----------------------------------------*/
526 *styleMap
= styleData
;
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
539 void Help(enum HelpTopic topic
)
541 if (HelpWindows
[topic
] != NULL
)
542 RaiseShellWindow(HelpWindows
[topic
]);
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);
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
);
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
)
579 Widget appShell
, btn
, closeBtn
, form
, btnFW
;
580 Widget sw
, hScrollBar
, vScrollBar
;
582 char * helpText
= NULL
;
583 char * styleData
= NULL
;
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
,
598 XtVaSetValues(form
, XmNshadowThickness
, 0, NULL
);
600 /* Create the bottom row of buttons */
601 btn
= XtVaCreateManagedWidget("find", xmPushButtonWidgetClass
, form
,
602 XmNlabelString
, st1
=XmStringCreateSimple("Find..."),
604 XmNbottomAttachment
, XmATTACH_FORM
,
605 XmNleftAttachment
, XmATTACH_POSITION
,
607 XmNrightAttachment
, XmATTACH_POSITION
,
608 XmNrightPosition
, 25,
610 XtAddCallback(btn
, XmNactivateCallback
, searchHelpCB
, appShell
);
613 btn
= XtVaCreateManagedWidget("findAgain", xmPushButtonWidgetClass
, form
,
614 XmNlabelString
, st1
=XmStringCreateSimple("Find Again"),
616 XmNbottomAttachment
, XmATTACH_FORM
,
617 XmNleftAttachment
, XmATTACH_POSITION
,
619 XmNrightAttachment
, XmATTACH_POSITION
,
620 XmNrightPosition
, 49,
622 XtAddCallback(btn
, XmNactivateCallback
, searchHelpAgainCB
, appShell
);
625 btn
= XtVaCreateManagedWidget("print", xmPushButtonWidgetClass
, form
,
626 XmNlabelString
, st1
=XmStringCreateSimple("Print..."),
628 XmNbottomAttachment
, XmATTACH_FORM
,
629 XmNleftAttachment
, XmATTACH_POSITION
,
631 XmNrightAttachment
, XmATTACH_POSITION
,
632 XmNrightPosition
, 73,
634 XtAddCallback(btn
, XmNactivateCallback
, printCB
, appShell
);
637 closeBtn
= XtVaCreateManagedWidget("close",
638 xmPushButtonWidgetClass
, form
,
639 XmNlabelString
, st1
=XmStringCreateSimple("Close"),
640 XmNbottomAttachment
, XmATTACH_FORM
,
641 XmNleftAttachment
, XmATTACH_POSITION
,
643 XmNrightAttachment
, XmATTACH_POSITION
,
644 XmNrightPosition
, 97,
646 XtAddCallback(closeBtn
, XmNactivateCallback
, closeCB
, appShell
);
649 /* Create the next row of buttons (for navigation) */
650 btn
= XtVaCreateManagedWidget("prevTopic", xmPushButtonWidgetClass
, form
,
651 XmNlabelString
, st1
=XmStringCreateSimple("<< Browse"),
653 XmNbottomAttachment
, XmATTACH_WIDGET
,
654 XmNbottomWidget
, closeBtn
,
655 XmNleftAttachment
, XmATTACH_POSITION
,
657 XmNrightAttachment
, XmATTACH_POSITION
,
658 XmNrightPosition
, 73,
660 XtAddCallback(btn
, XmNactivateCallback
, prevTopicCB
, appShell
);
663 btn
= XtVaCreateManagedWidget("nextTopic", xmPushButtonWidgetClass
, form
,
664 XmNlabelString
, st1
=XmStringCreateSimple("Browse >>"),
666 XmNbottomAttachment
, XmATTACH_WIDGET
,
667 XmNbottomWidget
, closeBtn
,
668 XmNleftAttachment
, XmATTACH_POSITION
,
670 XmNrightAttachment
, XmATTACH_POSITION
,
671 XmNrightPosition
, 97,
673 XtAddCallback(btn
, XmNactivateCallback
, nextTopicCB
, appShell
);
676 btn
= XtVaCreateManagedWidget("histBack", xmPushButtonWidgetClass
, form
,
677 XmNlabelString
, st1
=XmStringCreateSimple("Back"),
679 XmNbottomAttachment
, XmATTACH_WIDGET
,
680 XmNbottomWidget
, closeBtn
,
681 XmNleftAttachment
, XmATTACH_POSITION
,
683 XmNrightAttachment
, XmATTACH_POSITION
,
684 XmNrightPosition
, 25,
686 XtAddCallback(btn
, XmNactivateCallback
, bwHistoryCB
, appShell
);
689 btnFW
= XtVaCreateManagedWidget("histForw", xmPushButtonWidgetClass
, form
,
690 XmNlabelString
, st1
=XmStringCreateSimple("Forward"),
692 XmNbottomAttachment
, XmATTACH_WIDGET
,
693 XmNbottomWidget
, closeBtn
,
694 XmNleftAttachment
, XmATTACH_POSITION
,
696 XmNrightAttachment
, XmATTACH_POSITION
,
697 XmNrightPosition
, 49,
699 XtAddCallback(btnFW
, XmNactivateCallback
, fwHistoryCB
, appShell
);
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
,
711 hScrollBar
= XtVaCreateManagedWidget("hScrollBar",
712 xmScrollBarWidgetClass
, sw
,
713 XmNorientation
, XmHORIZONTAL
,
716 vScrollBar
= XtVaCreateManagedWidget("vScrollBar",
717 xmScrollBarWidgetClass
, sw
,
718 XmNorientation
, XmVERTICAL
,
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",
726 textNfont
, HelpStyleInfo
[0].font
, /* MUST correspond to 'A' above */
729 textNbacklightCharTypes
, NULL
,
730 textNhScrollBar
, hScrollBar
,
731 textNvScrollBar
, vScrollBar
,
733 textNcontinuousWrap
, True
,
734 textNautoShowInsertPos
, True
,
736 XtVaSetValues(sw
, XmNworkWindow
, HelpTextPanes
[topic
],
737 XmNhorizontalScrollBar
, hScrollBar
,
738 XmNverticalScrollBar
, vScrollBar
,
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
);
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
);
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
);
787 XtAddEventHandler (appShell
, (EventMask
)0, True
,
788 (XtEventHandler
)_XEditResCheckMessages
, NULL
);
794 static void changeTopicOrRaise(int existingTopic
, int newTopic
) {
795 if(HelpWindows
[newTopic
] == NULL
)
797 changeWindowTopic(existingTopic
, newTopic
);
798 adaptNavigationButtons(newTopic
);
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
)
815 if ((topic
= findTopicFromShellWidget((Widget
)clientData
)) == -1)
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
)
832 if ((topic
= findTopicFromShellWidget((Widget
)clientData
)) == -1)
833 return; /* shouldn't happen */
837 changeTopicOrRaise(topic
+1, topic
);
840 static void nextTopicCB(Widget w
, XtPointer clientData
, XtPointer callData
)
843 if ((topic
= findTopicFromShellWidget((Widget
)clientData
)) == -1)
844 return; /* shouldn't happen */
847 if(topic
< NUM_TOPICS
)
848 changeTopicOrRaise(topic
-1, topic
);
851 static void bwHistoryCB(Widget w
, XtPointer clientData
, XtPointer callData
)
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
)
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
];
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");
894 AddToHistoryList(promptText
, &searchHistory
, &nHistoryStrings
);
895 searchHelpText(HelpWindows
[topic
], topic
, promptText
, response
== 2, 0, 0);
898 static void searchHelpAgainCB(Widget w
, XtPointer clientData
,
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
;
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
],
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
)
933 /*------------------------------
934 * Direct topic links found here.
935 *------------------------------*/
936 for (*topic
=0; HelpTitles
[*topic
] != NULL
; (*topic
)++)
938 if (strcmp (link_name
, HelpTitles
[*topic
]) == 0)
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
;
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
;
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
]);
992 HelpWindows
[link_topic
] = createHelpPanel(link_topic
);
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
);
1007 static void helpFocusButtonsAP(Widget w
, XEvent
*event
, String
*args
,
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
,
1026 fprintf(stderr
, "help-button-action: requires exactly one argument.\n");
1030 /* Find the topic being displayed by this widget */
1031 for (topic
= 0; topic
< NUM_TOPICS
; topic
++)
1032 if (HelpTextPanes
[topic
] == w
)
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]);
1045 fprintf(stderr
, "help-button-action: argument too long");
1049 btn
=XtNameToWidget(HelpWindows
[topic
], buf
);
1052 XtCallCallbacks(btn
, XmNactivateCallback
, HelpWindows
[topic
]);
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
,
1074 XButtonEvent
*e
= (XButtonEvent
*)event
;
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 */
1088 newWin
= !strcmp(args
[0], "new");
1090 if(!newWin
&& strcmp(args
[0], "current")) {
1091 fprintf(stderr
, "help-hyperlink: Unrecognized argument %s\n", args
[0]);
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
)
1104 XtCallActionProc(w
, args
[2], event
, NULL
, 0);
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
])
1114 XtCallActionProc(w
, args
[2], event
, NULL
, 0);
1118 /* Find the topic being displayed by this widget */
1119 for (topic
= 0; topic
< NUM_TOPICS
; topic
++)
1120 if (HelpTextPanes
[topic
] == w
)
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.
1129 XtCallActionProc(w
, args
[2], event
, NULL
, 0);
1133 /* If the action help-hyperlink had 3 arguments execute the action
1134 * named in arg #2 before really following the link.
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
;
1166 char * helpText
= NULL
;
1168 /* Search for the string */
1169 for (topic
=startTopic
; topic
<NUM_TOPICS
; topic
++)
1171 if (!allSections
&& topic
!= parentTopic
)
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()))
1188 if (startPos
!= 0 || (allSections
&& startTopic
!= 0))
1191 searchHelpText(parent
, parentTopic
, searchFor
, allSections
, 0, 0);
1194 DialogF(DF_INF
, parent
, 1, "String Not Found", "String Not Found", "OK");
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
);
1249 BufSetAll(HelpStyleBuffers
[newTopic
], styleData
);
1251 TextDAttachHighlightData(((TextWidget
)HelpTextPanes
[newTopic
])->text
.textD
,
1252 HelpStyleBuffers
[newTopic
], HelpStyleInfo
, N_STYLES
, '\0', NULL
,
1256 static int findTopicFromShellWidget(Widget shellWidget
)
1260 for (i
=0; i
<NUM_TOPICS
; i
++)
1261 if (shellWidget
== HelpWindows
[i
])
1266 static void initNavigationHistory(void) {
1267 static int doInitNavigationHistory
= True
;
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);
1291 /* Print version info to stdout */
1292 void PrintVersion(void)
1296 #if XmVersion < 2001
1297 XmRegisterConverters(); /* see comment above */
1299 text
= getBuildInfo();