2 Hypertext file browser.
4 Copyright (C) 1994, 1995, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
6 The Free Software Foundation, Inc.
8 This file is part of the Midnight Commander.
10 The Midnight Commander is free software: you can redistribute it
11 and/or modify it under the terms of the GNU General Public License as
12 published by the Free Software Foundation, either version 3 of the License,
13 or (at your option) any later version.
15 The Midnight Commander is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program. If not, see <http://www.gnu.org/licenses/>.
26 * \brief Source: hypertext file browser
28 * Implements the hypertext file viewer.
29 * The hypertext file is a file that may have one or more nodes. Each
30 * node ends with a ^D character and starts with a bracket, then the
31 * name of the node and then a closing bracket. Right after the closing
32 * bracket a newline is placed. This newline is not to be displayed by
33 * the help viewer and must be skipped - its sole purpose is to faciliate
34 * the work of the people managing the help file template (xnc.hlp) .
36 * Links in the hypertext file are specified like this: the text that
37 * will be highlighted should have a leading ^A, then it comes the
38 * text, then a ^B indicating that highlighting is done, then the name
39 * of the node you want to link to and then a ^C.
41 * The file must contain a ^D at the beginning and at the end of the
42 * file or the program will not be able to detect the end of file.
44 * Lazyness/widgeting attack: This file does use the dialog manager
45 * and uses mainly the dialog to achieve the help work. there is only
46 * one specialized widget and it's only used to forward the mouse messages
47 * to the appropiate routine.
55 #include <sys/types.h>
58 #include "lib/global.h"
60 #include "lib/tty/tty.h"
61 #include "lib/tty/mouse.h"
63 #include "lib/strutil.h"
64 #include "lib/fileloc.h"
66 #include "lib/widget.h"
67 #include "lib/event-types.h"
69 #include "keybind-defaults.h"
70 #include "keybind-defaults.h"
74 /*** global variables ****************************************************************************/
76 /*** file scope macro definitions ****************************************************************/
78 #define MAXLINKNAME 80
79 #define HISTORY_SIZE 20
80 #define HELP_WINDOW_WIDTH min(80, COLS - 16)
82 #define STRING_LINK_START "\01"
83 #define STRING_LINK_POINTER "\02"
84 #define STRING_LINK_END "\03"
85 #define STRING_NODE_END "\04"
87 /*** file scope type declarations ****************************************************************/
89 /* Link areas for the mouse */
90 typedef struct Link_Area
93 const char *link_name
;
96 /*** file scope variables ************************************************************************/
98 static char *fdata
= NULL
; /* Pointer to the loaded data file */
99 static int help_lines
; /* Lines in help viewer */
100 static int history_ptr
; /* For the history queue */
101 static const char *main_node
; /* The main node */
102 static const char *last_shown
= NULL
; /* Last byte shown in a screen */
103 static gboolean end_of_node
= FALSE
; /* Flag: the last character of the node shown? */
104 static const char *currentpoint
;
105 static const char *selected_item
;
107 /* The widget variables */
108 static Dlg_head
*whelp
;
112 const char *page
; /* Pointer to the selected page */
113 const char *link
; /* Pointer to the selected link */
114 } history
[HISTORY_SIZE
];
116 static GSList
*link_area
= NULL
;
117 static gboolean inside_link_area
= FALSE
;
119 static cb_ret_t
help_callback (Dlg_head
* h
, Widget
* sender
, dlg_msg_t msg
, int parm
, void *data
);
121 /*** file scope functions ************************************************************************/
122 /* --------------------------------------------------------------------------------------------- */
124 /** returns the position where text was found in the start buffer
128 search_string (const char *start
, const char *text
)
130 const char *result
= NULL
;
131 char *local_text
= g_strdup (text
);
132 char *d
= local_text
;
133 const char *e
= start
;
135 /* fmt sometimes replaces a space with a newline in the help file */
136 /* Replace the newlines in the link name with spaces to correct the situation */
145 for (d
= local_text
; *e
; e
++)
162 /* --------------------------------------------------------------------------------------------- */
163 /** Searches text in the buffer pointed by start. Search ends
164 * if the CHAR_NODE_END is found in the text.
165 * @returns 0 on failure
169 search_string_node (const char *start
, const char *text
)
171 const char *d
= text
;
172 const char *e
= start
;
175 for (; *e
&& *e
!= CHAR_NODE_END
; e
++)
188 /* --------------------------------------------------------------------------------------------- */
189 /** Searches the_char in the buffer pointer by start and searches
190 * it can search forward (direction = 1) or backward (direction = -1)
194 search_char_node (const char *start
, char the_char
, int direction
)
198 for (e
= start
; (*e
!= '\0') && (*e
!= CHAR_NODE_END
); e
+= direction
)
205 /* --------------------------------------------------------------------------------------------- */
206 /** Returns the new current pointer when moved lines lines */
209 move_forward2 (const char *c
, int lines
)
215 for (line
= 0, p
= currentpoint
; (*p
!= '\0') && (*p
!= CHAR_NODE_END
); str_cnext_char (&p
))
218 return currentpoint
= p
;
223 return currentpoint
= c
;
226 /* --------------------------------------------------------------------------------------------- */
229 move_backward2 (const char *c
, int lines
)
235 for (line
= 0, p
= currentpoint
; (*p
!= '\0') && ((int) (p
- fdata
) >= 0); str_cprev_char (&p
))
237 if (*p
== CHAR_NODE_END
)
239 /* We reached the beginning of the node */
240 /* Skip the node headers */
243 return currentpoint
= p
+ 2; /* Skip the newline following the start of the node */
246 if (*(p
- 1) == '\n')
249 return currentpoint
= p
;
251 return currentpoint
= c
;
254 /* --------------------------------------------------------------------------------------------- */
260 currentpoint
= move_forward2 (currentpoint
, i
);
263 /* --------------------------------------------------------------------------------------------- */
266 move_backward (int i
)
268 currentpoint
= move_backward2 (currentpoint
, ++i
);
271 /* --------------------------------------------------------------------------------------------- */
276 while (((int) (currentpoint
> fdata
) > 0) && (*currentpoint
!= CHAR_NODE_END
))
279 while (*currentpoint
!= ']')
281 currentpoint
= currentpoint
+ 2; /* Skip the newline following the start of the node */
282 selected_item
= NULL
;
285 /* --------------------------------------------------------------------------------------------- */
288 move_to_bottom (void)
290 while ((*currentpoint
!= '\0') && (*currentpoint
!= CHAR_NODE_END
))
296 /* --------------------------------------------------------------------------------------------- */
299 help_follow_link (const char *start
, const char *lc_selected_item
)
301 char link_name
[MAXLINKNAME
];
305 if (lc_selected_item
== NULL
)
308 for (p
= lc_selected_item
; *p
&& *p
!= CHAR_NODE_END
&& *p
!= CHAR_LINK_POINTER
; p
++)
310 if (*p
== CHAR_LINK_POINTER
)
313 for (i
= 1; *p
!= CHAR_LINK_END
&& *p
&& *p
!= CHAR_NODE_END
&& i
< MAXLINKNAME
- 3;)
314 link_name
[i
++] = *++p
;
315 link_name
[i
- 1] = ']';
317 p
= search_string (fdata
, link_name
);
320 p
+= 1; /* Skip the newline following the start of the node */
325 /* Create a replacement page with the error message */
326 return _("Help file format error\n");
329 /* --------------------------------------------------------------------------------------------- */
332 select_next_link (const char *current_link
)
336 if (current_link
== NULL
)
339 p
= search_string_node (current_link
, STRING_LINK_END
);
342 p
= search_string_node (p
, STRING_LINK_START
);
348 /* --------------------------------------------------------------------------------------------- */
351 select_prev_link (const char *current_link
)
353 return current_link
== NULL
? NULL
: search_char_node (current_link
- 1, CHAR_LINK_START
, -1);
356 /* --------------------------------------------------------------------------------------------- */
359 start_link_area (int x
, int y
, const char *link_name
)
363 if (inside_link_area
)
364 message (D_NORMAL
, _("Warning"), _("Internal bug: Double start of link area"));
366 /* Allocate memory for a new link area */
367 la
= g_new (Link_Area
, 1);
368 /* Save the beginning coordinates of the link area */
371 /* Save the name of the destination anchor */
372 la
->link_name
= link_name
;
373 link_area
= g_slist_prepend (link_area
, la
);
375 inside_link_area
= TRUE
;
378 /* --------------------------------------------------------------------------------------------- */
381 end_link_area (int x
, int y
)
383 if (inside_link_area
)
385 Link_Area
*la
= (Link_Area
*) link_area
->data
;
386 /* Save the end coordinates of the link area */
389 inside_link_area
= FALSE
;
393 /* --------------------------------------------------------------------------------------------- */
396 clear_link_areas (void)
398 g_slist_foreach (link_area
, (GFunc
) g_free
, NULL
);
399 g_slist_free (link_area
);
401 inside_link_area
= FALSE
;
404 /* --------------------------------------------------------------------------------------------- */
407 help_print_word (Dlg_head
* h
, GString
* word
, int *col
, int *line
, gboolean add_space
)
409 if (*line
>= help_lines
)
410 g_string_set_size (word
, 0);
415 w
= str_term_width1 (word
->str
);
416 if (*col
+ w
>= HELP_WINDOW_WIDTH
)
422 if (*line
>= help_lines
)
423 g_string_set_size (word
, 0);
426 dlg_move (h
, *line
+ 2, *col
+ 2);
427 tty_print_string (word
->str
);
428 g_string_set_size (word
, 0);
435 if (*col
< HELP_WINDOW_WIDTH
- 1)
437 tty_print_char (' ');
448 /* --------------------------------------------------------------------------------------------- */
451 help_show (Dlg_head
* h
, const char *paint_start
)
455 gboolean painting
= TRUE
;
456 gboolean acs
; /* Flag: Alternate character set active? */
457 gboolean repeat_paint
;
458 int active_col
, active_line
; /* Active link position */
459 char buff
[MB_LEN_MAX
+ 1];
462 word
= g_string_sized_new (32);
464 tty_setcolor (HELP_NORMAL_COLOR
);
467 line
= col
= active_col
= active_line
= 0;
468 repeat_paint
= FALSE
;
472 if ((int) (selected_item
- paint_start
) < 0)
473 selected_item
= NULL
;
477 while ((n
[0] != '\0') && (n
[0] != CHAR_NODE_END
) && (line
< help_lines
))
480 n
= str_cget_next_char (p
);
481 memcpy (buff
, p
, n
- p
);
484 c
= (unsigned char) buff
[0];
487 case CHAR_LINK_START
:
488 if (selected_item
== NULL
)
490 if (p
!= selected_item
)
491 tty_setcolor (HELP_LINK_COLOR
);
494 tty_setcolor (HELP_SLINK_COLOR
);
496 /* Store the coordinates of the link */
497 active_col
= col
+ 2;
498 active_line
= line
+ 2;
500 start_link_area (col
, line
, p
);
502 case CHAR_LINK_POINTER
:
504 end_link_area (col
- 1, line
);
508 help_print_word (h
, word
, &col
, &line
, FALSE
);
509 tty_setcolor (HELP_NORMAL_COLOR
);
518 dlg_move (h
, line
+ 2, col
+ 2);
519 tty_print_string (VERSION
);
520 col
+= str_term_width1 (VERSION
);
523 tty_setcolor (HELP_BOLD_COLOR
);
525 case CHAR_FONT_ITALIC
:
526 tty_setcolor (HELP_ITALIC_COLOR
);
528 case CHAR_FONT_NORMAL
:
529 help_print_word (h
, word
, &col
, &line
, FALSE
);
530 tty_setcolor (HELP_NORMAL_COLOR
);
534 help_print_word (h
, word
, &col
, &line
, FALSE
);
539 col
= (col
/ 8 + 1) * 8;
540 if (col
>= HELP_WINDOW_WIDTH
)
549 help_print_word (h
, word
, &col
, &line
, TRUE
);
552 if (painting
&& (line
< help_lines
))
555 /* accumulate symbols in a word */
556 g_string_append (word
, buff
);
557 else if (col
< HELP_WINDOW_WIDTH
)
559 dlg_move (h
, line
+ 2, col
+ 2);
561 if ((c
== ' ') || (c
== '.'))
565 tty_print_char (acs_map
[c
]);
567 SLsmg_draw_object (h
->y
+ line
+ 2, h
->x
+ col
+ 2, c
);
575 /* print last word */
576 if (n
[0] == CHAR_NODE_END
)
577 help_print_word (h
, word
, &col
, &line
, FALSE
);
580 end_of_node
= line
< help_lines
;
581 tty_setcolor (HELP_NORMAL_COLOR
);
582 if ((int) (selected_item
- last_shown
) >= 0)
584 if ((link_area
== NULL
) || (link_area
->data
== NULL
))
585 selected_item
= NULL
;
588 selected_item
= ((Link_Area
*) link_area
->data
)->link_name
;
593 while (repeat_paint
);
595 g_string_free (word
, TRUE
);
597 /* Position the cursor over a nice link */
599 dlg_move (h
, active_line
, active_col
);
602 /* --------------------------------------------------------------------------------------------- */
605 help_event (Gpm_Event
* event
, void *vp
)
608 GSList
*current_area
;
610 if ((event
->type
& GPM_UP
) == 0)
613 /* The event is relative to the dialog window, adjust it: */
617 if (event
->buttons
& GPM_B_RIGHT
)
619 currentpoint
= history
[history_ptr
].page
;
620 selected_item
= history
[history_ptr
].link
;
623 history_ptr
= HISTORY_SIZE
- 1;
625 help_callback (w
->owner
, NULL
, DLG_DRAW
, 0, NULL
);
629 /* Test whether the mouse click is inside one of the link areas */
630 for (current_area
= link_area
; current_area
!= NULL
; current_area
= g_slist_next (current_area
))
632 Link_Area
*la
= (Link_Area
*) current_area
->data
;
633 /* Test one line link area */
634 if (event
->y
== la
->y1
&& event
->x
>= la
->x1
&& event
->y
== la
->y2
&& event
->x
<= la
->x2
)
636 /* Test two line link area */
637 if (la
->y1
+ 1 == la
->y2
)
640 if (event
->y
== la
->y1
&& event
->x
>= la
->x1
)
642 /* The second line */
643 if (event
->y
== la
->y2
&& event
->x
<= la
->x2
)
646 /* Mouse will not work with link areas of more than two lines */
649 /* Test whether a link area was found */
650 if (current_area
!= NULL
)
652 Link_Area
*la
= (Link_Area
*) current_area
->data
;
654 /* The click was inside a link area -> follow the link */
655 history_ptr
= (history_ptr
+ 1) % HISTORY_SIZE
;
656 history
[history_ptr
].page
= currentpoint
;
657 history
[history_ptr
].link
= la
->link_name
;
658 currentpoint
= help_follow_link (currentpoint
, la
->link_name
);
659 selected_item
= NULL
;
661 else if (event
->y
< 0)
662 move_backward (help_lines
- 1);
663 else if (event
->y
>= help_lines
)
664 move_forward (help_lines
- 1);
665 else if (event
->y
< help_lines
/ 2)
670 /* Show the new node */
671 help_callback (w
->owner
, NULL
, DLG_DRAW
, 0, NULL
);
676 /* --------------------------------------------------------------------------------------------- */
680 help_help (Dlg_head
* h
)
684 history_ptr
= (history_ptr
+ 1) % HISTORY_SIZE
;
685 history
[history_ptr
].page
= currentpoint
;
686 history
[history_ptr
].link
= selected_item
;
688 p
= search_string (fdata
, "[How to use help]");
691 currentpoint
= p
+ 1; /* Skip the newline following the start of the node */
692 selected_item
= NULL
;
693 help_callback (h
, NULL
, DLG_DRAW
, 0, NULL
);
697 /* --------------------------------------------------------------------------------------------- */
700 help_index (Dlg_head
* h
)
702 const char *new_item
;
704 new_item
= search_string (fdata
, "[Contents]");
706 if (new_item
== NULL
)
707 message (D_ERROR
, MSG_ERROR
, _("Cannot find node %s in help file"), "[Contents]");
710 history_ptr
= (history_ptr
+ 1) % HISTORY_SIZE
;
711 history
[history_ptr
].page
= currentpoint
;
712 history
[history_ptr
].link
= selected_item
;
714 currentpoint
= new_item
+ 1; /* Skip the newline following the start of the node */
715 selected_item
= NULL
;
716 help_callback (h
, NULL
, DLG_DRAW
, 0, NULL
);
720 /* --------------------------------------------------------------------------------------------- */
723 help_back (Dlg_head
* h
)
725 currentpoint
= history
[history_ptr
].page
;
726 selected_item
= history
[history_ptr
].link
;
729 history_ptr
= HISTORY_SIZE
- 1;
731 help_callback (h
, NULL
, DLG_DRAW
, 0, NULL
); /* FIXME: unneeded? */
734 /* --------------------------------------------------------------------------------------------- */
737 help_next_link (gboolean move_down
)
739 const char *new_item
;
741 new_item
= select_next_link (selected_item
);
742 if (new_item
!= NULL
)
744 selected_item
= new_item
;
745 if ((int) (selected_item
- last_shown
) >= 0)
750 selected_item
= NULL
;
756 selected_item
= NULL
;
759 /* --------------------------------------------------------------------------------------------- */
762 help_prev_link (gboolean move_up
)
764 const char *new_item
;
766 new_item
= select_prev_link (selected_item
);
767 selected_item
= new_item
;
768 if ((selected_item
== NULL
) || (selected_item
< currentpoint
))
772 else if ((link_area
!= NULL
) && (link_area
->data
!= NULL
))
773 selected_item
= ((Link_Area
*) link_area
->data
)->link_name
;
775 selected_item
= NULL
;
779 /* --------------------------------------------------------------------------------------------- */
782 help_next_node (void)
784 const char *new_item
;
786 new_item
= currentpoint
;
787 while ((*new_item
!= '\0') && (*new_item
!= CHAR_NODE_END
))
790 if (*++new_item
== '[')
791 while (*++new_item
!= '\0')
792 if ((*new_item
== ']') && (*++new_item
!= '\0') && (*++new_item
!= '\0'))
794 currentpoint
= new_item
;
795 selected_item
= NULL
;
800 /* --------------------------------------------------------------------------------------------- */
803 help_prev_node (void)
805 const char *new_item
;
807 new_item
= currentpoint
;
808 while (((int) (new_item
- fdata
) > 1) && (*new_item
!= CHAR_NODE_END
))
811 while (((int) (new_item
- fdata
) > 0) && (*new_item
!= CHAR_NODE_END
))
813 while (*new_item
!= ']')
815 currentpoint
= new_item
+ 2;
816 selected_item
= NULL
;
819 /* --------------------------------------------------------------------------------------------- */
822 help_select_link (void)
825 if (selected_item
== NULL
)
827 #ifdef WE_WANT_TO_GO_BACKWARD_ON_KEY_RIGHT
828 /* Is there any reason why the right key would take us
829 * backward if there are no links selected?, I agree
830 * with Torben than doing nothing in this case is better
832 /* If there are no links, go backward in history */
835 history_ptr
= HISTORY_SIZE
- 1;
837 currentpoint
= history
[history_ptr
].page
;
838 selected_item
= history
[history_ptr
].link
;
843 history_ptr
= (history_ptr
+ 1) % HISTORY_SIZE
;
844 history
[history_ptr
].page
= currentpoint
;
845 history
[history_ptr
].link
= selected_item
;
846 currentpoint
= help_follow_link (currentpoint
, selected_item
);
849 selected_item
= NULL
;
852 /* --------------------------------------------------------------------------------------------- */
855 help_execute_cmd (unsigned long command
)
857 cb_ret_t ret
= MSG_HANDLED
;
871 help_prev_link (TRUE
);
874 help_next_link (TRUE
);
877 move_forward (help_lines
- 1);
880 move_backward (help_lines
- 1);
882 case CK_HalfPageDown
:
883 move_forward (help_lines
/ 2);
886 move_backward (help_lines
/ 2);
898 help_next_link (FALSE
);
901 help_prev_link (FALSE
);
913 /* don't close help due to SIGINT */
916 ret
= MSG_NOT_HANDLED
;
922 /* --------------------------------------------------------------------------------------------- */
925 help_handle_key (Dlg_head
* h
, int c
)
927 unsigned long command
;
929 command
= keybind_lookup_keymap_command (help_map
, c
);
930 if ((command
== CK_IgnoreKey
) || (help_execute_cmd (command
) == MSG_NOT_HANDLED
))
931 return MSG_NOT_HANDLED
;
933 help_callback (h
, NULL
, DLG_DRAW
, 0, NULL
);
937 /* --------------------------------------------------------------------------------------------- */
940 help_callback (Dlg_head
* h
, Widget
* sender
, dlg_msg_t msg
, int parm
, void *data
)
947 help_lines
= min (LINES
- 4, max (2 * LINES
/ 3, 18));
948 dlg_set_size (h
, help_lines
+ 4, HELP_WINDOW_WIDTH
+ 4);
949 bb
= find_buttonbar (h
);
950 widget_set_size (&bb
->widget
, LINES
- 1, 0, 1, COLS
);
954 common_dialog_repaint (h
);
955 help_show (h
, currentpoint
);
959 return help_handle_key (h
, parm
);
964 return help_execute_cmd (parm
);
965 /* message from buttonbar */
966 if (sender
== (Widget
*) find_buttonbar (h
))
969 return send_message ((Widget
*) data
, WIDGET_COMMAND
, parm
);
970 return help_execute_cmd (parm
);
972 return MSG_NOT_HANDLED
;
975 return default_dlg_callback (h
, sender
, msg
, parm
, data
);
979 /* --------------------------------------------------------------------------------------------- */
982 interactive_display_finish (void)
987 /* --------------------------------------------------------------------------------------------- */
988 /** translate help file into terminal encoding */
991 translate_file (char *filedata
)
994 GString
*translated_data
;
996 /* initial allocation for largest whole help file */
997 translated_data
= g_string_sized_new (32 * 1024);
999 conv
= str_crt_conv_from ("UTF-8");
1001 if (conv
== INVALID_CONV
)
1002 g_string_free (translated_data
, TRUE
);
1007 if (str_convert (conv
, filedata
, translated_data
) != ESTR_FAILURE
)
1008 fdata
= g_string_free (translated_data
, FALSE
);
1012 g_string_free (translated_data
, TRUE
);
1014 str_close_conv (conv
);
1018 /* --------------------------------------------------------------------------------------------- */
1021 md_callback (Widget
* w
, widget_msg_t msg
, int parm
)
1025 case WIDGET_RESIZED
:
1026 w
->lines
= help_lines
;
1030 return default_proc (msg
, parm
);
1034 /* --------------------------------------------------------------------------------------------- */
1037 mousedispatch_new (int y
, int x
, int yl
, int xl
)
1039 Widget
*w
= g_new (Widget
, 1);
1040 init_widget (w
, y
, x
, yl
, xl
, md_callback
, help_event
);
1044 /* --------------------------------------------------------------------------------------------- */
1045 /*** public functions ****************************************************************************/
1046 /* --------------------------------------------------------------------------------------------- */
1048 /* event callback */
1050 help_interactive_display (const gchar
* event_group_name
, const gchar
* event_name
,
1051 gpointer init_data
, gpointer data
)
1053 const dlg_colors_t help_colors
= {
1054 HELP_NORMAL_COLOR
, /* common text color */
1055 0, /* unused in help */
1056 HELP_BOLD_COLOR
, /* bold text color */
1057 0, /* unused in help */
1058 HELP_TITLE_COLOR
/* title color */
1061 WButtonBar
*help_bar
;
1063 char *hlpfile
= NULL
;
1065 ev_help_t
*event_data
= (ev_help_t
*) data
;
1067 (void) event_group_name
;
1071 if (event_data
->filename
!= NULL
)
1072 g_file_get_contents (event_data
->filename
, &filedata
, NULL
, NULL
);
1074 filedata
= load_mc_home_file (mc_global
.share_data_dir
, MC_HELP
, &hlpfile
);
1076 if (filedata
== NULL
)
1077 message (D_ERROR
, MSG_ERROR
, _("Cannot open file %s\n%s"),
1078 event_data
->filename
? event_data
->filename
: hlpfile
, unix_error_string (errno
));
1082 if (filedata
== NULL
)
1085 translate_file (filedata
);
1092 if ((event_data
->node
== NULL
) || (*event_data
->node
== '\0'))
1093 event_data
->node
= "[main]";
1095 main_node
= search_string (fdata
, event_data
->node
);
1097 if (main_node
== NULL
)
1099 message (D_ERROR
, MSG_ERROR
, _("Cannot find node %s in help file"), event_data
->node
);
1101 /* Fallback to [main], return if it also cannot be found */
1102 main_node
= search_string (fdata
, "[main]");
1103 if (main_node
== NULL
)
1105 interactive_display_finish ();
1110 help_lines
= min (LINES
- 4, max (2 * LINES
/ 3, 18));
1113 create_dlg (TRUE
, 0, 0, help_lines
+ 4, HELP_WINDOW_WIDTH
+ 4,
1114 help_colors
, help_callback
, "[Help]", _("Help"),
1115 DLG_TRYUP
| DLG_CENTER
| DLG_WANT_TAB
);
1117 selected_item
= search_string_node (main_node
, STRING_LINK_START
) - 1;
1118 currentpoint
= main_node
+ 1; /* Skip the newline following the start of the node */
1120 for (history_ptr
= HISTORY_SIZE
; history_ptr
;)
1123 history
[history_ptr
].page
= currentpoint
;
1124 history
[history_ptr
].link
= selected_item
;
1127 help_bar
= buttonbar_new (TRUE
);
1128 help_bar
->widget
.y
-= whelp
->y
;
1129 help_bar
->widget
.x
-= whelp
->x
;
1131 md
= mousedispatch_new (1, 1, help_lines
, HELP_WINDOW_WIDTH
- 2);
1133 add_widget (whelp
, md
);
1134 add_widget (whelp
, help_bar
);
1136 buttonbar_set_label (help_bar
, 1, Q_ ("ButtonBar|Help"), help_map
, NULL
);
1137 buttonbar_set_label (help_bar
, 2, Q_ ("ButtonBar|Index"), help_map
, NULL
);
1138 buttonbar_set_label (help_bar
, 3, Q_ ("ButtonBar|Prev"), help_map
, NULL
);
1139 buttonbar_set_label (help_bar
, 4, "", help_map
, NULL
);
1140 buttonbar_set_label (help_bar
, 5, "", help_map
, NULL
);
1141 buttonbar_set_label (help_bar
, 6, "", help_map
, NULL
);
1142 buttonbar_set_label (help_bar
, 7, "", help_map
, NULL
);
1143 buttonbar_set_label (help_bar
, 8, "", help_map
, NULL
);
1144 buttonbar_set_label (help_bar
, 9, "", help_map
, NULL
);
1145 buttonbar_set_label (help_bar
, 10, Q_ ("ButtonBar|Quit"), help_map
, NULL
);
1148 interactive_display_finish ();
1149 destroy_dlg (whelp
);
1153 /* --------------------------------------------------------------------------------------------- */