Moved src/util.[ch] into lib, also moved unixcompat.h and utilunix.c.
[midnight-commander.git] / src / help.c
blob1e615b203db5c4c208c4d21084c6f7fbce479865
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 "lib/global.h"
56 #include "lib/tty/tty.h"
57 #include "lib/skin/skin.h"
58 #include "lib/tty/mouse.h"
59 #include "lib/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"
68 #include "main.h"
70 const global_keymap_t *help_map;
72 #define MAXLINKNAME 80
73 #define HISTORY_SIZE 20
74 #define HELP_WINDOW_WIDTH (HELP_TEXT_WIDTH + 4)
76 #define STRING_LINK_START "\01"
77 #define STRING_LINK_POINTER "\02"
78 #define STRING_LINK_END "\03"
79 #define STRING_NODE_END "\04"
82 static char *fdata = NULL; /* Pointer to the loaded data file */
83 static int help_lines; /* Lines in help viewer */
84 static int history_ptr; /* For the history queue */
85 static const char *main_node; /* The main node */
86 static const char *last_shown = NULL; /* Last byte shown in a screen */
87 static gboolean end_of_node = FALSE; /* Flag: the last character of the node shown? */
88 static const char *currentpoint;
89 static const char *selected_item;
91 /* The widget variables */
92 static Dlg_head *whelp;
94 static struct {
95 const char *page; /* Pointer to the selected page */
96 const char *link; /* Pointer to the selected link */
97 } history [HISTORY_SIZE];
99 /* Link areas for the mouse */
100 typedef struct Link_Area {
101 int x1, y1, x2, y2;
102 const char *link_name;
103 struct Link_Area *next;
104 } Link_Area;
106 static Link_Area *link_area = NULL;
107 static gboolean inside_link_area = FALSE;
109 static cb_ret_t help_callback (Dlg_head *h, Widget *sender,
110 dlg_msg_t msg, int parm, void *data);
112 /* returns the position where text was found in the start buffer */
113 /* or 0 if not found */
114 static const char *
115 search_string (const char *start, const char *text)
117 const char *result = NULL;
118 char *local_text = g_strdup (text);
119 char *d = local_text;
120 const char *e = start;
122 /* fmt sometimes replaces a space with a newline in the help file */
123 /* Replace the newlines in the link name with spaces to correct the situation */
124 while (*d != '\0') {
125 if (*d == '\n')
126 *d = ' ';
127 str_next_char (&d);
130 /* Do search */
131 for (d = local_text; *e; e++){
132 if (*d == *e)
133 d++;
134 else
135 d = local_text;
136 if (*d == '\0') {
137 result = e + 1;
138 break;
142 g_free (local_text);
143 return result;
146 /* Searches text in the buffer pointed by start. Search ends */
147 /* if the CHAR_NODE_END is found in the text. Returns 0 on failure */
148 static const char *
149 search_string_node (const char *start, const char *text)
151 const char *d = text;
152 const char *e = start;
154 if (start != NULL)
155 for (; *e && *e != CHAR_NODE_END; e++) {
156 if (*d == *e)
157 d++;
158 else
159 d = text;
160 if (*d == '\0')
161 return e + 1;
164 return NULL;
167 /* Searches the_char in the buffer pointer by start and searches */
168 /* it can search forward (direction = 1) or backward (direction = -1) */
169 static const char *
170 search_char_node (const char *start, char the_char, int direction)
172 const char *e;
174 for (e = start; (*e != '\0') && (*e != CHAR_NODE_END); e += direction)
175 if (*e == the_char)
176 return e;
178 return NULL;
181 /* Returns the new current pointer when moved lines lines */
182 static const char *
183 move_forward2 (const char *c, int lines)
185 const char *p;
186 int line;
188 currentpoint = c;
189 for (line = 0, p = currentpoint; (*p != '\0') && (*p != CHAR_NODE_END);
190 str_cnext_char (&p)) {
191 if (line == lines)
192 return currentpoint = p;
194 if (*p == '\n')
195 line++;
197 return currentpoint = c;
200 static const char *
201 move_backward2 (const char *c, int lines)
203 const char *p;
204 int line;
206 currentpoint = c;
207 for (line = 0, p = currentpoint; (*p != '\0') && ((int) (p - fdata) >= 0);
208 str_cprev_char (&p)) {
209 if (*p == CHAR_NODE_END) {
210 /* We reached the beginning of the node */
211 /* Skip the node headers */
212 while (*p != ']')
213 str_cnext_char (&p);
214 return currentpoint = p + 2; /* Skip the newline following the start of the node */
217 if (*(p - 1) == '\n')
218 line++;
219 if (line == lines)
220 return currentpoint = p;
222 return currentpoint = c;
225 static void
226 move_forward (int i)
228 if (!end_of_node)
229 currentpoint = move_forward2 (currentpoint, i);
232 static void
233 move_backward (int i)
235 currentpoint = move_backward2 (currentpoint, ++i);
238 static void
239 move_to_top (void)
241 while (((int) (currentpoint > fdata) > 0) && (*currentpoint != CHAR_NODE_END))
242 currentpoint--;
244 while (*currentpoint != ']')
245 currentpoint++;
246 currentpoint = currentpoint + 2; /* Skip the newline following the start of the node */
247 selected_item = NULL;
250 static void
251 move_to_bottom (void)
253 while ((*currentpoint != '\0') && (*currentpoint != CHAR_NODE_END))
254 currentpoint++;
255 currentpoint--;
256 move_backward (help_lines - 1);
259 static const char *
260 help_follow_link (const char *start, const char *lc_selected_item)
262 char link_name [MAXLINKNAME];
263 const char *p;
264 int i = 0;
266 if (lc_selected_item == NULL)
267 return start;
269 for (p = lc_selected_item; *p && *p != CHAR_NODE_END && *p != CHAR_LINK_POINTER; p++)
271 if (*p == CHAR_LINK_POINTER){
272 link_name [0] = '[';
273 for (i = 1; *p != CHAR_LINK_END && *p && *p != CHAR_NODE_END && i < MAXLINKNAME-3; )
274 link_name [i++] = *++p;
275 link_name [i - 1] = ']';
276 link_name [i] = '\0';
277 p = search_string (fdata, link_name);
278 if (p != NULL) {
279 p += 1; /* Skip the newline following the start of the node */
280 return p;
284 /* Create a replacement page with the error message */
285 return _(" Help file format error\n");
288 static const char *
289 select_next_link (const char *current_link)
291 const char *p;
293 if (current_link == NULL)
294 return NULL;
296 p = search_string_node (current_link, STRING_LINK_END);
297 if (p == NULL)
298 return NULL;
299 p = search_string_node (p, STRING_LINK_START);
300 if (p == NULL)
301 return NULL;
302 return p - 1;
305 static const char *
306 select_prev_link (const char *current_link)
308 return current_link == NULL
309 ? NULL
310 : search_char_node (current_link - 1, CHAR_LINK_START, -1);
313 static void
314 start_link_area (int x, int y, const char *link_name)
316 Link_Area *new;
318 if (inside_link_area)
319 message (D_NORMAL, _("Warning"), _(" Internal bug: Double start of link area "));
321 /* Allocate memory for a new link area */
322 new = g_new (Link_Area, 1);
323 new->next = link_area;
324 link_area = new;
326 /* Save the beginning coordinates of the link area */
327 link_area->x1 = x;
328 link_area->y1 = y;
330 /* Save the name of the destination anchor */
331 link_area->link_name = link_name;
333 inside_link_area = TRUE;
336 static void
337 end_link_area (int x, int y)
339 if (inside_link_area) {
340 /* Save the end coordinates of the link area */
341 link_area->x2 = x;
342 link_area->y2 = y;
343 inside_link_area = FALSE;
347 static void
348 clear_link_areas (void)
350 Link_Area *current;
352 while (link_area != NULL) {
353 current = link_area;
354 link_area = current->next;
355 g_free (current);
358 inside_link_area = FALSE;
361 static void
362 help_show (Dlg_head *h, const char *paint_start)
364 const char *p, *n;
365 int col, line, c, w;
366 gboolean painting = TRUE;
367 gboolean acs; /* Flag: Alternate character set active? */
368 gboolean repeat_paint;
369 int active_col, active_line; /* Active link position */
370 static char buff[MB_LEN_MAX + 1];
372 tty_setcolor (HELP_NORMAL_COLOR);
373 do {
374 line = col = active_col = active_line = 0;
375 repeat_paint = FALSE;
376 acs = FALSE;
378 clear_link_areas ();
379 if ((int) (selected_item - paint_start) < 0)
380 selected_item = NULL;
382 p = paint_start;
383 n = paint_start;
384 while (n[0] != '\0' && n[0] != CHAR_NODE_END && line < help_lines) {
385 p = n;
386 n = str_cget_next_char (p);
387 memcpy (buff, p, n - p);
388 buff[n - p] = '\0';
390 c = (unsigned char) buff[0];
391 switch (c){
392 case CHAR_LINK_START:
393 if (selected_item == NULL)
394 selected_item = p;
395 if (p == selected_item){
396 tty_setcolor (HELP_SLINK_COLOR);
398 /* Store the coordinates of the link */
399 active_col = col + 2;
400 active_line = line + 2;
402 else
403 tty_setcolor (HELP_LINK_COLOR);
404 start_link_area (col, line, p);
405 break;
406 case CHAR_LINK_POINTER:
407 painting = FALSE;
408 end_link_area (col - 1, line);
409 break;
410 case CHAR_LINK_END:
411 painting = TRUE;
412 tty_setcolor (HELP_NORMAL_COLOR);
413 break;
414 case CHAR_ALTERNATE:
415 acs = TRUE;
416 break;
417 case CHAR_NORMAL:
418 acs = FALSE;
419 break;
420 case CHAR_VERSION:
421 dlg_move (h, line+2, col+2);
422 tty_print_string (VERSION);
423 col += str_term_width1 (VERSION);
424 break;
425 case CHAR_FONT_BOLD:
426 tty_setcolor (HELP_BOLD_COLOR);
427 break;
428 case CHAR_FONT_ITALIC:
429 tty_setcolor (HELP_ITALIC_COLOR);
430 break;
431 case CHAR_FONT_NORMAL:
432 tty_setcolor (HELP_NORMAL_COLOR);
433 break;
434 case '\n':
435 line++;
436 col = 0;
437 break;
438 case '\t':
439 col = (col / 8 + 1) * 8;
440 break;
441 default:
442 if (!painting)
443 continue;
444 w = str_term_width1 (buff);
445 if (col + w > HELP_WINDOW_WIDTH)
446 continue;
448 dlg_move (h, line + 2, col + 2);
449 if (!acs)
450 tty_print_string (buff);
451 else if (c == ' ' || c == '.')
452 tty_print_char (c);
453 else
454 #ifndef HAVE_SLANG
455 tty_print_char (acs_map [c]);
456 #else
457 SLsmg_draw_object (h->y + line + 2, h->x + col + 2, c);
458 #endif
459 col += w;
460 break;
464 last_shown = p;
465 end_of_node = line < help_lines;
466 tty_setcolor (HELP_NORMAL_COLOR);
467 if ((int) (selected_item - last_shown) >= 0) {
468 if (link_area == NULL)
469 selected_item = NULL;
470 else {
471 selected_item = link_area->link_name;
472 repeat_paint = TRUE;
475 } while (repeat_paint);
477 /* Position the cursor over a nice link */
478 if (active_col)
479 dlg_move (h, active_line, active_col);
482 static int
483 help_event (Gpm_Event *event, void *vp)
485 Widget *w = vp;
486 Link_Area *current_area;
488 if ((event->type & GPM_UP) == 0)
489 return 0;
491 /* The event is relative to the dialog window, adjust it: */
492 event->x -= 2;
493 event->y -= 2;
495 if (event->buttons & GPM_B_RIGHT){
496 currentpoint = history [history_ptr].page;
497 selected_item = history [history_ptr].link;
498 history_ptr--;
499 if (history_ptr < 0)
500 history_ptr = HISTORY_SIZE-1;
502 help_callback (w->parent, NULL, DLG_DRAW, 0, NULL);
503 return 0;
506 /* Test whether the mouse click is inside one of the link areas */
507 current_area = link_area;
508 while (current_area != NULL)
510 /* Test one line link area */
511 if (event->y == current_area->y1 && event->x >= current_area->x1 &&
512 event->y == current_area->y2 && event->x <= current_area->x2)
513 break;
514 /* Test two line link area */
515 if (current_area->y1 + 1 == current_area->y2){
516 /* The first line */
517 if (event->y == current_area->y1 && event->x >= current_area->x1)
518 break;
519 /* The second line */
520 if (event->y == current_area->y2 && event->x <= current_area->x2)
521 break;
523 /* Mouse will not work with link areas of more than two lines */
525 current_area = current_area->next;
528 /* Test whether a link area was found */
529 if (current_area != NULL) {
530 /* The click was inside a link area -> follow the link */
531 history_ptr = (history_ptr+1) % HISTORY_SIZE;
532 history [history_ptr].page = currentpoint;
533 history [history_ptr].link = current_area->link_name;
534 currentpoint = help_follow_link (currentpoint, current_area->link_name);
535 selected_item = NULL;
536 } else if (event->y < 0)
537 move_backward (help_lines - 1);
538 else if (event->y >= help_lines)
539 move_forward (help_lines - 1);
540 else if (event->y < help_lines/2)
541 move_backward (1);
542 else
543 move_forward (1);
545 /* Show the new node */
546 help_callback (w->parent, NULL, DLG_DRAW, 0, NULL);
548 return 0;
551 /* show help */
552 static void
553 help_help (Dlg_head *h)
555 const char *p;
557 history_ptr = (history_ptr + 1) % HISTORY_SIZE;
558 history [history_ptr].page = currentpoint;
559 history [history_ptr].link = selected_item;
561 p = search_string (fdata, "[How to use help]");
562 if (p != NULL) {
563 currentpoint = p + 1; /* Skip the newline following the start of the node */
564 selected_item = NULL;
565 help_callback (h, NULL, DLG_DRAW, 0, NULL);
569 static void
570 help_index (Dlg_head *h)
572 const char *new_item;
574 new_item = search_string (fdata, "[Contents]");
576 if (new_item == NULL)
577 message (D_ERROR, MSG_ERROR, _(" Cannot find node %s in help file "),
578 "[Contents]");
579 else {
580 history_ptr = (history_ptr + 1) % HISTORY_SIZE;
581 history[history_ptr].page = currentpoint;
582 history[history_ptr].link = selected_item;
584 currentpoint = new_item + 1; /* Skip the newline following the start of the node */
585 selected_item = NULL;
586 help_callback (h, NULL, DLG_DRAW, 0, NULL);
590 static void
591 help_back (Dlg_head *h)
593 currentpoint = history [history_ptr].page;
594 selected_item = history [history_ptr].link;
595 history_ptr--;
596 if (history_ptr < 0)
597 history_ptr = HISTORY_SIZE - 1;
599 help_callback (h, NULL, DLG_DRAW, 0, NULL); /* FIXME: unneeded? */
602 static void
603 help_cmk_move_backward(void *vp, int lines)
605 (void) &vp;
606 move_backward (lines);
609 static void
610 help_cmk_move_forward(void *vp, int lines)
612 (void) &vp;
613 move_forward (lines);
616 static void
617 help_cmk_moveto_top(void *vp, int lines)
619 (void) &vp;
620 (void) &lines;
621 move_to_top ();
624 static void
625 help_cmk_moveto_bottom(void *vp, int lines)
627 (void) &vp;
628 (void) &lines;
629 move_to_bottom ();
632 static void
633 help_next_link (gboolean move_down)
635 const char *new_item;
637 new_item = select_next_link (selected_item);
638 if (new_item != NULL) {
639 selected_item = new_item;
640 if ((int) (selected_item - last_shown) >= 0) {
641 if (move_down)
642 move_forward (1);
643 else
644 selected_item = NULL;
646 } else if (move_down)
647 move_forward (1);
648 else
649 selected_item = NULL;
652 static void
653 help_prev_link (gboolean move_up)
655 const char *new_item;
657 new_item = select_prev_link (selected_item);
658 selected_item = new_item;
659 if ((selected_item == NULL) || (selected_item < currentpoint)) {
660 if (move_up)
661 move_backward (1);
662 else if (link_area != NULL)
663 selected_item = link_area->link_name;
664 else
665 selected_item = NULL;
669 static void
670 help_next_node (void)
672 const char *new_item;
674 new_item = currentpoint;
675 while ((*new_item != '\0') && (*new_item != CHAR_NODE_END))
676 new_item++;
678 if (*++new_item == '[')
679 while (*++new_item != '\0')
680 if ((*new_item == ']') && (*++new_item != '\0')
681 && (*++new_item != '\0')) {
682 currentpoint = new_item;
683 selected_item = NULL;
684 break;
688 static void
689 help_prev_node (void)
691 const char *new_item;
693 new_item = currentpoint;
694 while (((int) (new_item - fdata) > 1) && (*new_item != CHAR_NODE_END))
695 new_item--;
696 new_item--;
697 while (((int) (new_item - fdata) > 0) && (*new_item != CHAR_NODE_END))
698 new_item--;
699 while (*new_item != ']')
700 new_item++;
701 currentpoint = new_item + 2;
702 selected_item = NULL;
705 static void
706 help_select_link (void)
708 /* follow link */
709 if (selected_item == NULL) {
710 #ifdef WE_WANT_TO_GO_BACKWARD_ON_KEY_RIGHT
711 /* Is there any reason why the right key would take us
712 * backward if there are no links selected?, I agree
713 * with Torben than doing nothing in this case is better
715 /* If there are no links, go backward in history */
716 history_ptr--;
717 if (history_ptr < 0)
718 history_ptr = HISTORY_SIZE-1;
720 currentpoint = history [history_ptr].page;
721 selected_item = history [history_ptr].link;
722 #endif
723 } else {
724 history_ptr = (history_ptr + 1) % HISTORY_SIZE;
725 history [history_ptr].page = currentpoint;
726 history [history_ptr].link = selected_item;
727 currentpoint = help_follow_link (currentpoint, selected_item);
730 selected_item = NULL;
733 static cb_ret_t
734 help_execute_cmd (unsigned long command)
736 cb_ret_t ret = MSG_HANDLED;
738 switch (command) {
739 case CK_HelpHelp:
740 help_help (whelp);
741 break;
742 case CK_HelpIndex:
743 help_index (whelp);
744 break;
745 case CK_HelpBack:
746 help_back (whelp);
747 break;
748 case CK_HelpMoveUp:
749 help_prev_link (TRUE);
750 break;
751 case CK_HelpMoveDown:
752 help_next_link (TRUE);
753 break;
754 case CK_HelpSelectLink:
755 help_select_link ();
756 break;
757 case CK_HelpNextLink:
758 help_next_link (FALSE);
759 break;
760 case CK_HelpPrevLink:
761 help_prev_link (FALSE);
762 break;
763 case CK_HelpNextNode:
764 help_next_node ();
765 break;
766 case CK_HelpPrevNode:
767 help_prev_node ();
768 break;
769 case CK_HelpQuit:
770 dlg_stop (whelp);
771 break;
772 default:
773 ret = MSG_NOT_HANDLED;
776 return ret;
779 static cb_ret_t
780 help_handle_key (Dlg_head *h, int c)
782 if (c != KEY_UP && c != KEY_DOWN &&
783 check_movement_keys (c, help_lines, NULL,
784 help_cmk_move_backward,
785 help_cmk_move_forward,
786 help_cmk_moveto_top,
787 help_cmk_moveto_bottom) == MSG_HANDLED) {
788 /* Nothing */;
789 } else {
790 unsigned long command;
792 command = lookup_keymap_command (help_map, c);
793 if ((command == CK_Ignore_Key)
794 || (help_execute_cmd (command) == MSG_NOT_HANDLED))
795 return MSG_NOT_HANDLED;
798 help_callback (h, NULL, DLG_DRAW, 0, NULL);
799 return MSG_HANDLED;
802 static cb_ret_t
803 help_callback (Dlg_head *h, Widget *sender,
804 dlg_msg_t msg, int parm, void *data)
806 WButtonBar *bb;
808 switch (msg) {
809 case DLG_RESIZE:
810 help_lines = min (LINES - 4, max (2 * LINES / 3, 18));
811 dlg_set_size (h, help_lines + 4, HELP_WINDOW_WIDTH + 4);
812 bb = find_buttonbar (h);
813 widget_set_size (&bb->widget, LINES - 1, 0, 1, COLS);
814 return MSG_HANDLED;
816 case DLG_DRAW:
817 common_dialog_repaint (h);
818 help_show (h, currentpoint);
819 return MSG_HANDLED;
821 case DLG_KEY:
822 return help_handle_key (h, parm);
824 case DLG_ACTION:
825 /* command from buttonbar */
826 return help_execute_cmd (parm);
828 default:
829 return default_dlg_callback (h, sender, msg, parm, data);
833 static void
834 interactive_display_finish (void)
836 clear_link_areas ();
839 /* translate help file into terminal encoding */
840 static void
841 translate_file (char *filedata)
843 GIConv conv;
844 GString *translated_data;
846 translated_data = g_string_new ("");
848 conv = str_crt_conv_from ("UTF-8");
850 if (conv == INVALID_CONV)
851 g_string_free (translated_data, TRUE);
852 else {
853 g_free (fdata);
855 if (str_convert (conv, filedata, translated_data) != ESTR_FAILURE) {
856 fdata = translated_data->str;
857 g_string_free (translated_data, FALSE);
858 } else {
859 fdata = NULL;
860 g_string_free (translated_data, TRUE);
862 str_close_conv (conv);
866 static cb_ret_t
867 md_callback (Widget *w, widget_msg_t msg, int parm)
869 switch (msg) {
870 case WIDGET_RESIZED:
871 w->lines = help_lines;
872 return MSG_HANDLED;
874 default:
875 return default_proc (msg, parm);
879 static Widget *
880 mousedispatch_new (int y, int x, int yl, int xl)
882 Widget *w = g_new (Widget, 1);
883 init_widget (w, y, x, yl, xl, md_callback, help_event);
884 return w;
887 void
888 interactive_display (const char *filename, const char *node)
890 WButtonBar *help_bar;
891 Widget *md;
892 char *hlpfile = NULL;
893 char *filedata;
895 if (filename != NULL)
896 filedata = load_file (filename);
897 else
898 filedata = load_mc_home_file (mc_home, mc_home_alt, "mc.hlp", &hlpfile);
900 if (filedata == NULL)
901 message (D_ERROR, MSG_ERROR, _(" Cannot open file %s \n %s "), filename ? filename : hlpfile,
902 unix_error_string (errno));
904 g_free (hlpfile);
906 if (filedata == NULL)
907 return;
909 translate_file (filedata);
911 g_free (filedata);
913 if (fdata == NULL)
914 return;
916 if ((node == NULL) || (*node == '\0'))
917 node = "[main]";
919 main_node = search_string (fdata, node);
921 if (main_node == NULL) {
922 message (D_ERROR, MSG_ERROR, _(" Cannot find node %s in help file "),
923 node);
925 /* Fallback to [main], return if it also cannot be found */
926 main_node = search_string (fdata, "[main]");
927 if (main_node == NULL) {
928 interactive_display_finish ();
929 return;
933 help_lines = min (LINES - 4, max (2 * LINES / 3, 18));
935 whelp =
936 create_dlg (0, 0, help_lines + 4, HELP_WINDOW_WIDTH + 4,
937 dialog_colors, help_callback, "[Help]", _("Help"),
938 DLG_TRYUP | DLG_CENTER | DLG_WANT_TAB);
940 selected_item = search_string_node (main_node, STRING_LINK_START) - 1;
941 currentpoint = main_node + 1; /* Skip the newline following the start of the node */
943 for (history_ptr = HISTORY_SIZE; history_ptr;) {
944 history_ptr--;
945 history[history_ptr].page = currentpoint;
946 history[history_ptr].link = selected_item;
949 help_bar = buttonbar_new (TRUE);
950 help_bar->widget.y -= whelp->y;
951 help_bar->widget.x -= whelp->x;
953 md = mousedispatch_new (1, 1, help_lines, HELP_WINDOW_WIDTH - 2);
955 add_widget (whelp, md);
956 add_widget (whelp, help_bar);
958 buttonbar_set_label (help_bar, 1, Q_("ButtonBar|Help"), help_map, NULL);
959 buttonbar_set_label (help_bar, 2, Q_("ButtonBar|Index"), help_map, NULL);
960 buttonbar_set_label (help_bar, 3, Q_("ButtonBar|Prev"), help_map, NULL);
961 buttonbar_set_label (help_bar, 4, "", help_map, NULL);
962 buttonbar_set_label (help_bar, 5, "", help_map, NULL);
963 buttonbar_set_label (help_bar, 6, "", help_map, NULL);
964 buttonbar_set_label (help_bar, 7, "", help_map, NULL);
965 buttonbar_set_label (help_bar, 8, "", help_map, NULL);
966 buttonbar_set_label (help_bar, 9, "", help_map, NULL);
967 buttonbar_set_label (help_bar, 10, Q_("ButtonBar|Quit"), help_map, NULL);
969 run_dlg (whelp);
970 interactive_display_finish ();
971 destroy_dlg (whelp);