1 static const char CVSID
[] = "$Id: help.c,v 1.104 2005/02/12 01:53:19 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 *
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
, BOLD_FIXED_HELP_FONT
,
116 BOLD_ITALIC_FIXED_HELP_FONT
,
118 /* Underlined fixed fonts, styles: 'E', 'F', 'G', 'H' */
119 FIXED_HELP_FONT
, BOLD_FIXED_HELP_FONT
, BOLD_FIXED_HELP_FONT
,
120 BOLD_ITALIC_FIXED_HELP_FONT
,
122 /* Normal (proportional) fonts, styles: 'I', 'J', 'K', 'L' */
123 HELP_FONT
, BOLD_HELP_FONT
, ITALIC_HELP_FONT
, BOLD_ITALIC_HELP_FONT
,
125 /* Underlined fonts, styles: 'M', 'N', 'O', 'P' */
126 HELP_FONT
, BOLD_HELP_FONT
, ITALIC_HELP_FONT
, BOLD_ITALIC_HELP_FONT
,
128 /* Link font, style: 'Q' */
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
[] = {"", "(Known Bad) ", "(Untested) "};
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
, 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
] = createHelpPanel(link_topic
);
997 changeWindowTopic(topic
, link_topic
);
1000 navHistBack
[link_topic
] = topic
;
1001 navHistForw
[topic
] = link_topic
;
1002 TextSetCursorPos(HelpTextPanes
[link_topic
], link_pos
);
1003 adaptNavigationButtons(link_topic
);
1004 adaptNavigationButtons(topic
);
1009 static void helpFocusButtonsAP(Widget w
, XEvent
*event
, String
*args
,
1012 XmProcessTraversal(w
, XmTRAVERSE_NEXT_TAB_GROUP
);
1016 * handler for help-button-action(<button-name>)
1017 * Calls the activate callback for the named button widget of the help text win.
1019 static void helpButtonActionAP(Widget w
, XEvent
*event
, String
*args
,
1028 fprintf(stderr
, "help-button-action: requires exactly one argument.\n");
1032 /* Find the topic being displayed by this widget */
1033 for (topic
= 0; topic
< NUM_TOPICS
; topic
++)
1034 if (HelpTextPanes
[topic
] == w
)
1037 if(topic
== NUM_TOPICS
|| HelpWindows
[topic
] == NULL
)
1038 return; /* Shouldn't happen */
1040 /* Compose the button widget name */
1041 strcpy(&buf
[0], "helpForm.");
1042 if (strlen(args
[0]) <= 70)
1044 strcat(&buf
[0], args
[0]);
1047 fprintf(stderr
, "help-button-action: argument too long");
1051 btn
=XtNameToWidget(HelpWindows
[topic
], buf
);
1054 XtCallCallbacks(btn
, XmNactivateCallback
, HelpWindows
[topic
]);
1057 fprintf(stderr
, "help-button-action: invalid argument: %s\n", args
[0]);
1062 * Handler for action help-hyperlink()
1063 * Arguments: none - init: record event position
1064 * "current": if clicked on a link, follow link in same window
1065 * "new": if clicked on a link, follow link in new window
1067 * With the 1st argument "current" or "new" this action can have two additional
1068 * arguments. These arguments must be valid names of XmText actions.
1069 * In this case, the action named in argument #2 is called if the action
1070 * help-hyperlink is about to follow the hyperlink. The Action in argument #3
1071 * is called if no hyperlink has been recognized at the current event position.
1073 static void helpHyperlinkAP(Widget w
, XEvent
*event
, String
*args
,
1076 XButtonEvent
*e
= (XButtonEvent
*)event
;
1078 textDisp
*textD
= ((TextWidget
)w
)->text
.textD
;
1079 int clickedPos
, newWin
;
1080 static int pressX
=0, pressY
=0;
1082 /* called without arguments we just record coordinates */
1090 newWin
= !strcmp(args
[0], "new");
1092 if(!newWin
&& strcmp(args
[0], "current")) {
1093 fprintf(stderr
, "help-hyperlink: Unrecognized argument %s\n", args
[0]);
1098 * If for any reason (pointer moved - drag!, no hyperlink found)
1099 * this action can't follow a hyperlink then execute the the action
1100 * named in arg #3 (if provided)
1102 if (abs(pressX
- e
->x
) > CLICK_THRESHOLD
1103 || abs(pressY
- e
->y
) > CLICK_THRESHOLD
)
1106 XtCallActionProc(w
, args
[2], event
, NULL
, 0);
1110 clickedPos
= TextDXYToCharPos(textD
, e
->x
, e
->y
);
1111 /* Beware of possible EBCDIC coding! Use the mapping table. */
1112 if (BufGetCharacter(textD
->styleBuffer
, clickedPos
) !=
1113 (char)AlphabetToAsciiTable
[(unsigned char)STL_NM_LINK
])
1116 XtCallActionProc(w
, args
[2], event
, NULL
, 0);
1120 /* Find the topic being displayed by this widget */
1121 for (topic
= 0; topic
< NUM_TOPICS
; topic
++)
1122 if (HelpTextPanes
[topic
] == w
)
1125 if (topic
== NUM_TOPICS
)
1127 /* If we get here someone must have bound help-hyperlink to a non-help
1128 * text widget (!) Or some other really strange thing happened.
1131 XtCallActionProc(w
, args
[2], event
, NULL
, 0);
1135 /* If the action help-hyperlink had 3 arguments execute the action
1136 * named in arg #2 before really following the link.
1139 XtCallActionProc(w
, args
[1], event
, NULL
, 0);
1141 followHyperlink(topic
, clickedPos
, newWin
);
1145 ** Install the action for following hyperlinks in the help window
1147 void InstallHelpLinkActions(XtAppContext context
)
1149 static XtActionsRec Actions
[] =
1151 {"help-hyperlink", helpHyperlinkAP
},
1152 {"help-focus-buttons", helpFocusButtonsAP
},
1153 {"help-button-action", helpButtonActionAP
}
1156 XtAppAddActions(context
, Actions
, XtNumber(Actions
));
1160 ** Search the help text. If allSections is true, searches all of the help
1161 ** text, otherwise searches only in parentTopic.
1163 static void searchHelpText(Widget parent
, int parentTopic
,
1164 const char *searchFor
, int allSections
, int startPos
, int startTopic
)
1166 int topic
, beginMatch
, endMatch
;
1168 char * helpText
= NULL
;
1170 /* Search for the string */
1171 for (topic
=startTopic
; topic
<NUM_TOPICS
; topic
++)
1173 if (!allSections
&& topic
!= parentTopic
)
1175 helpText
= stitch(parent
, HelpText
[topic
], NULL
);
1177 if (SearchString(helpText
, searchFor
, SEARCH_FORWARD
, SEARCH_LITERAL
,
1178 False
, topic
== startTopic
? startPos
: 0, &beginMatch
,
1179 &endMatch
, NULL
, NULL
, GetPrefDelimiters()))
1190 if (startPos
!= 0 || (allSections
&& startTopic
!= 0))
1193 searchHelpText(parent
, parentTopic
, searchFor
, allSections
, 0, 0);
1196 DialogF(DF_INF
, parent
, 1, "String Not Found", "String Not Found", "OK");
1200 /* update navigation history */
1201 if (parentTopic
!= topic
)
1203 navHistForw
[parentTopic
]= topic
;
1204 navHistBack
[topic
]= parentTopic
;
1207 /* If the appropriate window is already up, bring it to the top, if not,
1208 make the parent window become this topic */
1209 changeTopicOrRaise(parentTopic
, topic
);
1210 BufSelect(TextGetBuffer(HelpTextPanes
[topic
]), beginMatch
, endMatch
);
1211 TextSetCursorPos(HelpTextPanes
[topic
], endMatch
);
1213 /* Save the search information for search-again */
1214 strcpy(LastSearchString
, searchFor
);
1215 LastSearchTopic
= topic
;
1216 LastSearchPos
= endMatch
;
1217 LastSearchWasAllTopics
= allSections
;
1221 ** Change a help window to display a new topic. (Help window data is stored
1222 ** and indexed by topic so if a given topic is already displayed or has been
1223 ** positioned by the user, it can be found and popped back up in the same
1224 ** place.) To change the topic displayed, the stored data has to be relocated.
1226 static void changeWindowTopic(int existingTopic
, enum HelpTopic newTopic
)
1228 char *helpText
, *styleData
;
1230 /* Relocate the window/widget/buffer information */
1231 if (newTopic
!= existingTopic
)
1233 HelpWindows
[newTopic
] = HelpWindows
[existingTopic
];
1234 HelpWindows
[existingTopic
] = NULL
;
1235 HelpStyleBuffers
[newTopic
] = HelpStyleBuffers
[existingTopic
];
1236 HelpStyleBuffers
[existingTopic
] = NULL
;
1237 HelpTextPanes
[newTopic
] = HelpTextPanes
[existingTopic
];
1238 HelpTextPanes
[existingTopic
] = NULL
;
1239 setHelpWinTitle(HelpWindows
[newTopic
], newTopic
);
1242 /* Set the existing text widget to display the new text. Because it's
1243 highlighted, we have to turn off highlighting before changing the
1244 displayed text to prevent the text widget from trying to apply the
1245 old, mismatched, highlighting to the new text */
1246 helpText
= stitch(HelpTextPanes
[newTopic
], HelpText
[newTopic
], &styleData
);
1247 TextDAttachHighlightData(((TextWidget
)HelpTextPanes
[newTopic
])->text
.textD
,
1248 NULL
, NULL
, 0, '\0', NULL
, NULL
);
1249 BufSetAll(TextGetBuffer(HelpTextPanes
[newTopic
]), helpText
);
1251 BufSetAll(HelpStyleBuffers
[newTopic
], styleData
);
1253 TextDAttachHighlightData(((TextWidget
)HelpTextPanes
[newTopic
])->text
.textD
,
1254 HelpStyleBuffers
[newTopic
], HelpStyleInfo
, N_STYLES
, '\0', NULL
,
1258 static int findTopicFromShellWidget(Widget shellWidget
)
1262 for (i
=0; i
<NUM_TOPICS
; i
++)
1263 if (shellWidget
== HelpWindows
[i
])
1268 static void initNavigationHistory(void) {
1269 static int doInitNavigationHistory
= True
;
1272 if (doInitNavigationHistory
)
1274 for (i
=0; i
<NUM_TOPICS
; i
++)
1275 navHistBack
[i
] = navHistForw
[i
] = -1;
1277 doInitNavigationHistory
= False
;
1281 #if XmVersion == 2000
1282 /* amai: This function may be called before the Motif part
1283 is being initialized. The following, public interface
1284 is known to initialize at least xmUseVersion.
1285 That interface is declared in <Xm/Xm.h> in Motif 1.2 only.
1286 As for Motif 2.1 we don't need this call anymore.
1287 This also holds for the Motif 2.1 version of LessTif
1288 releases > 0.93.0. */
1289 extern void XmRegisterConverters(void);
1293 /* Print version info to stdout */
1294 void PrintVersion(void)
1298 #if XmVersion < 2001
1299 XmRegisterConverters(); /* see comment above */
1301 text
= getBuildInfo();