Ticket #1809 (invalid length of nonprintable chars)
[kaloumi3.git] / src / help.c
blob75f575864875fbfe39a13e8acb2e1448563c6211
1 /* Hypertext file browser.
2 Copyright (C) 1994, 1995, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
3 2005, 2006, 2007 Free Software Foundation, Inc.
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21 /** \file help.c
22 * \brief Source: hypertext file browser
24 * Implements the hypertext file viewer.
25 * The hypertext file is a file that may have one or more nodes. Each
26 * node ends with a ^D character and starts with a bracket, then the
27 * name of the node and then a closing bracket. Right after the closing
28 * bracket a newline is placed. This newline is not to be displayed by
29 * the help viewer and must be skipped - its sole purpose is to faciliate
30 * the work of the people managing the help file template (xnc.hlp) .
32 * Links in the hypertext file are specified like this: the text that
33 * will be highlighted should have a leading ^A, then it comes the
34 * text, then a ^B indicating that highlighting is done, then the name
35 * of the node you want to link to and then a ^C.
37 * The file must contain a ^D at the beginning and at the end of the
38 * file or the program will not be able to detect the end of file.
40 * Lazyness/widgeting attack: This file does use the dialog manager
41 * and uses mainly the dialog to achieve the help work. there is only
42 * one specialized widget and it's only used to forward the mouse messages
43 * to the appropiate routine.
47 #include <config.h>
49 #include <errno.h>
50 #include <stdio.h>
51 #include <sys/types.h>
52 #include <sys/stat.h>
54 #include "global.h"
56 #include "../src/tty/tty.h"
57 #include "../src/skin/skin.h"
58 #include "../src/tty/mouse.h"
59 #include "../src/tty/key.h"
61 #include "dialog.h" /* For Dlg_head */
62 #include "widget.h" /* For Widget */
63 #include "wtools.h" /* For common_dialog_repaint() */
64 #include "strutil.h"
65 #include "cmddef.h"
66 #include "keybind.h"
67 #include "help.h"
69 const global_keymap_t *help_map;
71 #define MAXLINKNAME 80
72 #define HISTORY_SIZE 20
73 #define HELP_WINDOW_WIDTH (HELP_TEXT_WIDTH + 4)
75 #define STRING_LINK_START "\01"
76 #define STRING_LINK_POINTER "\02"
77 #define STRING_LINK_END "\03"
78 #define STRING_NODE_END "\04"
81 static char *fdata = NULL; /* Pointer to the loaded data file */
82 static int help_lines; /* Lines in help viewer */
83 static int history_ptr; /* For the history queue */
84 static const char *main_node; /* The main node */
85 static const char *last_shown = NULL; /* Last byte shown in a screen */
86 static gboolean end_of_node = FALSE; /* Flag: the last character of the node shown? */
87 static const char *currentpoint;
88 static const char *selected_item;
90 /* The widget variables */
91 static Dlg_head *whelp;
93 static struct {
94 const char *page; /* Pointer to the selected page */
95 const char *link; /* Pointer to the selected link */
96 } history [HISTORY_SIZE];
98 /* Link areas for the mouse */
99 typedef struct Link_Area {
100 int x1, y1, x2, y2;
101 const char *link_name;
102 struct Link_Area *next;
103 } Link_Area;
105 static Link_Area *link_area = NULL;
106 static gboolean inside_link_area = FALSE;
108 static cb_ret_t help_callback (Dlg_head *h, Widget *sender,
109 dlg_msg_t msg, int parm, void *data);
111 /* returns the position where text was found in the start buffer */
112 /* or 0 if not found */
113 static const char *
114 search_string (const char *start, const char *text)
116 const char *result = NULL;
117 char *local_text = g_strdup (text);
118 char *d = local_text;
119 const char *e = start;
121 /* fmt sometimes replaces a space with a newline in the help file */
122 /* Replace the newlines in the link name with spaces to correct the situation */
123 while (*d != '\0') {
124 if (*d == '\n')
125 *d = ' ';
126 str_next_char (&d);
129 /* Do search */
130 for (d = local_text; *e; e++){
131 if (*d == *e)
132 d++;
133 else
134 d = local_text;
135 if (*d == '\0') {
136 result = e + 1;
137 break;
141 g_free (local_text);
142 return result;
145 /* Searches text in the buffer pointed by start. Search ends */
146 /* if the CHAR_NODE_END is found in the text. Returns 0 on failure */
147 static const char *
148 search_string_node (const char *start, const char *text)
150 const char *d = text;
151 const char *e = start;
153 if (start != NULL)
154 for (; *e && *e != CHAR_NODE_END; e++) {
155 if (*d == *e)
156 d++;
157 else
158 d = text;
159 if (*d == '\0')
160 return e + 1;
163 return NULL;
166 /* Searches the_char in the buffer pointer by start and searches */
167 /* it can search forward (direction = 1) or backward (direction = -1) */
168 static const char *
169 search_char_node (const char *start, char the_char, int direction)
171 const char *e;
173 for (e = start; (*e != '\0') && (*e != CHAR_NODE_END); e += direction)
174 if (*e == the_char)
175 return e;
177 return NULL;
180 /* Returns the new current pointer when moved lines lines */
181 static const char *
182 move_forward2 (const char *c, int lines)
184 const char *p;
185 int line;
187 currentpoint = c;
188 for (line = 0, p = currentpoint; (*p != '\0') && (*p != CHAR_NODE_END);
189 str_cnext_char (&p)) {
190 if (line == lines)
191 return currentpoint = p;
193 if (*p == '\n')
194 line++;
196 return currentpoint = c;
199 static const char *
200 move_backward2 (const char *c, int lines)
202 const char *p;
203 int line;
205 currentpoint = c;
206 for (line = 0, p = currentpoint; (*p != '\0') && ((int) (p - fdata) >= 0);
207 str_cprev_char (&p)) {
208 if (*p == CHAR_NODE_END) {
209 /* We reached the beginning of the node */
210 /* Skip the node headers */
211 while (*p != ']')
212 str_cnext_char (&p);
213 return currentpoint = p + 2; /* Skip the newline following the start of the node */
216 if (*(p - 1) == '\n')
217 line++;
218 if (line == lines)
219 return currentpoint = p;
221 return currentpoint = c;
224 static void
225 move_forward (int i)
227 if (!end_of_node)
228 currentpoint = move_forward2 (currentpoint, i);
231 static void
232 move_backward (int i)
234 currentpoint = move_backward2 (currentpoint, ++i);
237 static void
238 move_to_top (void)
240 while (((int) (currentpoint > fdata) > 0) && (*currentpoint != CHAR_NODE_END))
241 currentpoint--;
243 while (*currentpoint != ']')
244 currentpoint++;
245 currentpoint = currentpoint + 2; /* Skip the newline following the start of the node */
246 selected_item = NULL;
249 static void
250 move_to_bottom (void)
252 while ((*currentpoint != '\0') && (*currentpoint != CHAR_NODE_END))
253 currentpoint++;
254 currentpoint--;
255 move_backward (help_lines - 1);
258 static const char *
259 help_follow_link (const char *start, const char *lc_selected_item)
261 char link_name [MAXLINKNAME];
262 const char *p;
263 int i = 0;
265 if (lc_selected_item == NULL)
266 return start;
268 for (p = lc_selected_item; *p && *p != CHAR_NODE_END && *p != CHAR_LINK_POINTER; p++)
270 if (*p == CHAR_LINK_POINTER){
271 link_name [0] = '[';
272 for (i = 1; *p != CHAR_LINK_END && *p && *p != CHAR_NODE_END && i < MAXLINKNAME-3; )
273 link_name [i++] = *++p;
274 link_name [i - 1] = ']';
275 link_name [i] = '\0';
276 p = search_string (fdata, link_name);
277 if (p != NULL) {
278 p += 1; /* Skip the newline following the start of the node */
279 return p;
283 /* Create a replacement page with the error message */
284 return _(" Help file format error\n");
287 static const char *
288 select_next_link (const char *current_link)
290 const char *p;
292 if (current_link == NULL)
293 return NULL;
295 p = search_string_node (current_link, STRING_LINK_END);
296 if (p == NULL)
297 return NULL;
298 p = search_string_node (p, STRING_LINK_START);
299 if (p == NULL)
300 return NULL;
301 return p - 1;
304 static const char *
305 select_prev_link (const char *current_link)
307 return current_link == NULL
308 ? NULL
309 : search_char_node (current_link - 1, CHAR_LINK_START, -1);
312 static void
313 start_link_area (int x, int y, const char *link_name)
315 Link_Area *new;
317 if (inside_link_area)
318 message (D_NORMAL, _("Warning"), _(" Internal bug: Double start of link area "));
320 /* Allocate memory for a new link area */
321 new = g_new (Link_Area, 1);
322 new->next = link_area;
323 link_area = new;
325 /* Save the beginning coordinates of the link area */
326 link_area->x1 = x;
327 link_area->y1 = y;
329 /* Save the name of the destination anchor */
330 link_area->link_name = link_name;
332 inside_link_area = TRUE;
335 static void
336 end_link_area (int x, int y)
338 if (inside_link_area) {
339 /* Save the end coordinates of the link area */
340 link_area->x2 = x;
341 link_area->y2 = y;
342 inside_link_area = FALSE;
346 static void
347 clear_link_areas (void)
349 Link_Area *current;
351 while (link_area != NULL) {
352 current = link_area;
353 link_area = current->next;
354 g_free (current);
357 inside_link_area = FALSE;
360 static void
361 help_show (Dlg_head *h, const char *paint_start)
363 const char *p, *n;
364 int col, line, c, w;
365 gboolean painting = TRUE;
366 gboolean acs; /* Flag: Alternate character set active? */
367 gboolean repeat_paint;
368 int active_col, active_line; /* Active link position */
369 static char buff[MB_LEN_MAX + 1];
371 tty_setcolor (HELP_NORMAL_COLOR);
372 do {
373 line = col = active_col = active_line = 0;
374 repeat_paint = FALSE;
375 acs = FALSE;
377 clear_link_areas ();
378 if ((int) (selected_item - paint_start) < 0)
379 selected_item = NULL;
381 p = paint_start;
382 n = paint_start;
383 while (n[0] != '\0' && n[0] != CHAR_NODE_END && line < help_lines) {
384 p = n;
385 n = str_cget_next_char (p);
386 memcpy (buff, p, n - p);
387 buff[n - p] = '\0';
389 c = (unsigned char) buff[0];
390 switch (c){
391 case CHAR_LINK_START:
392 if (selected_item == NULL)
393 selected_item = p;
394 if (p == selected_item){
395 tty_setcolor (HELP_SLINK_COLOR);
397 /* Store the coordinates of the link */
398 active_col = col + 2;
399 active_line = line + 2;
401 else
402 tty_setcolor (HELP_LINK_COLOR);
403 start_link_area (col, line, p);
404 break;
405 case CHAR_LINK_POINTER:
406 painting = FALSE;
407 end_link_area (col - 1, line);
408 break;
409 case CHAR_LINK_END:
410 painting = TRUE;
411 tty_setcolor (HELP_NORMAL_COLOR);
412 break;
413 case CHAR_ALTERNATE:
414 acs = TRUE;
415 break;
416 case CHAR_NORMAL:
417 acs = FALSE;
418 break;
419 case CHAR_VERSION:
420 dlg_move (h, line+2, col+2);
421 tty_print_string (VERSION);
422 col += str_term_width1 (VERSION);
423 break;
424 case CHAR_FONT_BOLD:
425 tty_setcolor (HELP_BOLD_COLOR);
426 break;
427 case CHAR_FONT_ITALIC:
428 tty_setcolor (HELP_ITALIC_COLOR);
429 break;
430 case CHAR_FONT_NORMAL:
431 tty_setcolor (HELP_NORMAL_COLOR);
432 break;
433 case '\n':
434 line++;
435 col = 0;
436 break;
437 case '\t':
438 col = (col / 8 + 1) * 8;
439 break;
440 default:
441 if (!painting)
442 continue;
443 w = str_term_width1 (buff);
444 if (col + w > HELP_WINDOW_WIDTH)
445 continue;
447 dlg_move (h, line + 2, col + 2);
448 if (!acs)
449 tty_print_string (buff);
450 else if (c == ' ' || c == '.')
451 tty_print_char (c);
452 else
453 #ifndef HAVE_SLANG
454 tty_print_char (acs_map [c]);
455 #else
456 SLsmg_draw_object (h->y + line + 2, h->x + col + 2, c);
457 #endif
458 col += w;
459 break;
463 last_shown = p;
464 end_of_node = line < help_lines;
465 tty_setcolor (HELP_NORMAL_COLOR);
466 if ((int) (selected_item - last_shown) >= 0) {
467 if (link_area == NULL)
468 selected_item = NULL;
469 else {
470 selected_item = link_area->link_name;
471 repeat_paint = TRUE;
474 } while (repeat_paint);
476 /* Position the cursor over a nice link */
477 if (active_col)
478 dlg_move (h, active_line, active_col);
481 static int
482 help_event (Gpm_Event *event, void *vp)
484 Widget *w = vp;
485 Link_Area *current_area;
487 if ((event->type & GPM_UP) == 0)
488 return 0;
490 /* The event is relative to the dialog window, adjust it: */
491 event->x -= 2;
492 event->y -= 2;
494 if (event->buttons & GPM_B_RIGHT){
495 currentpoint = history [history_ptr].page;
496 selected_item = history [history_ptr].link;
497 history_ptr--;
498 if (history_ptr < 0)
499 history_ptr = HISTORY_SIZE-1;
501 help_callback (w->parent, NULL, DLG_DRAW, 0, NULL);
502 return 0;
505 /* Test whether the mouse click is inside one of the link areas */
506 current_area = link_area;
507 while (current_area != NULL)
509 /* Test one line link area */
510 if (event->y == current_area->y1 && event->x >= current_area->x1 &&
511 event->y == current_area->y2 && event->x <= current_area->x2)
512 break;
513 /* Test two line link area */
514 if (current_area->y1 + 1 == current_area->y2){
515 /* The first line */
516 if (event->y == current_area->y1 && event->x >= current_area->x1)
517 break;
518 /* The second line */
519 if (event->y == current_area->y2 && event->x <= current_area->x2)
520 break;
522 /* Mouse will not work with link areas of more than two lines */
524 current_area = current_area->next;
527 /* Test whether a link area was found */
528 if (current_area != NULL) {
529 /* The click was inside a link area -> follow the link */
530 history_ptr = (history_ptr+1) % HISTORY_SIZE;
531 history [history_ptr].page = currentpoint;
532 history [history_ptr].link = current_area->link_name;
533 currentpoint = help_follow_link (currentpoint, current_area->link_name);
534 selected_item = NULL;
535 } else if (event->y < 0)
536 move_backward (help_lines - 1);
537 else if (event->y >= help_lines)
538 move_forward (help_lines - 1);
539 else if (event->y < help_lines/2)
540 move_backward (1);
541 else
542 move_forward (1);
544 /* Show the new node */
545 help_callback (w->parent, NULL, DLG_DRAW, 0, NULL);
547 return 0;
550 /* show help */
551 static void
552 help_help (Dlg_head *h)
554 const char *p;
556 history_ptr = (history_ptr + 1) % HISTORY_SIZE;
557 history [history_ptr].page = currentpoint;
558 history [history_ptr].link = selected_item;
560 p = search_string (fdata, "[How to use help]");
561 if (p != NULL) {
562 currentpoint = p + 1; /* Skip the newline following the start of the node */
563 selected_item = NULL;
564 help_callback (h, NULL, DLG_DRAW, 0, NULL);
568 static void
569 help_index (Dlg_head *h)
571 const char *new_item;
573 new_item = search_string (fdata, "[Contents]");
575 if (new_item == NULL)
576 message (D_ERROR, MSG_ERROR, _(" Cannot find node %s in help file "),
577 "[Contents]");
578 else {
579 history_ptr = (history_ptr + 1) % HISTORY_SIZE;
580 history[history_ptr].page = currentpoint;
581 history[history_ptr].link = selected_item;
583 currentpoint = new_item + 1; /* Skip the newline following the start of the node */
584 selected_item = NULL;
585 help_callback (h, NULL, DLG_DRAW, 0, NULL);
589 static void
590 help_back (Dlg_head *h)
592 currentpoint = history [history_ptr].page;
593 selected_item = history [history_ptr].link;
594 history_ptr--;
595 if (history_ptr < 0)
596 history_ptr = HISTORY_SIZE - 1;
598 help_callback (h, NULL, DLG_DRAW, 0, NULL); /* FIXME: unneeded? */
601 static void
602 help_cmk_move_backward(void *vp, int lines)
604 (void) &vp;
605 move_backward (lines);
608 static void
609 help_cmk_move_forward(void *vp, int lines)
611 (void) &vp;
612 move_forward (lines);
615 static void
616 help_cmk_moveto_top(void *vp, int lines)
618 (void) &vp;
619 (void) &lines;
620 move_to_top ();
623 static void
624 help_cmk_moveto_bottom(void *vp, int lines)
626 (void) &vp;
627 (void) &lines;
628 move_to_bottom ();
631 static void
632 help_next_link (gboolean move_down)
634 const char *new_item;
636 new_item = select_next_link (selected_item);
637 if (new_item != NULL) {
638 selected_item = new_item;
639 if ((int) (selected_item - last_shown) >= 0) {
640 if (move_down)
641 move_forward (1);
642 else
643 selected_item = NULL;
645 } else if (move_down)
646 move_forward (1);
647 else
648 selected_item = NULL;
651 static void
652 help_prev_link (gboolean move_up)
654 const char *new_item;
656 new_item = select_prev_link (selected_item);
657 selected_item = new_item;
658 if ((selected_item == NULL) || (selected_item < currentpoint)) {
659 if (move_up)
660 move_backward (1);
661 else if (link_area != NULL)
662 selected_item = link_area->link_name;
663 else
664 selected_item = NULL;
668 static void
669 help_next_node (void)
671 const char *new_item;
673 new_item = currentpoint;
674 while ((*new_item != '\0') && (*new_item != CHAR_NODE_END))
675 new_item++;
677 if (*++new_item == '[')
678 while (*++new_item != '\0')
679 if ((*new_item == ']') && (*++new_item != '\0')
680 && (*++new_item != '\0')) {
681 currentpoint = new_item;
682 selected_item = NULL;
683 break;
687 static void
688 help_prev_node (void)
690 const char *new_item;
692 new_item = currentpoint;
693 while (((int) (new_item - fdata) > 1) && (*new_item != CHAR_NODE_END))
694 new_item--;
695 new_item--;
696 while (((int) (new_item - fdata) > 0) && (*new_item != CHAR_NODE_END))
697 new_item--;
698 while (*new_item != ']')
699 new_item++;
700 currentpoint = new_item + 2;
701 selected_item = NULL;
704 static void
705 help_select_link (void)
707 /* follow link */
708 if (selected_item == NULL) {
709 #ifdef WE_WANT_TO_GO_BACKWARD_ON_KEY_RIGHT
710 /* Is there any reason why the right key would take us
711 * backward if there are no links selected?, I agree
712 * with Torben than doing nothing in this case is better
714 /* If there are no links, go backward in history */
715 history_ptr--;
716 if (history_ptr < 0)
717 history_ptr = HISTORY_SIZE-1;
719 currentpoint = history [history_ptr].page;
720 selected_item = history [history_ptr].link;
721 #endif
722 } else {
723 history_ptr = (history_ptr + 1) % HISTORY_SIZE;
724 history [history_ptr].page = currentpoint;
725 history [history_ptr].link = selected_item;
726 currentpoint = help_follow_link (currentpoint, selected_item);
729 selected_item = NULL;
732 static cb_ret_t
733 help_execute_cmd (unsigned long command)
735 cb_ret_t ret = MSG_HANDLED;
737 switch (command) {
738 case CK_HelpHelp:
739 help_help (whelp);
740 break;
741 case CK_HelpIndex:
742 help_index (whelp);
743 break;
744 case CK_HelpBack:
745 help_back (whelp);
746 break;
747 case CK_HelpMoveUp:
748 help_prev_link (TRUE);
749 break;
750 case CK_HelpMoveDown:
751 help_next_link (TRUE);
752 break;
753 case CK_HelpSelectLink:
754 help_select_link ();
755 break;
756 case CK_HelpNextLink:
757 help_next_link (FALSE);
758 break;
759 case CK_HelpPrevLink:
760 help_prev_link (FALSE);
761 break;
762 case CK_HelpNextNode:
763 help_next_node ();
764 break;
765 case CK_HelpPrevNode:
766 help_prev_node ();
767 break;
768 case CK_HelpQuit:
769 dlg_stop (whelp);
770 break;
771 default:
772 ret = MSG_NOT_HANDLED;
775 return ret;
778 static cb_ret_t
779 help_handle_key (Dlg_head *h, int c)
781 if (c != KEY_UP && c != KEY_DOWN &&
782 check_movement_keys (c, help_lines, NULL,
783 help_cmk_move_backward,
784 help_cmk_move_forward,
785 help_cmk_moveto_top,
786 help_cmk_moveto_bottom) == MSG_HANDLED) {
787 /* Nothing */;
788 } else {
789 unsigned long command;
791 command = lookup_keymap_command (help_map, c);
792 if ((command == CK_Ignore_Key)
793 || (help_execute_cmd (command) == MSG_NOT_HANDLED))
794 return MSG_NOT_HANDLED;
797 help_callback (h, NULL, DLG_DRAW, 0, NULL);
798 return MSG_HANDLED;
801 static cb_ret_t
802 help_callback (Dlg_head *h, Widget *sender,
803 dlg_msg_t msg, int parm, void *data)
805 WButtonBar *bb;
807 switch (msg) {
808 case DLG_RESIZE:
809 help_lines = min (LINES - 4, max (2 * LINES / 3, 18));
810 dlg_set_size (h, help_lines + 4, HELP_WINDOW_WIDTH + 4);
811 bb = find_buttonbar (h);
812 widget_set_size (&bb->widget, LINES - 1, 0, 1, COLS);
813 return MSG_HANDLED;
815 case DLG_DRAW:
816 common_dialog_repaint (h);
817 help_show (h, currentpoint);
818 return MSG_HANDLED;
820 case DLG_KEY:
821 return help_handle_key (h, parm);
823 case DLG_ACTION:
824 /* command from buttonbar */
825 return help_execute_cmd (parm);
827 default:
828 return default_dlg_callback (h, sender, msg, parm, data);
832 static void
833 interactive_display_finish (void)
835 clear_link_areas ();
838 /* translate help file into terminal encoding */
839 static void
840 translate_file (char *filedata)
842 GIConv conv;
843 GString *translated_data;
845 translated_data = g_string_new ("");
847 conv = str_crt_conv_from ("UTF-8");
849 if (conv == INVALID_CONV)
850 g_string_free (translated_data, TRUE);
851 else {
852 g_free (fdata);
854 if (str_convert (conv, filedata, translated_data) != ESTR_FAILURE) {
855 fdata = translated_data->str;
856 g_string_free (translated_data, FALSE);
857 } else {
858 fdata = NULL;
859 g_string_free (translated_data, TRUE);
861 str_close_conv (conv);
865 static cb_ret_t
866 md_callback (Widget *w, widget_msg_t msg, int parm)
868 switch (msg) {
869 case WIDGET_RESIZED:
870 w->lines = help_lines;
871 return MSG_HANDLED;
873 default:
874 return default_proc (msg, parm);
878 static Widget *
879 mousedispatch_new (int y, int x, int yl, int xl)
881 Widget *w = g_new (Widget, 1);
882 init_widget (w, y, x, yl, xl, md_callback, help_event);
883 return w;
886 void
887 interactive_display (const char *filename, const char *node)
889 WButtonBar *help_bar;
890 Widget *md;
891 char *hlpfile = NULL;
892 char *filedata;
894 if (filename != NULL)
895 filedata = load_file (filename);
896 else
897 filedata = load_mc_home_file ("mc.hlp", &hlpfile);
899 if (filedata == NULL)
900 message (D_ERROR, MSG_ERROR, _(" Cannot open file %s \n %s "), filename ? filename : hlpfile,
901 unix_error_string (errno));
903 g_free (hlpfile);
905 if (filedata == NULL)
906 return;
908 translate_file (filedata);
910 g_free (filedata);
912 if (fdata == NULL)
913 return;
915 if ((node == NULL) || (*node == '\0'))
916 node = "[main]";
918 main_node = search_string (fdata, node);
920 if (main_node == NULL) {
921 message (D_ERROR, MSG_ERROR, _(" Cannot find node %s in help file "),
922 node);
924 /* Fallback to [main], return if it also cannot be found */
925 main_node = search_string (fdata, "[main]");
926 if (main_node == NULL) {
927 interactive_display_finish ();
928 return;
932 help_lines = min (LINES - 4, max (2 * LINES / 3, 18));
934 whelp =
935 create_dlg (0, 0, help_lines + 4, HELP_WINDOW_WIDTH + 4,
936 dialog_colors, help_callback, "[Help]", _("Help"),
937 DLG_TRYUP | DLG_CENTER | DLG_WANT_TAB);
939 selected_item = search_string_node (main_node, STRING_LINK_START) - 1;
940 currentpoint = main_node + 1; /* Skip the newline following the start of the node */
942 for (history_ptr = HISTORY_SIZE; history_ptr;) {
943 history_ptr--;
944 history[history_ptr].page = currentpoint;
945 history[history_ptr].link = selected_item;
948 help_bar = buttonbar_new (TRUE);
949 help_bar->widget.y -= whelp->y;
950 help_bar->widget.x -= whelp->x;
952 md = mousedispatch_new (1, 1, help_lines, HELP_WINDOW_WIDTH - 2);
954 add_widget (whelp, md);
955 add_widget (whelp, help_bar);
957 buttonbar_set_label (help_bar, 1, Q_("ButtonBar|Help"), help_map, NULL);
958 buttonbar_set_label (help_bar, 2, Q_("ButtonBar|Index"), help_map, NULL);
959 buttonbar_set_label (help_bar, 3, Q_("ButtonBar|Prev"), help_map, NULL);
960 buttonbar_set_label (help_bar, 4, "", help_map, NULL);
961 buttonbar_set_label (help_bar, 5, "", help_map, NULL);
962 buttonbar_set_label (help_bar, 6, "", help_map, NULL);
963 buttonbar_set_label (help_bar, 7, "", help_map, NULL);
964 buttonbar_set_label (help_bar, 8, "", help_map, NULL);
965 buttonbar_set_label (help_bar, 9, "", help_map, NULL);
966 buttonbar_set_label (help_bar, 10, Q_("ButtonBar|Quit"), help_map, NULL);
968 run_dlg (whelp);
969 interactive_display_finish ();
970 destroy_dlg (whelp);