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