2 Hypertext file browser.
4 Copyright (C) 1994-2024
5 Free Software Foundation, Inc.
7 This file is part of the Midnight Commander.
9 The Midnight Commander is free software: you can redistribute it
10 and/or modify it under the terms of the GNU General Public License as
11 published by the Free Software Foundation, either version 3 of the License,
12 or (at your option) any later version.
14 The Midnight Commander is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program. If not, see <http://www.gnu.org/licenses/>.
25 * \brief Source: hypertext file browser
27 * Implements the hypertext file viewer.
28 * The hypertext file is a file that may have one or more nodes. Each
29 * node ends with a ^D character and starts with a bracket, then the
30 * name of the node and then a closing bracket. Right after the closing
31 * bracket a newline is placed. This newline is not to be displayed by
32 * the help viewer and must be skipped - its sole purpose is to facilitate
33 * the work of the people managing the help file template (xnc.hlp) .
35 * Links in the hypertext file are specified like this: the text that
36 * will be highlighted should have a leading ^A, then it comes the
37 * text, then a ^B indicating that highlighting is done, then the name
38 * of the node you want to link to and then a ^C.
40 * The file must contain a ^D at the beginning and at the end of the
41 * file or the program will not be able to detect the end of file.
43 * Laziness/widgeting attack: This file does use the dialog manager
44 * and uses mainly the dialog to achieve the help work. there is only
45 * one specialized widget and it's only used to forward the mouse messages
46 * to the appropriate routine.
53 #include <limits.h> /* MB_LEN_MAX */
55 #include <sys/types.h>
58 #include "lib/global.h"
60 #include "lib/tty/tty.h"
62 #include "lib/strutil.h"
63 #include "lib/fileloc.h"
65 #include "lib/widget.h"
66 #include "lib/event-types.h"
71 /*** global variables ****************************************************************************/
73 /*** file scope macro definitions ****************************************************************/
75 #define MAXLINKNAME 80
76 #define HISTORY_SIZE 20
77 #define HELP_WINDOW_WIDTH MIN(80, COLS - 16)
79 #define STRING_LINK_START "\01"
80 #define STRING_LINK_POINTER "\02"
81 #define STRING_LINK_END "\03"
82 #define STRING_NODE_END "\04"
84 /*** file scope type declarations ****************************************************************/
86 /* Link areas for the mouse */
87 typedef struct Link_Area
90 const char *link_name
;
93 /*** forward declarations (file scope functions) *************************************************/
95 /*** file scope variables ************************************************************************/
97 static char *fdata
= NULL
; /* Pointer to the loaded data file */
98 static int help_lines
; /* Lines in help viewer */
99 static int history_ptr
; /* For the history queue */
100 static const char *main_node
; /* The main node */
101 static const char *last_shown
= NULL
; /* Last byte shown in a screen */
102 static gboolean end_of_node
= FALSE
; /* Flag: the last character of the node shown? */
103 static const char *currentpoint
;
104 static const char *selected_item
;
106 /* The widget variables */
107 static WDialog
*whelp
;
111 const char *page
; /* Pointer to the selected page */
112 const char *link
; /* Pointer to the selected link */
113 } history
[HISTORY_SIZE
];
115 static GSList
*link_area
= NULL
;
116 static gboolean inside_link_area
= FALSE
;
118 /* --------------------------------------------------------------------------------------------- */
119 /*** file scope functions ************************************************************************/
120 /* --------------------------------------------------------------------------------------------- */
122 /** returns the position where text was found in the start buffer
126 search_string (const char *start
, const char *text
)
128 const char *result
= NULL
;
131 const char *e
= start
;
133 local_text
= g_strdup (text
);
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 */
137 for (d
= local_text
; *d
!= '\0'; str_next_char (&d
))
142 for (d
= local_text
; *e
!= '\0'; e
++)
159 /* --------------------------------------------------------------------------------------------- */
160 /** Searches text in the buffer pointed by start. Search ends
161 * if the CHAR_NODE_END is found in the text.
162 * @return NULL on failure
166 search_string_node (const char *start
, const char *text
)
170 const char *d
= text
;
173 for (e
= start
; *e
!= '\0' && *e
!= CHAR_NODE_END
; e
++)
187 /* --------------------------------------------------------------------------------------------- */
188 /** Searches the_char in the buffer pointer by start and searches
189 * it can search forward (direction = 1) or backward (direction = -1)
193 search_char_node (const char *start
, char the_char
, int direction
)
197 for (e
= start
; (*e
!= '\0') && (*e
!= CHAR_NODE_END
); e
+= direction
)
204 /* --------------------------------------------------------------------------------------------- */
205 /** Returns the new current pointer when moved lines lines */
208 move_forward2 (const char *c
, int lines
)
214 for (line
= 0, p
= currentpoint
; (*p
!= '\0') && (*p
!= CHAR_NODE_END
); str_cnext_char (&p
))
217 return currentpoint
= p
;
222 return currentpoint
= c
;
225 /* --------------------------------------------------------------------------------------------- */
228 move_backward2 (const char *c
, int lines
)
234 for (line
= 0, p
= currentpoint
; (*p
!= '\0') && ((int) (p
- fdata
) >= 0); str_cprev_char (&p
))
236 if (*p
== CHAR_NODE_END
)
238 /* We reached the beginning of the node */
239 /* Skip the node headers */
242 return currentpoint
= p
+ 2; /* Skip the newline following the start of the node */
245 if (*(p
- 1) == '\n')
248 return currentpoint
= p
;
250 return currentpoint
= c
;
253 /* --------------------------------------------------------------------------------------------- */
259 currentpoint
= move_forward2 (currentpoint
, i
);
262 /* --------------------------------------------------------------------------------------------- */
265 move_backward (int i
)
267 currentpoint
= move_backward2 (currentpoint
, ++i
);
270 /* --------------------------------------------------------------------------------------------- */
275 while (((int) (currentpoint
- fdata
) > 0) && (*currentpoint
!= CHAR_NODE_END
))
278 while (*currentpoint
!= ']')
280 currentpoint
= currentpoint
+ 2; /* Skip the newline following the start of the node */
281 selected_item
= NULL
;
284 /* --------------------------------------------------------------------------------------------- */
287 move_to_bottom (void)
289 while ((*currentpoint
!= '\0') && (*currentpoint
!= CHAR_NODE_END
))
295 /* --------------------------------------------------------------------------------------------- */
298 help_follow_link (const char *start
, const char *lc_selected_item
)
302 if (lc_selected_item
== NULL
)
305 for (p
= lc_selected_item
; *p
!= '\0' && *p
!= CHAR_NODE_END
&& *p
!= CHAR_LINK_POINTER
; p
++)
307 if (*p
== CHAR_LINK_POINTER
)
310 char link_name
[MAXLINKNAME
];
314 *p
!= CHAR_LINK_END
&& *p
!= '\0' && *p
!= CHAR_NODE_END
&& i
< MAXLINKNAME
- 3;)
315 link_name
[i
++] = *++p
;
316 link_name
[i
- 1] = ']';
318 p
= search_string (fdata
, link_name
);
321 p
+= 1; /* Skip the newline following the start of the node */
326 /* Create a replacement page with the error message */
327 return _("Help file format error\n");
330 /* --------------------------------------------------------------------------------------------- */
333 select_next_link (const char *current_link
)
337 if (current_link
== NULL
)
340 p
= search_string_node (current_link
, STRING_LINK_END
);
343 p
= search_string_node (p
, STRING_LINK_START
);
349 /* --------------------------------------------------------------------------------------------- */
352 select_prev_link (const char *current_link
)
354 return current_link
== NULL
? NULL
: search_char_node (current_link
- 1, CHAR_LINK_START
, -1);
357 /* --------------------------------------------------------------------------------------------- */
360 start_link_area (int x
, int y
, const char *link_name
)
364 if (inside_link_area
)
365 message (D_NORMAL
, _("Warning"), "%s", _("Internal bug: Double start of link area"));
367 /* Allocate memory for a new link area */
368 la
= g_new (Link_Area
, 1);
369 /* Save the beginning coordinates of the link area */
372 /* Save the name of the destination anchor */
373 la
->link_name
= link_name
;
374 link_area
= g_slist_prepend (link_area
, la
);
376 inside_link_area
= TRUE
;
379 /* --------------------------------------------------------------------------------------------- */
382 end_link_area (int x
, int y
)
384 if (inside_link_area
)
386 Link_Area
*la
= (Link_Area
*) link_area
->data
;
387 /* Save the end coordinates of the link area */
390 inside_link_area
= FALSE
;
394 /* --------------------------------------------------------------------------------------------- */
397 clear_link_areas (void)
399 g_clear_slist (&link_area
, g_free
);
400 inside_link_area
= FALSE
;
403 /* --------------------------------------------------------------------------------------------- */
406 help_print_word (WDialog
*h
, GString
*word
, int *col
, int *line
, gboolean add_space
)
408 if (*line
>= help_lines
)
409 g_string_set_size (word
, 0);
414 w
= str_term_width1 (word
->str
);
415 if (*col
+ w
>= HELP_WINDOW_WIDTH
)
421 if (*line
>= help_lines
)
422 g_string_set_size (word
, 0);
425 widget_gotoyx (h
, *line
+ 2, *col
+ 2);
426 tty_print_string (word
->str
);
427 g_string_set_size (word
, 0);
434 if (*col
< HELP_WINDOW_WIDTH
- 1)
436 tty_print_char (' ');
447 /* --------------------------------------------------------------------------------------------- */
450 help_show (WDialog
*h
, const char *paint_start
)
452 gboolean painting
= TRUE
;
453 gboolean repeat_paint
;
454 int active_col
, active_line
; /* Active link position */
455 char buff
[MB_LEN_MAX
+ 1];
458 word
= g_string_sized_new (32);
460 tty_setcolor (HELP_NORMAL_COLOR
);
465 gboolean acs
= FALSE
; /* Flag: Is alternate character set active? */
471 repeat_paint
= FALSE
;
474 if ((int) (selected_item
- paint_start
) < 0)
475 selected_item
= NULL
;
479 while ((n
[0] != '\0') && (n
[0] != CHAR_NODE_END
) && (line
< help_lines
))
484 n
= str_cget_next_char (p
);
485 memcpy (buff
, p
, n
- p
);
488 c
= (unsigned char) buff
[0];
491 case CHAR_LINK_START
:
492 if (selected_item
== NULL
)
494 if (p
!= selected_item
)
495 tty_setcolor (HELP_LINK_COLOR
);
498 tty_setcolor (HELP_SLINK_COLOR
);
500 /* Store the coordinates of the link */
501 active_col
= col
+ 2;
502 active_line
= line
+ 2;
504 start_link_area (col
, line
, p
);
506 case CHAR_LINK_POINTER
:
511 help_print_word (h
, word
, &col
, &line
, FALSE
);
512 end_link_area (col
- 1, line
);
513 tty_setcolor (HELP_NORMAL_COLOR
);
522 widget_gotoyx (h
, line
+ 2, col
+ 2);
523 tty_print_string (mc_global
.mc_version
);
524 col
+= str_term_width1 (mc_global
.mc_version
);
527 tty_setcolor (HELP_BOLD_COLOR
);
529 case CHAR_FONT_ITALIC
:
530 tty_setcolor (HELP_ITALIC_COLOR
);
532 case CHAR_FONT_NORMAL
:
533 help_print_word (h
, word
, &col
, &line
, FALSE
);
534 tty_setcolor (HELP_NORMAL_COLOR
);
538 help_print_word (h
, word
, &col
, &line
, FALSE
);
547 help_print_word (h
, word
, &col
, &line
, c
== ' ');
550 col
= (col
/ 8 + 1) * 8;
551 if (col
>= HELP_WINDOW_WIDTH
)
560 if (painting
&& (line
< help_lines
))
563 /* accumulate symbols in a word */
564 g_string_append (word
, buff
);
565 else if (col
< HELP_WINDOW_WIDTH
)
567 widget_gotoyx (h
, line
+ 2, col
+ 2);
569 if ((c
== ' ') || (c
== '.'))
573 tty_print_char (acs_map
[c
]);
575 SLsmg_draw_object (WIDGET (h
)->rect
.y
+ line
+ 2,
576 WIDGET (h
)->rect
.x
+ col
+ 2, c
);
584 /* print last word */
585 if (n
[0] == CHAR_NODE_END
)
586 help_print_word (h
, word
, &col
, &line
, FALSE
);
589 end_of_node
= line
< help_lines
;
590 tty_setcolor (HELP_NORMAL_COLOR
);
591 if ((int) (selected_item
- last_shown
) >= 0)
593 if ((link_area
== NULL
) || (link_area
->data
== NULL
))
594 selected_item
= NULL
;
597 selected_item
= ((Link_Area
*) link_area
->data
)->link_name
;
602 while (repeat_paint
);
604 g_string_free (word
, TRUE
);
606 /* Position the cursor over a nice link */
608 widget_gotoyx (h
, active_line
, active_col
);
611 /* --------------------------------------------------------------------------------------------- */
615 help_help (WDialog
*h
)
619 history_ptr
= (history_ptr
+ 1) % HISTORY_SIZE
;
620 history
[history_ptr
].page
= currentpoint
;
621 history
[history_ptr
].link
= selected_item
;
623 p
= search_string (fdata
, "[How to use help]");
626 currentpoint
= p
+ 1; /* Skip the newline following the start of the node */
627 selected_item
= NULL
;
628 widget_draw (WIDGET (h
));
632 /* --------------------------------------------------------------------------------------------- */
635 help_index (WDialog
*h
)
637 const char *new_item
;
639 new_item
= search_string (fdata
, "[Contents]");
641 if (new_item
== NULL
)
642 message (D_ERROR
, MSG_ERROR
, _("Cannot find node %s in help file"), "[Contents]");
645 history_ptr
= (history_ptr
+ 1) % HISTORY_SIZE
;
646 history
[history_ptr
].page
= currentpoint
;
647 history
[history_ptr
].link
= selected_item
;
649 currentpoint
= new_item
+ 1; /* Skip the newline following the start of the node */
650 selected_item
= NULL
;
651 widget_draw (WIDGET (h
));
655 /* --------------------------------------------------------------------------------------------- */
658 help_back (WDialog
*h
)
660 currentpoint
= history
[history_ptr
].page
;
661 selected_item
= history
[history_ptr
].link
;
664 history_ptr
= HISTORY_SIZE
- 1;
666 widget_draw (WIDGET (h
)); /* FIXME: unneeded? */
669 /* --------------------------------------------------------------------------------------------- */
672 help_next_link (gboolean move_down
)
674 const char *new_item
;
676 new_item
= select_next_link (selected_item
);
677 if (new_item
!= NULL
)
679 selected_item
= new_item
;
680 if ((int) (selected_item
- last_shown
) >= 0)
685 selected_item
= NULL
;
691 selected_item
= NULL
;
694 /* --------------------------------------------------------------------------------------------- */
697 help_prev_link (gboolean move_up
)
699 const char *new_item
;
701 new_item
= select_prev_link (selected_item
);
702 selected_item
= new_item
;
703 if ((selected_item
== NULL
) || (selected_item
< currentpoint
))
707 else if ((link_area
!= NULL
) && (link_area
->data
!= NULL
))
708 selected_item
= ((Link_Area
*) link_area
->data
)->link_name
;
710 selected_item
= NULL
;
714 /* --------------------------------------------------------------------------------------------- */
717 help_next_node (void)
719 const char *new_item
;
721 new_item
= currentpoint
;
722 while ((*new_item
!= '\0') && (*new_item
!= CHAR_NODE_END
))
725 if (*++new_item
== '[')
726 while (*++new_item
!= '\0')
727 if ((*new_item
== ']') && (*++new_item
!= '\0') && (*++new_item
!= '\0'))
729 currentpoint
= new_item
;
730 selected_item
= NULL
;
735 /* --------------------------------------------------------------------------------------------- */
738 help_prev_node (void)
740 const char *new_item
;
742 new_item
= currentpoint
;
743 while (((int) (new_item
- fdata
) > 1) && (*new_item
!= CHAR_NODE_END
))
746 while (((int) (new_item
- fdata
) > 0) && (*new_item
!= CHAR_NODE_END
))
748 while (*new_item
!= ']')
750 currentpoint
= new_item
+ 2;
751 selected_item
= NULL
;
754 /* --------------------------------------------------------------------------------------------- */
757 help_select_link (void)
760 if (selected_item
== NULL
)
762 #ifdef WE_WANT_TO_GO_BACKWARD_ON_KEY_RIGHT
763 /* Is there any reason why the right key would take us
764 * backward if there are no links selected?, I agree
765 * with Torben than doing nothing in this case is better
767 /* If there are no links, go backward in history */
770 history_ptr
= HISTORY_SIZE
- 1;
772 currentpoint
= history
[history_ptr
].page
;
773 selected_item
= history
[history_ptr
].link
;
778 history_ptr
= (history_ptr
+ 1) % HISTORY_SIZE
;
779 history
[history_ptr
].page
= currentpoint
;
780 history
[history_ptr
].link
= selected_item
;
781 currentpoint
= help_follow_link (currentpoint
, selected_item
);
784 selected_item
= NULL
;
787 /* --------------------------------------------------------------------------------------------- */
790 help_execute_cmd (long command
)
792 cb_ret_t ret
= MSG_HANDLED
;
806 help_prev_link (TRUE
);
809 help_next_link (TRUE
);
812 move_forward (help_lines
- 1);
815 move_backward (help_lines
- 1);
817 case CK_HalfPageDown
:
818 move_forward (help_lines
/ 2);
821 move_backward (help_lines
/ 2);
833 help_next_link (FALSE
);
836 help_prev_link (FALSE
);
848 ret
= MSG_NOT_HANDLED
;
854 /* --------------------------------------------------------------------------------------------- */
857 help_handle_key (WDialog
*h
, int key
)
859 Widget
*w
= WIDGET (h
);
862 command
= widget_lookup_key (w
, key
);
863 if (command
== CK_IgnoreKey
)
864 return MSG_NOT_HANDLED
;
866 return help_execute_cmd (command
);
869 /* --------------------------------------------------------------------------------------------- */
872 help_bg_callback (Widget
*w
, Widget
*sender
, widget_msg_t msg
, int parm
, void *data
)
877 frame_callback (w
, NULL
, MSG_DRAW
, 0, NULL
);
878 help_show (DIALOG (w
->owner
), currentpoint
);
882 return frame_callback (w
, sender
, msg
, parm
, data
);
886 /* --------------------------------------------------------------------------------------------- */
889 help_resize (WDialog
*h
)
891 Widget
*w
= WIDGET (h
);
895 help_lines
= MIN (LINES
- 4, MAX (2 * LINES
/ 3, 18));
896 r
.lines
= help_lines
+ 4;
897 r
.cols
= HELP_WINDOW_WIDTH
+ 4;
898 dlg_default_callback (w
, NULL
, MSG_RESIZE
, 0, &r
);
899 bb
= buttonbar_find (h
);
900 widget_set_size (WIDGET (bb
), LINES
- 1, 0, 1, COLS
);
905 /* --------------------------------------------------------------------------------------------- */
908 help_callback (Widget
*w
, Widget
*sender
, widget_msg_t msg
, int parm
, void *data
)
910 WDialog
*h
= DIALOG (w
);
915 return help_resize (h
);
921 ret
= help_handle_key (h
, parm
);
922 if (ret
== MSG_HANDLED
)
929 /* Handle shortcuts and buttonbar. */
930 return help_execute_cmd (parm
);
933 return dlg_default_callback (w
, sender
, msg
, parm
, data
);
937 /* --------------------------------------------------------------------------------------------- */
940 interactive_display_finish (void)
945 /* --------------------------------------------------------------------------------------------- */
946 /** translate help file into terminal encoding */
949 translate_file (char *filedata
)
953 conv
= str_crt_conv_from ("UTF-8");
954 if (conv
!= INVALID_CONV
)
956 GString
*translated_data
;
961 /* initial allocation for largest whole help file */
962 translated_data
= g_string_sized_new (32 * 1024);
963 nok
= (str_convert (conv
, filedata
, translated_data
) == ESTR_FAILURE
);
964 fdata
= g_string_free (translated_data
, nok
);
966 str_close_conv (conv
);
970 /* --------------------------------------------------------------------------------------------- */
973 md_callback (Widget
*w
, Widget
*sender
, widget_msg_t msg
, int parm
, void *data
)
978 widget_default_callback (w
, NULL
, MSG_RESIZE
, 0, data
);
979 w
->rect
.lines
= help_lines
;
983 return widget_default_callback (w
, sender
, msg
, parm
, data
);
987 /* --------------------------------------------------------------------------------------------- */
990 help_mouse_callback (Widget
*w
, mouse_msg_t msg
, mouse_event_t
*event
)
993 GSList
*current_area
;
995 if (msg
!= MSG_MOUSE_CLICK
)
998 if ((event
->buttons
& GPM_B_RIGHT
) != 0)
1000 /* Right button click */
1005 /* Left bytton click */
1007 /* The event is relative to the dialog window, adjust it: */
1011 /* Test whether the mouse click is inside one of the link areas */
1012 for (current_area
= link_area
; current_area
!= NULL
; current_area
= g_slist_next (current_area
))
1014 Link_Area
*la
= (Link_Area
*) current_area
->data
;
1016 /* Test one line link area */
1017 if (y
== la
->y1
&& x
>= la
->x1
&& y
== la
->y2
&& x
<= la
->x2
)
1020 /* Test two line link area */
1021 if (la
->y1
+ 1 == la
->y2
)
1023 /* The first line || The second line */
1024 if ((y
== la
->y1
&& x
>= la
->x1
) || (y
== la
->y2
&& x
<= la
->x2
))
1027 /* Mouse will not work with link areas of more than two lines */
1030 /* Test whether a link area was found */
1031 if (current_area
!= NULL
)
1033 Link_Area
*la
= (Link_Area
*) current_area
->data
;
1035 /* The click was inside a link area -> follow the link */
1036 history_ptr
= (history_ptr
+ 1) % HISTORY_SIZE
;
1037 history
[history_ptr
].page
= currentpoint
;
1038 history
[history_ptr
].link
= la
->link_name
;
1039 currentpoint
= help_follow_link (currentpoint
, la
->link_name
);
1040 selected_item
= NULL
;
1043 move_backward (help_lines
- 1);
1044 else if (y
>= help_lines
)
1045 move_forward (help_lines
- 1);
1046 else if (y
< help_lines
/ 2)
1051 /* Show the new node */
1052 widget_draw (WIDGET (w
->owner
));
1055 /* --------------------------------------------------------------------------------------------- */
1058 mousedispatch_new (const WRect
*r
)
1062 w
= g_new0 (Widget
, 1);
1063 widget_init (w
, r
, md_callback
, help_mouse_callback
);
1064 w
->options
|= WOP_SELECTABLE
| WOP_WANT_CURSOR
;
1069 /* --------------------------------------------------------------------------------------------- */
1070 /*** public functions ****************************************************************************/
1071 /* --------------------------------------------------------------------------------------------- */
1073 /* event callback */
1075 help_interactive_display (const gchar
*event_group_name
, const gchar
*event_name
,
1076 gpointer init_data
, gpointer data
)
1078 const dlg_colors_t help_colors
= {
1079 HELP_NORMAL_COLOR
, /* common text color */
1080 0, /* unused in help */
1081 HELP_BOLD_COLOR
, /* bold text color */
1082 0, /* unused in help */
1083 HELP_TITLE_COLOR
/* title color */
1088 WButtonBar
*help_bar
;
1090 char *hlpfile
= NULL
;
1092 ev_help_t
*event_data
= (ev_help_t
*) data
;
1093 WRect r
= { 1, 1, 1, 1 };
1095 (void) event_group_name
;
1099 if (event_data
->filename
!= NULL
)
1100 g_file_get_contents (event_data
->filename
, &filedata
, NULL
, NULL
);
1102 filedata
= load_mc_home_file (mc_global
.share_data_dir
, MC_HELP
, &hlpfile
, NULL
);
1104 if (filedata
== NULL
)
1105 message (D_ERROR
, MSG_ERROR
, _("Cannot open file %s\n%s"),
1106 event_data
->filename
? event_data
->filename
: hlpfile
, unix_error_string (errno
));
1110 if (filedata
== NULL
)
1113 translate_file (filedata
);
1120 if ((event_data
->node
== NULL
) || (*event_data
->node
== '\0'))
1121 event_data
->node
= "[main]";
1123 main_node
= search_string (fdata
, event_data
->node
);
1125 if (main_node
== NULL
)
1127 message (D_ERROR
, MSG_ERROR
, _("Cannot find node %s in help file"), event_data
->node
);
1129 /* Fallback to [main], return if it also cannot be found */
1130 main_node
= search_string (fdata
, "[main]");
1131 if (main_node
== NULL
)
1133 interactive_display_finish ();
1138 help_lines
= MIN (LINES
- 4, MAX (2 * LINES
/ 3, 18));
1141 dlg_create (TRUE
, 0, 0, help_lines
+ 4, HELP_WINDOW_WIDTH
+ 4, WPOS_CENTER
| WPOS_TRYUP
,
1142 FALSE
, help_colors
, help_callback
, NULL
, "[Help]", _("Help"));
1143 wh
= WIDGET (whelp
);
1145 wh
->keymap
= help_map
;
1146 widget_want_tab (wh
, TRUE
);
1147 /* draw background */
1148 whelp
->bg
->callback
= help_bg_callback
;
1150 selected_item
= search_string_node (main_node
, STRING_LINK_START
) - 1;
1151 currentpoint
= main_node
+ 1; /* Skip the newline following the start of the node */
1153 for (history_ptr
= HISTORY_SIZE
- 1; history_ptr
>= 0; history_ptr
--)
1155 history
[history_ptr
].page
= currentpoint
;
1156 history
[history_ptr
].link
= selected_item
;
1159 help_bar
= buttonbar_new ();
1160 WIDGET (help_bar
)->rect
.y
-= wh
->rect
.y
;
1161 WIDGET (help_bar
)->rect
.x
-= wh
->rect
.x
;
1163 r
.lines
= help_lines
;
1164 r
.cols
= HELP_WINDOW_WIDTH
- 2;
1165 md
= mousedispatch_new (&r
);
1167 group_add_widget (g
, md
);
1168 group_add_widget (g
, help_bar
); /* FIXME */
1170 buttonbar_set_label (help_bar
, 1, Q_ ("ButtonBar|Help"), wh
->keymap
, NULL
);
1171 buttonbar_set_label (help_bar
, 2, Q_ ("ButtonBar|Index"), wh
->keymap
, NULL
);
1172 buttonbar_set_label (help_bar
, 3, Q_ ("ButtonBar|Prev"), wh
->keymap
, NULL
);
1173 buttonbar_set_label (help_bar
, 4, "", wh
->keymap
, NULL
);
1174 buttonbar_set_label (help_bar
, 5, "", wh
->keymap
, NULL
);
1175 buttonbar_set_label (help_bar
, 6, "", wh
->keymap
, NULL
);
1176 buttonbar_set_label (help_bar
, 7, "", wh
->keymap
, NULL
);
1177 buttonbar_set_label (help_bar
, 8, "", wh
->keymap
, NULL
);
1178 buttonbar_set_label (help_bar
, 9, "", wh
->keymap
, NULL
);
1179 buttonbar_set_label (help_bar
, 10, Q_ ("ButtonBar|Quit"), wh
->keymap
, NULL
);
1182 interactive_display_finish ();
1183 widget_destroy (wh
);
1187 /* --------------------------------------------------------------------------------------------- */