1 static const char CVSID
[] = "$Id: help.c,v 1.107 2008/08/19 22:24:41 ajbj 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/motif.h"
49 #include "../util/misc.h"
50 #include "../util/DialogF.h"
51 #include "../util/system.h"
58 #include "../util/VMSparam.h"
61 #include <sys/param.h>
66 #include <Xm/XmP.h> /* These are for applying style info to help text */
68 #include <Xm/PrimitiveP.h>
69 #include <Xm/ScrolledW.h>
70 #include <Xm/ScrollBar.h>
73 #include <X11/Xmu/Editres.h>
74 /* extern void _XEditResCheckMessages(); */
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
, ITALIC_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
, ITALIC_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' */
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' */
152 /* Heading fonts, styles: 'R', 'S', 'T' */
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
,
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
[];
199 static char _XmVersionString
[] = "unknown";
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
=
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"
241 static const char *visualClass
[] = {"StaticGray", "GrayScale",
242 "StaticColor", "PseudoColor",
243 "TrueColor", "DirectColor"};
245 static const char *const stabilities
[] = {"", "(Untested) ", "(Known Bad) "};
247 if (bldInfoString
== NULL
)
250 char visualStr
[500] = "<unknown>";
251 const enum MotifStability stab
= GetMotifStability();
257 Boolean usingDefaultVisual
= FindBestVisual(TheDisplay
, APP_NAME
,
260 sprintf(visualStr
,"%d-bit %s (ID %#lx%s)",
262 visualClass
[visual
->class],
264 usingDefaultVisual
? ", Default" : "");
267 bldInfoString
= XtMalloc(strlen(bldFormat
) + strlen(warning
) + 1024);
268 locale
= setlocale(LC_MESSAGES
, "");
270 sprintf(bldInfoString
, bldFormat
,
272 COMPILE_OS
, COMPILE_MACHINE
, COMPILE_COMPILER
,
274 stabilities
[stab
], XmVERSION
, XmREVISION
, XmUPDATE_LEVEL
,
276 xmUseVersion
/1000, xmUseVersion
%1000,
278 (NULL
== TheDisplay
? "<unknown>" : ServerVendor(TheDisplay
)),
279 (NULL
== TheDisplay
? 0 : VendorRelease(TheDisplay
)),
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
)
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
);
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
)
382 if (HelpStyleInfo
[STYLE_INDEX(style
)].font
== NULL
)
384 font
= XLoadQueryFont(XtDisplay(parent
),
385 GetPrefHelpFontName(StyleFonts
[STYLE_INDEX(style
)]));
388 fprintf(stderr
, "NEdit: help font, %s, not available\n",
389 GetPrefHelpFontName(StyleFonts
[STYLE_INDEX(style
)]));
390 font
= XLoadQueryFont(XtDisplay(parent
), "fixed");
393 fprintf(stderr
, "NEdit: fallback help font, \"fixed\", not "
394 "available, cannot continue\n");
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
) {
409 if(HelpWindows
[topic
] == NULL
)
410 return; /* Shouldn't happen */
412 btn
=XtNameToWidget(HelpWindows
[topic
], "helpForm.prevTopic");
416 XtSetSensitive(btn
, True
);
418 XtSetSensitive(btn
, False
);
421 btn
=XtNameToWidget(HelpWindows
[topic
], "helpForm.nextTopic");
424 if(topic
< (NUM_TOPICS
- 1))
425 XtSetSensitive(btn
, True
);
427 XtSetSensitive(btn
, False
);
430 btn
=XtNameToWidget(HelpWindows
[topic
], "helpForm.histBack");
433 if(navHistBack
[topic
] != -1)
434 XtSetSensitive(btn
, True
);
436 XtSetSensitive(btn
, False
);
439 btn
=XtNameToWidget(HelpWindows
[topic
], "helpForm.histForw");
442 if(navHistForw
[topic
] != -1)
443 XtSetSensitive(btn
, True
);
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 */
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 */
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
) {
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
;
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
)
510 loadFontsAndColors(parent
, style
);
515 /* Beware of possible EBCDIC coding! Use the mapping table. */
517 *(sdp
++) = AlphabetToAsciiTable
[(unsigned char)style
];
524 /*-----------------------------------------
525 * Only deal with style map, when available.
526 *-----------------------------------------*/
528 *styleMap
= styleData
;
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
541 void Help(enum HelpTopic topic
)
543 if (HelpWindows
[topic
] != NULL
)
544 RaiseShellWindow(HelpWindows
[topic
], True
);
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);
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
);
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
)
581 Widget appShell
, btn
, closeBtn
, form
, btnFW
;
582 Widget sw
, hScrollBar
, vScrollBar
;
584 char * helpText
= NULL
;
585 char * styleData
= NULL
;
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
,
600 XtVaSetValues(form
, XmNshadowThickness
, 0, NULL
);
602 /* Create the bottom row of buttons */
603 btn
= XtVaCreateManagedWidget("find", xmPushButtonWidgetClass
, form
,
604 XmNlabelString
, st1
=XmStringCreateSimple("Find..."),
606 XmNbottomAttachment
, XmATTACH_FORM
,
607 XmNleftAttachment
, XmATTACH_POSITION
,
609 XmNrightAttachment
, XmATTACH_POSITION
,
610 XmNrightPosition
, 25,
612 XtAddCallback(btn
, XmNactivateCallback
, searchHelpCB
, appShell
);
615 btn
= XtVaCreateManagedWidget("findAgain", xmPushButtonWidgetClass
, form
,
616 XmNlabelString
, st1
=XmStringCreateSimple("Find Again"),
618 XmNbottomAttachment
, XmATTACH_FORM
,
619 XmNleftAttachment
, XmATTACH_POSITION
,
621 XmNrightAttachment
, XmATTACH_POSITION
,
622 XmNrightPosition
, 49,
624 XtAddCallback(btn
, XmNactivateCallback
, searchHelpAgainCB
, appShell
);
627 btn
= XtVaCreateManagedWidget("print", xmPushButtonWidgetClass
, form
,
628 XmNlabelString
, st1
=XmStringCreateSimple("Print..."),
630 XmNbottomAttachment
, XmATTACH_FORM
,
631 XmNleftAttachment
, XmATTACH_POSITION
,
633 XmNrightAttachment
, XmATTACH_POSITION
,
634 XmNrightPosition
, 73,
636 XtAddCallback(btn
, XmNactivateCallback
, printCB
, appShell
);
639 closeBtn
= XtVaCreateManagedWidget("close",
640 xmPushButtonWidgetClass
, form
,
641 XmNlabelString
, st1
=XmStringCreateSimple("Close"),
642 XmNbottomAttachment
, XmATTACH_FORM
,
643 XmNleftAttachment
, XmATTACH_POSITION
,
645 XmNrightAttachment
, XmATTACH_POSITION
,
646 XmNrightPosition
, 97,
648 XtAddCallback(closeBtn
, XmNactivateCallback
, closeCB
, appShell
);
651 /* Create the next row of buttons (for navigation) */
652 btn
= XtVaCreateManagedWidget("prevTopic", xmPushButtonWidgetClass
, form
,
653 XmNlabelString
, st1
=XmStringCreateSimple("<< Browse"),
655 XmNbottomAttachment
, XmATTACH_WIDGET
,
656 XmNbottomWidget
, closeBtn
,
657 XmNleftAttachment
, XmATTACH_POSITION
,
659 XmNrightAttachment
, XmATTACH_POSITION
,
660 XmNrightPosition
, 73,
662 XtAddCallback(btn
, XmNactivateCallback
, prevTopicCB
, appShell
);
665 btn
= XtVaCreateManagedWidget("nextTopic", xmPushButtonWidgetClass
, form
,
666 XmNlabelString
, st1
=XmStringCreateSimple("Browse >>"),
668 XmNbottomAttachment
, XmATTACH_WIDGET
,
669 XmNbottomWidget
, closeBtn
,
670 XmNleftAttachment
, XmATTACH_POSITION
,
672 XmNrightAttachment
, XmATTACH_POSITION
,
673 XmNrightPosition
, 97,
675 XtAddCallback(btn
, XmNactivateCallback
, nextTopicCB
, appShell
);
678 btn
= XtVaCreateManagedWidget("histBack", xmPushButtonWidgetClass
, form
,
679 XmNlabelString
, st1
=XmStringCreateSimple("Back"),
681 XmNbottomAttachment
, XmATTACH_WIDGET
,
682 XmNbottomWidget
, closeBtn
,
683 XmNleftAttachment
, XmATTACH_POSITION
,
685 XmNrightAttachment
, XmATTACH_POSITION
,
686 XmNrightPosition
, 25,
688 XtAddCallback(btn
, XmNactivateCallback
, bwHistoryCB
, appShell
);
691 btnFW
= XtVaCreateManagedWidget("histForw", xmPushButtonWidgetClass
, form
,
692 XmNlabelString
, st1
=XmStringCreateSimple("Forward"),
694 XmNbottomAttachment
, XmATTACH_WIDGET
,
695 XmNbottomWidget
, closeBtn
,
696 XmNleftAttachment
, XmATTACH_POSITION
,
698 XmNrightAttachment
, XmATTACH_POSITION
,
699 XmNrightPosition
, 49,
701 XtAddCallback(btnFW
, XmNactivateCallback
, fwHistoryCB
, appShell
);
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
,
713 hScrollBar
= XtVaCreateManagedWidget("hScrollBar",
714 xmScrollBarWidgetClass
, sw
,
715 XmNorientation
, XmHORIZONTAL
,
718 vScrollBar
= XtVaCreateManagedWidget("vScrollBar",
719 xmScrollBarWidgetClass
, sw
,
720 XmNorientation
, XmVERTICAL
,
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",
728 textNfont
, HelpStyleInfo
[0].font
, /* MUST correspond to 'A' above */
731 textNbacklightCharTypes
, NULL
,
732 textNhScrollBar
, hScrollBar
,
733 textNvScrollBar
, vScrollBar
,
735 textNcontinuousWrap
, True
,
736 textNautoShowInsertPos
, True
,
738 XtVaSetValues(sw
, XmNworkWindow
, HelpTextPanes
[topic
],
739 XmNhorizontalScrollBar
, hScrollBar
,
740 XmNverticalScrollBar
, vScrollBar
,
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
);
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
);
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
);
789 XtAddEventHandler (appShell
, (EventMask
)0, True
,
790 (XtEventHandler
)_XEditResCheckMessages
, NULL
);
796 static void changeTopicOrRaise(int existingTopic
, int newTopic
) {
797 if(HelpWindows
[newTopic
] == NULL
)
799 changeWindowTopic(existingTopic
, (enum HelpTopic
) newTopic
);
800 adaptNavigationButtons(newTopic
);
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
)
817 if ((topic
= findTopicFromShellWidget((Widget
)clientData
)) == -1)
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
)
834 if ((topic
= findTopicFromShellWidget((Widget
)clientData
)) == -1)
835 return; /* shouldn't happen */
839 changeTopicOrRaise(topic
+1, topic
);
842 static void nextTopicCB(Widget w
, XtPointer clientData
, XtPointer callData
)
845 if ((topic
= findTopicFromShellWidget((Widget
)clientData
)) == -1)
846 return; /* shouldn't happen */
849 if(topic
< NUM_TOPICS
)
850 changeTopicOrRaise(topic
-1, topic
);
853 static void bwHistoryCB(Widget w
, XtPointer clientData
, XtPointer callData
)
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
)
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
];
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");
896 AddToHistoryList(promptText
, &searchHistory
, &nHistoryStrings
);
897 searchHelpText(HelpWindows
[topic
], topic
, promptText
, response
== 2, 0, 0);
900 static void searchHelpAgainCB(Widget w
, XtPointer clientData
,
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
;
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
],
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
)
935 /*------------------------------
936 * Direct topic links found here.
937 *------------------------------*/
938 for (*topic
=0; HelpTitles
[*topic
] != NULL
; (*topic
)++)
940 if (strcmp (link_name
, HelpTitles
[*topic
]) == 0)
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
;
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
;
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
);
994 HelpWindows
[link_topic
]
995 = createHelpPanel((enum HelpTopic
) link_topic
);
998 changeWindowTopic(topic
, (enum HelpTopic
) link_topic
);
1001 navHistBack
[link_topic
] = topic
;
1002 navHistForw
[topic
] = link_topic
;
1003 TextSetCursorPos(HelpTextPanes
[link_topic
], link_pos
);
1004 adaptNavigationButtons(link_topic
);
1005 adaptNavigationButtons(topic
);
1010 static void helpFocusButtonsAP(Widget w
, XEvent
*event
, String
*args
,
1013 XmProcessTraversal(w
, XmTRAVERSE_NEXT_TAB_GROUP
);
1017 * handler for help-button-action(<button-name>)
1018 * Calls the activate callback for the named button widget of the help text win.
1020 static void helpButtonActionAP(Widget w
, XEvent
*event
, String
*args
,
1029 fprintf(stderr
, "help-button-action: requires exactly one argument.\n");
1033 /* Find the topic being displayed by this widget */
1034 for (topic
= 0; topic
< NUM_TOPICS
; topic
++)
1035 if (HelpTextPanes
[topic
] == w
)
1038 if(topic
== NUM_TOPICS
|| HelpWindows
[topic
] == NULL
)
1039 return; /* Shouldn't happen */
1041 /* Compose the button widget name */
1042 strcpy(&buf
[0], "helpForm.");
1043 if (strlen(args
[0]) <= 70)
1045 strcat(&buf
[0], args
[0]);
1048 fprintf(stderr
, "help-button-action: argument too long");
1052 btn
=XtNameToWidget(HelpWindows
[topic
], buf
);
1055 XtCallCallbacks(btn
, XmNactivateCallback
, HelpWindows
[topic
]);
1058 fprintf(stderr
, "help-button-action: invalid argument: %s\n", args
[0]);
1063 * Handler for action help-hyperlink()
1064 * Arguments: none - init: record event position
1065 * "current": if clicked on a link, follow link in same window
1066 * "new": if clicked on a link, follow link in new window
1068 * With the 1st argument "current" or "new" this action can have two additional
1069 * arguments. These arguments must be valid names of XmText actions.
1070 * In this case, the action named in argument #2 is called if the action
1071 * help-hyperlink is about to follow the hyperlink. The Action in argument #3
1072 * is called if no hyperlink has been recognized at the current event position.
1074 static void helpHyperlinkAP(Widget w
, XEvent
*event
, String
*args
,
1077 XButtonEvent
*e
= (XButtonEvent
*)event
;
1079 textDisp
*textD
= ((TextWidget
)w
)->text
.textD
;
1080 int clickedPos
, newWin
;
1081 static int pressX
=0, pressY
=0;
1083 /* called without arguments we just record coordinates */
1091 newWin
= !strcmp(args
[0], "new");
1093 if(!newWin
&& strcmp(args
[0], "current")) {
1094 fprintf(stderr
, "help-hyperlink: Unrecognized argument %s\n", args
[0]);
1099 * If for any reason (pointer moved - drag!, no hyperlink found)
1100 * this action can't follow a hyperlink then execute the the action
1101 * named in arg #3 (if provided)
1103 if (abs(pressX
- e
->x
) > CLICK_THRESHOLD
1104 || abs(pressY
- e
->y
) > CLICK_THRESHOLD
)
1107 XtCallActionProc(w
, args
[2], event
, NULL
, 0);
1111 clickedPos
= TextDXYToCharPos(textD
, e
->x
, e
->y
);
1112 /* Beware of possible EBCDIC coding! Use the mapping table. */
1113 if (BufGetCharacter(textD
->styleBuffer
, clickedPos
) !=
1114 (char)AlphabetToAsciiTable
[(unsigned char)STL_NM_LINK
])
1117 XtCallActionProc(w
, args
[2], event
, NULL
, 0);
1121 /* Find the topic being displayed by this widget */
1122 for (topic
= 0; topic
< NUM_TOPICS
; topic
++)
1123 if (HelpTextPanes
[topic
] == w
)
1126 if (topic
== NUM_TOPICS
)
1128 /* If we get here someone must have bound help-hyperlink to a non-help
1129 * text widget (!) Or some other really strange thing happened.
1132 XtCallActionProc(w
, args
[2], event
, NULL
, 0);
1136 /* If the action help-hyperlink had 3 arguments execute the action
1137 * named in arg #2 before really following the link.
1140 XtCallActionProc(w
, args
[1], event
, NULL
, 0);
1142 followHyperlink(topic
, clickedPos
, newWin
);
1146 ** Install the action for following hyperlinks in the help window
1148 void InstallHelpLinkActions(XtAppContext context
)
1150 static XtActionsRec Actions
[] =
1152 {"help-hyperlink", helpHyperlinkAP
},
1153 {"help-focus-buttons", helpFocusButtonsAP
},
1154 {"help-button-action", helpButtonActionAP
}
1157 XtAppAddActions(context
, Actions
, XtNumber(Actions
));
1161 ** Search the help text. If allSections is true, searches all of the help
1162 ** text, otherwise searches only in parentTopic.
1164 static void searchHelpText(Widget parent
, int parentTopic
,
1165 const char *searchFor
, int allSections
, int startPos
, int startTopic
)
1167 int topic
, beginMatch
, endMatch
;
1169 char * helpText
= NULL
;
1171 /* Search for the string */
1172 for (topic
=startTopic
; topic
<NUM_TOPICS
; topic
++)
1174 if (!allSections
&& topic
!= parentTopic
)
1176 helpText
= stitch(parent
, HelpText
[topic
], NULL
);
1178 if (SearchString(helpText
, searchFor
, SEARCH_FORWARD
, SEARCH_LITERAL
,
1179 False
, topic
== startTopic
? startPos
: 0, &beginMatch
,
1180 &endMatch
, NULL
, NULL
, GetPrefDelimiters()))
1191 if (startPos
!= 0 || (allSections
&& startTopic
!= 0))
1194 searchHelpText(parent
, parentTopic
, searchFor
, allSections
, 0, 0);
1197 DialogF(DF_INF
, parent
, 1, "String Not Found", "String Not Found", "OK");
1201 /* update navigation history */
1202 if (parentTopic
!= topic
)
1204 navHistForw
[parentTopic
]= topic
;
1205 navHistBack
[topic
]= parentTopic
;
1208 /* If the appropriate window is already up, bring it to the top, if not,
1209 make the parent window become this topic */
1210 changeTopicOrRaise(parentTopic
, topic
);
1211 BufSelect(TextGetBuffer(HelpTextPanes
[topic
]), beginMatch
, endMatch
);
1212 TextSetCursorPos(HelpTextPanes
[topic
], endMatch
);
1214 /* Save the search information for search-again */
1215 strcpy(LastSearchString
, searchFor
);
1216 LastSearchTopic
= topic
;
1217 LastSearchPos
= endMatch
;
1218 LastSearchWasAllTopics
= allSections
;
1222 ** Change a help window to display a new topic. (Help window data is stored
1223 ** and indexed by topic so if a given topic is already displayed or has been
1224 ** positioned by the user, it can be found and popped back up in the same
1225 ** place.) To change the topic displayed, the stored data has to be relocated.
1227 static void changeWindowTopic(int existingTopic
, enum HelpTopic newTopic
)
1229 char *helpText
, *styleData
;
1231 /* Relocate the window/widget/buffer information */
1232 if (newTopic
!= existingTopic
)
1234 HelpWindows
[newTopic
] = HelpWindows
[existingTopic
];
1235 HelpWindows
[existingTopic
] = NULL
;
1236 HelpStyleBuffers
[newTopic
] = HelpStyleBuffers
[existingTopic
];
1237 HelpStyleBuffers
[existingTopic
] = NULL
;
1238 HelpTextPanes
[newTopic
] = HelpTextPanes
[existingTopic
];
1239 HelpTextPanes
[existingTopic
] = NULL
;
1240 setHelpWinTitle(HelpWindows
[newTopic
], newTopic
);
1243 /* Set the existing text widget to display the new text. Because it's
1244 highlighted, we have to turn off highlighting before changing the
1245 displayed text to prevent the text widget from trying to apply the
1246 old, mismatched, highlighting to the new text */
1247 helpText
= stitch(HelpTextPanes
[newTopic
], HelpText
[newTopic
], &styleData
);
1248 TextDAttachHighlightData(((TextWidget
)HelpTextPanes
[newTopic
])->text
.textD
,
1249 NULL
, NULL
, 0, '\0', NULL
, NULL
);
1250 BufSetAll(TextGetBuffer(HelpTextPanes
[newTopic
]), helpText
);
1252 BufSetAll(HelpStyleBuffers
[newTopic
], styleData
);
1254 TextDAttachHighlightData(((TextWidget
)HelpTextPanes
[newTopic
])->text
.textD
,
1255 HelpStyleBuffers
[newTopic
], HelpStyleInfo
, N_STYLES
, '\0', NULL
,
1259 static int findTopicFromShellWidget(Widget shellWidget
)
1263 for (i
=0; i
<NUM_TOPICS
; i
++)
1264 if (shellWidget
== HelpWindows
[i
])
1269 static void initNavigationHistory(void) {
1270 static int doInitNavigationHistory
= True
;
1273 if (doInitNavigationHistory
)
1275 for (i
=0; i
<NUM_TOPICS
; i
++)
1276 navHistBack
[i
] = navHistForw
[i
] = -1;
1278 doInitNavigationHistory
= False
;
1282 #if XmVersion == 2000
1283 /* amai: This function may be called before the Motif part
1284 is being initialized. The following, public interface
1285 is known to initialize at least xmUseVersion.
1286 That interface is declared in <Xm/Xm.h> in Motif 1.2 only.
1287 As for Motif 2.1 we don't need this call anymore.
1288 This also holds for the Motif 2.1 version of LessTif
1289 releases > 0.93.0. */
1290 extern void XmRegisterConverters(void);
1294 /* Print version info to stdout */
1295 void PrintVersion(void)
1299 #if XmVersion < 2001
1300 XmRegisterConverters(); /* see comment above */
1302 text
= getBuildInfo();