Merge branch '2123_crash_while_copy'
[midnight-commander.git] / src / help.c
blob257a3f746780b5b4c11f60b9004d3ebc70179ce1
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.h"
58 #include "lib/tty/mouse.h"
59 #include "lib/tty/key.h"
60 #include "lib/strutil.h"
62 #include "dialog.h" /* For Dlg_head */
63 #include "widget.h" /* For Widget */
64 #include "wtools.h" /* For common_dialog_repaint() */
65 #include "cmddef.h"
66 #include "keybind.h"
67 #include "help.h"
68 #include "main.h"
70 #define MAXLINKNAME 80
71 #define HISTORY_SIZE 20
72 #define HELP_WINDOW_WIDTH min(80, COLS - 16)
74 #define STRING_LINK_START "\01"
75 #define STRING_LINK_POINTER "\02"
76 #define STRING_LINK_END "\03"
77 #define STRING_NODE_END "\04"
80 static char *fdata = NULL; /* Pointer to the loaded data file */
81 static int help_lines; /* Lines in help viewer */
82 static int history_ptr; /* For the history queue */
83 static const char *main_node; /* The main node */
84 static const char *last_shown = NULL; /* Last byte shown in a screen */
85 static gboolean end_of_node = FALSE; /* Flag: the last character of the node shown? */
86 static const char *currentpoint;
87 static const char *selected_item;
89 /* The widget variables */
90 static Dlg_head *whelp;
92 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
101 int x1, y1, x2, y2;
102 const char *link_name;
103 } Link_Area;
105 static GSList *link_area = NULL;
106 static gboolean inside_link_area = FALSE;
108 static cb_ret_t help_callback (Dlg_head * h, Widget * sender, dlg_msg_t msg, int parm, void *data);
110 /* returns the position where text was found in the start buffer */
111 /* or 0 if not found */
112 static const char *
113 search_string (const char *start, const char *text)
115 const char *result = NULL;
116 char *local_text = g_strdup (text);
117 char *d = local_text;
118 const char *e = start;
120 /* fmt sometimes replaces a space with a newline in the help file */
121 /* Replace the newlines in the link name with spaces to correct the situation */
122 while (*d != '\0')
124 if (*d == '\n')
125 *d = ' ';
126 str_next_char (&d);
129 /* Do search */
130 for (d = local_text; *e; e++)
132 if (*d == *e)
133 d++;
134 else
135 d = local_text;
136 if (*d == '\0')
138 result = e + 1;
139 break;
143 g_free (local_text);
144 return result;
147 /* Searches text in the buffer pointed by start. Search ends */
148 /* if the CHAR_NODE_END is found in the text. Returns 0 on failure */
149 static const char *
150 search_string_node (const char *start, const char *text)
152 const char *d = text;
153 const char *e = start;
155 if (start != NULL)
156 for (; *e && *e != CHAR_NODE_END; e++)
158 if (*d == *e)
159 d++;
160 else
161 d = text;
162 if (*d == '\0')
163 return e + 1;
166 return NULL;
169 /* Searches the_char in the buffer pointer by start and searches */
170 /* it can search forward (direction = 1) or backward (direction = -1) */
171 static const char *
172 search_char_node (const char *start, char the_char, int direction)
174 const char *e;
176 for (e = start; (*e != '\0') && (*e != CHAR_NODE_END); e += direction)
177 if (*e == the_char)
178 return e;
180 return NULL;
183 /* Returns the new current pointer when moved lines lines */
184 static const char *
185 move_forward2 (const char *c, int lines)
187 const char *p;
188 int line;
190 currentpoint = c;
191 for (line = 0, p = currentpoint; (*p != '\0') && (*p != CHAR_NODE_END); str_cnext_char (&p))
193 if (line == lines)
194 return currentpoint = p;
196 if (*p == '\n')
197 line++;
199 return currentpoint = c;
202 static const char *
203 move_backward2 (const char *c, int lines)
205 const char *p;
206 int line;
208 currentpoint = c;
209 for (line = 0, p = currentpoint; (*p != '\0') && ((int) (p - fdata) >= 0); str_cprev_char (&p))
211 if (*p == CHAR_NODE_END)
213 /* We reached the beginning of the node */
214 /* Skip the node headers */
215 while (*p != ']')
216 str_cnext_char (&p);
217 return currentpoint = p + 2; /* Skip the newline following the start of the node */
220 if (*(p - 1) == '\n')
221 line++;
222 if (line == lines)
223 return currentpoint = p;
225 return currentpoint = c;
228 static void
229 move_forward (int i)
231 if (!end_of_node)
232 currentpoint = move_forward2 (currentpoint, i);
235 static void
236 move_backward (int i)
238 currentpoint = move_backward2 (currentpoint, ++i);
241 static void
242 move_to_top (void)
244 while (((int) (currentpoint > fdata) > 0) && (*currentpoint != CHAR_NODE_END))
245 currentpoint--;
247 while (*currentpoint != ']')
248 currentpoint++;
249 currentpoint = currentpoint + 2; /* Skip the newline following the start of the node */
250 selected_item = NULL;
253 static void
254 move_to_bottom (void)
256 while ((*currentpoint != '\0') && (*currentpoint != CHAR_NODE_END))
257 currentpoint++;
258 currentpoint--;
259 move_backward (1);
262 static const char *
263 help_follow_link (const char *start, const char *lc_selected_item)
265 char link_name[MAXLINKNAME];
266 const char *p;
267 int i = 0;
269 if (lc_selected_item == NULL)
270 return start;
272 for (p = lc_selected_item; *p && *p != CHAR_NODE_END && *p != CHAR_LINK_POINTER; p++)
274 if (*p == CHAR_LINK_POINTER)
276 link_name[0] = '[';
277 for (i = 1; *p != CHAR_LINK_END && *p && *p != CHAR_NODE_END && i < MAXLINKNAME - 3;)
278 link_name[i++] = *++p;
279 link_name[i - 1] = ']';
280 link_name[i] = '\0';
281 p = search_string (fdata, link_name);
282 if (p != NULL)
284 p += 1; /* Skip the newline following the start of the node */
285 return p;
289 /* Create a replacement page with the error message */
290 return _("Help file format error\n");
293 static const char *
294 select_next_link (const char *current_link)
296 const char *p;
298 if (current_link == NULL)
299 return NULL;
301 p = search_string_node (current_link, STRING_LINK_END);
302 if (p == NULL)
303 return NULL;
304 p = search_string_node (p, STRING_LINK_START);
305 if (p == NULL)
306 return NULL;
307 return p - 1;
310 static const char *
311 select_prev_link (const char *current_link)
313 return current_link == NULL ? NULL : search_char_node (current_link - 1, CHAR_LINK_START, -1);
316 static void
317 start_link_area (int x, int y, const char *link_name)
319 Link_Area *la;
321 if (inside_link_area)
322 message (D_NORMAL, _("Warning"), _("Internal bug: Double start of link area"));
324 /* Allocate memory for a new link area */
325 la = g_new (Link_Area, 1);
326 /* Save the beginning coordinates of the link area */
327 la->x1 = x;
328 la->y1 = y;
329 /* Save the name of the destination anchor */
330 la->link_name = link_name;
331 link_area = g_slist_prepend (link_area, la);
333 inside_link_area = TRUE;
336 static void
337 end_link_area (int x, int y)
339 if (inside_link_area)
341 Link_Area *la = (Link_Area *) link_area->data;
342 /* Save the end coordinates of the link area */
343 la->x2 = x;
344 la->y2 = y;
345 inside_link_area = FALSE;
349 static void
350 clear_link_areas (void)
352 g_slist_foreach (link_area, (GFunc) g_free, NULL);
353 g_slist_free (link_area);
354 link_area = NULL;
355 inside_link_area = FALSE;
358 static void
359 help_print_word (Dlg_head * h, GString * word, int *col, int *line, gboolean add_space)
361 if (*line >= help_lines)
362 g_string_set_size (word, 0);
363 else
365 int w;
367 w = str_term_width1 (word->str);
368 if (*col + w >= HELP_WINDOW_WIDTH)
370 *col = 0;
371 (*line)++;
374 if (*line >= help_lines)
375 g_string_set_size (word, 0);
376 else
378 dlg_move (h, *line + 2, *col + 2);
379 tty_print_string (word->str);
380 g_string_set_size (word, 0);
381 *col += w;
385 if (add_space)
387 if (*col < HELP_WINDOW_WIDTH - 1)
389 tty_print_char (' ');
390 (*col)++;
392 else
394 *col = 0;
395 (*line)++;
400 static void
401 help_show (Dlg_head * h, const char *paint_start)
403 const char *p, *n;
404 int col, line, c;
405 gboolean painting = TRUE;
406 gboolean acs; /* Flag: Alternate character set active? */
407 gboolean repeat_paint;
408 int active_col, active_line; /* Active link position */
409 char buff[MB_LEN_MAX + 1];
410 GString *word;
412 word = g_string_sized_new (32);
414 tty_setcolor (HELP_NORMAL_COLOR);
417 line = col = active_col = active_line = 0;
418 repeat_paint = FALSE;
419 acs = FALSE;
421 clear_link_areas ();
422 if ((int) (selected_item - paint_start) < 0)
423 selected_item = NULL;
425 p = paint_start;
426 n = paint_start;
427 while ((n[0] != '\0') && (n[0] != CHAR_NODE_END) && (line < help_lines))
429 p = n;
430 n = str_cget_next_char (p);
431 memcpy (buff, p, n - p);
432 buff[n - p] = '\0';
434 c = (unsigned char) buff[0];
435 switch (c)
437 case CHAR_LINK_START:
438 if (selected_item == NULL)
439 selected_item = p;
440 if (p != selected_item)
441 tty_setcolor (HELP_LINK_COLOR);
442 else
444 tty_setcolor (HELP_SLINK_COLOR);
446 /* Store the coordinates of the link */
447 active_col = col + 2;
448 active_line = line + 2;
450 start_link_area (col, line, p);
451 break;
452 case CHAR_LINK_POINTER:
453 painting = FALSE;
454 end_link_area (col - 1, line);
455 break;
456 case CHAR_LINK_END:
457 painting = TRUE;
458 help_print_word (h, word, &col, &line, FALSE);
459 tty_setcolor (HELP_NORMAL_COLOR);
460 break;
461 case CHAR_ALTERNATE:
462 acs = TRUE;
463 break;
464 case CHAR_NORMAL:
465 acs = FALSE;
466 break;
467 case CHAR_VERSION:
468 dlg_move (h, line + 2, col + 2);
469 tty_print_string (VERSION);
470 col += str_term_width1 (VERSION);
471 break;
472 case CHAR_FONT_BOLD:
473 tty_setcolor (HELP_BOLD_COLOR);
474 break;
475 case CHAR_FONT_ITALIC:
476 tty_setcolor (HELP_ITALIC_COLOR);
477 break;
478 case CHAR_FONT_NORMAL:
479 help_print_word (h, word, &col, &line, FALSE);
480 tty_setcolor (HELP_NORMAL_COLOR);
481 break;
482 case '\n':
483 if (painting)
484 help_print_word (h, word, &col, &line, FALSE);
485 line++;
486 col = 0;
487 break;
488 case '\t':
489 col = (col / 8 + 1) * 8;
490 if (col >= HELP_WINDOW_WIDTH)
492 line++;
493 col = 8;
495 break;
496 case ' ':
497 /* word delimeter */
498 if (painting)
499 help_print_word (h, word, &col, &line, TRUE);
500 break;
501 default:
502 if (painting && (line < help_lines))
504 if (!acs)
505 /* accumulate symbols in a word */
506 g_string_append (word, buff);
507 else if (col < HELP_WINDOW_WIDTH)
509 dlg_move (h, line + 2, col + 2);
511 if ((c == ' ') || (c == '.'))
512 tty_print_char (c);
513 else
514 #ifndef HAVE_SLANG
515 tty_print_char (acs_map[c]);
516 #else
517 SLsmg_draw_object (h->y + line + 2, h->x + col + 2, c);
518 #endif
519 col++;
525 /* print last word */
526 if (n[0] == CHAR_NODE_END)
527 help_print_word (h, word, &col, &line, FALSE);
529 last_shown = p;
530 end_of_node = line < help_lines;
531 tty_setcolor (HELP_NORMAL_COLOR);
532 if ((int) (selected_item - last_shown) >= 0)
534 if ((link_area == NULL) || (link_area->data == NULL))
535 selected_item = NULL;
536 else
538 selected_item = ((Link_Area *) link_area->data)->link_name;
539 repeat_paint = TRUE;
543 while (repeat_paint);
545 g_string_free (word, TRUE);
547 /* Position the cursor over a nice link */
548 if (active_col)
549 dlg_move (h, active_line, active_col);
552 static int
553 help_event (Gpm_Event * event, void *vp)
555 Widget *w = vp;
556 GSList *current_area;
558 if ((event->type & GPM_UP) == 0)
559 return 0;
561 /* The event is relative to the dialog window, adjust it: */
562 event->x -= 2;
563 event->y -= 2;
565 if (event->buttons & GPM_B_RIGHT)
567 currentpoint = history[history_ptr].page;
568 selected_item = history[history_ptr].link;
569 history_ptr--;
570 if (history_ptr < 0)
571 history_ptr = HISTORY_SIZE - 1;
573 help_callback (w->owner, NULL, DLG_DRAW, 0, NULL);
574 return 0;
577 /* Test whether the mouse click is inside one of the link areas */
578 for (current_area = link_area; current_area != NULL; current_area = g_slist_next (current_area))
580 Link_Area *la = (Link_Area *) current_area->data;
581 /* Test one line link area */
582 if (event->y == la->y1 && event->x >= la->x1 && event->y == la->y2 && event->x <= la->x2)
583 break;
584 /* Test two line link area */
585 if (la->y1 + 1 == la->y2)
587 /* The first line */
588 if (event->y == la->y1 && event->x >= la->x1)
589 break;
590 /* The second line */
591 if (event->y == la->y2 && event->x <= la->x2)
592 break;
594 /* Mouse will not work with link areas of more than two lines */
597 /* Test whether a link area was found */
598 if (current_area != NULL)
600 Link_Area *la = (Link_Area *) current_area->data;
602 /* The click was inside a link area -> follow the link */
603 history_ptr = (history_ptr + 1) % HISTORY_SIZE;
604 history[history_ptr].page = currentpoint;
605 history[history_ptr].link = la->link_name;
606 currentpoint = help_follow_link (currentpoint, la->link_name);
607 selected_item = NULL;
609 else if (event->y < 0)
610 move_backward (help_lines - 1);
611 else if (event->y >= help_lines)
612 move_forward (help_lines - 1);
613 else if (event->y < help_lines / 2)
614 move_backward (1);
615 else
616 move_forward (1);
618 /* Show the new node */
619 help_callback (w->owner, NULL, DLG_DRAW, 0, NULL);
621 return 0;
624 /* show help */
625 static void
626 help_help (Dlg_head * h)
628 const char *p;
630 history_ptr = (history_ptr + 1) % HISTORY_SIZE;
631 history[history_ptr].page = currentpoint;
632 history[history_ptr].link = selected_item;
634 p = search_string (fdata, "[How to use help]");
635 if (p != NULL)
637 currentpoint = p + 1; /* Skip the newline following the start of the node */
638 selected_item = NULL;
639 help_callback (h, NULL, DLG_DRAW, 0, NULL);
643 static void
644 help_index (Dlg_head * h)
646 const char *new_item;
648 new_item = search_string (fdata, "[Contents]");
650 if (new_item == NULL)
651 message (D_ERROR, MSG_ERROR, _("Cannot find node %s in help file"), "[Contents]");
652 else
654 history_ptr = (history_ptr + 1) % HISTORY_SIZE;
655 history[history_ptr].page = currentpoint;
656 history[history_ptr].link = selected_item;
658 currentpoint = new_item + 1; /* Skip the newline following the start of the node */
659 selected_item = NULL;
660 help_callback (h, NULL, DLG_DRAW, 0, NULL);
664 static void
665 help_back (Dlg_head * h)
667 currentpoint = history[history_ptr].page;
668 selected_item = history[history_ptr].link;
669 history_ptr--;
670 if (history_ptr < 0)
671 history_ptr = HISTORY_SIZE - 1;
673 help_callback (h, NULL, DLG_DRAW, 0, NULL); /* FIXME: unneeded? */
676 static void
677 help_cmk_move_backward (void *vp, int lines)
679 (void) &vp;
680 move_backward (lines);
683 static void
684 help_cmk_move_forward (void *vp, int lines)
686 (void) &vp;
687 move_forward (lines);
690 static void
691 help_cmk_moveto_top (void *vp, int lines)
693 (void) &vp;
694 (void) &lines;
695 move_to_top ();
698 static void
699 help_cmk_moveto_bottom (void *vp, int lines)
701 (void) &vp;
702 (void) &lines;
703 move_to_bottom ();
706 static void
707 help_next_link (gboolean move_down)
709 const char *new_item;
711 new_item = select_next_link (selected_item);
712 if (new_item != NULL)
714 selected_item = new_item;
715 if ((int) (selected_item - last_shown) >= 0)
717 if (move_down)
718 move_forward (1);
719 else
720 selected_item = NULL;
723 else if (move_down)
724 move_forward (1);
725 else
726 selected_item = NULL;
729 static void
730 help_prev_link (gboolean move_up)
732 const char *new_item;
734 new_item = select_prev_link (selected_item);
735 selected_item = new_item;
736 if ((selected_item == NULL) || (selected_item < currentpoint))
738 if (move_up)
739 move_backward (1);
740 else if ((link_area != NULL) && (link_area->data != NULL))
741 selected_item = ((Link_Area *) link_area->data)->link_name;
742 else
743 selected_item = NULL;
747 static void
748 help_next_node (void)
750 const char *new_item;
752 new_item = currentpoint;
753 while ((*new_item != '\0') && (*new_item != CHAR_NODE_END))
754 new_item++;
756 if (*++new_item == '[')
757 while (*++new_item != '\0')
758 if ((*new_item == ']') && (*++new_item != '\0') && (*++new_item != '\0'))
760 currentpoint = new_item;
761 selected_item = NULL;
762 break;
766 static void
767 help_prev_node (void)
769 const char *new_item;
771 new_item = currentpoint;
772 while (((int) (new_item - fdata) > 1) && (*new_item != CHAR_NODE_END))
773 new_item--;
774 new_item--;
775 while (((int) (new_item - fdata) > 0) && (*new_item != CHAR_NODE_END))
776 new_item--;
777 while (*new_item != ']')
778 new_item++;
779 currentpoint = new_item + 2;
780 selected_item = NULL;
783 static void
784 help_select_link (void)
786 /* follow link */
787 if (selected_item == NULL)
789 #ifdef WE_WANT_TO_GO_BACKWARD_ON_KEY_RIGHT
790 /* Is there any reason why the right key would take us
791 * backward if there are no links selected?, I agree
792 * with Torben than doing nothing in this case is better
794 /* If there are no links, go backward in history */
795 history_ptr--;
796 if (history_ptr < 0)
797 history_ptr = HISTORY_SIZE - 1;
799 currentpoint = history[history_ptr].page;
800 selected_item = history[history_ptr].link;
801 #endif
803 else
805 history_ptr = (history_ptr + 1) % HISTORY_SIZE;
806 history[history_ptr].page = currentpoint;
807 history[history_ptr].link = selected_item;
808 currentpoint = help_follow_link (currentpoint, selected_item);
811 selected_item = NULL;
814 static cb_ret_t
815 help_execute_cmd (unsigned long command)
817 cb_ret_t ret = MSG_HANDLED;
819 switch (command)
821 case CK_HelpHelp:
822 help_help (whelp);
823 break;
824 case CK_HelpIndex:
825 help_index (whelp);
826 break;
827 case CK_HelpBack:
828 help_back (whelp);
829 break;
830 case CK_HelpMoveUp:
831 help_prev_link (TRUE);
832 break;
833 case CK_HelpMoveDown:
834 help_next_link (TRUE);
835 break;
836 case CK_HelpSelectLink:
837 help_select_link ();
838 break;
839 case CK_HelpNextLink:
840 help_next_link (FALSE);
841 break;
842 case CK_HelpPrevLink:
843 help_prev_link (FALSE);
844 break;
845 case CK_HelpNextNode:
846 help_next_node ();
847 break;
848 case CK_HelpPrevNode:
849 help_prev_node ();
850 break;
851 case CK_HelpQuit:
852 dlg_stop (whelp);
853 break;
854 default:
855 ret = MSG_NOT_HANDLED;
858 return ret;
861 static cb_ret_t
862 help_handle_key (Dlg_head * h, int c)
864 if (c != KEY_UP && c != KEY_DOWN &&
865 check_movement_keys (c, help_lines, NULL,
866 help_cmk_move_backward,
867 help_cmk_move_forward,
868 help_cmk_moveto_top, help_cmk_moveto_bottom) == MSG_HANDLED)
870 /* Nothing */ ;
872 else
874 unsigned long command;
876 command = lookup_keymap_command (help_map, c);
877 if ((command == CK_Ignore_Key) || (help_execute_cmd (command) == MSG_NOT_HANDLED))
878 return MSG_NOT_HANDLED;
881 help_callback (h, NULL, DLG_DRAW, 0, NULL);
882 return MSG_HANDLED;
885 static cb_ret_t
886 help_callback (Dlg_head * h, Widget * sender, dlg_msg_t msg, int parm, void *data)
888 WButtonBar *bb;
890 switch (msg)
892 case DLG_RESIZE:
893 help_lines = min (LINES - 4, max (2 * LINES / 3, 18));
894 dlg_set_size (h, help_lines + 4, HELP_WINDOW_WIDTH + 4);
895 bb = find_buttonbar (h);
896 widget_set_size (&bb->widget, LINES - 1, 0, 1, COLS);
897 return MSG_HANDLED;
899 case DLG_DRAW:
900 common_dialog_repaint (h);
901 help_show (h, currentpoint);
902 return MSG_HANDLED;
904 case DLG_KEY:
905 return help_handle_key (h, parm);
907 case DLG_ACTION:
908 /* command from buttonbar */
909 return help_execute_cmd (parm);
911 default:
912 return default_dlg_callback (h, sender, msg, parm, data);
916 static void
917 interactive_display_finish (void)
919 clear_link_areas ();
922 /* translate help file into terminal encoding */
923 static void
924 translate_file (char *filedata)
926 GIConv conv;
927 GString *translated_data;
929 /* initial allocation for largest whole help file */
930 translated_data = g_string_sized_new (32 * 1024);
932 conv = str_crt_conv_from ("UTF-8");
934 if (conv == INVALID_CONV)
935 g_string_free (translated_data, TRUE);
936 else
938 g_free (fdata);
940 if (str_convert (conv, filedata, translated_data) != ESTR_FAILURE)
941 fdata = g_string_free (translated_data, FALSE);
942 else
944 fdata = NULL;
945 g_string_free (translated_data, TRUE);
947 str_close_conv (conv);
951 static cb_ret_t
952 md_callback (Widget * w, widget_msg_t msg, int parm)
954 switch (msg)
956 case WIDGET_RESIZED:
957 w->lines = help_lines;
958 return MSG_HANDLED;
960 default:
961 return default_proc (msg, parm);
965 static Widget *
966 mousedispatch_new (int y, int x, int yl, int xl)
968 Widget *w = g_new (Widget, 1);
969 init_widget (w, y, x, yl, xl, md_callback, help_event);
970 return w;
973 void
974 interactive_display (const char *filename, const char *node)
976 const int help_colors[DLG_COLOR_NUM] = {
977 HELP_NORMAL_COLOR, /* common text color */
978 0, /* unused in help */
979 HELP_BOLD_COLOR, /* title color */
980 0 /* unused in help */
983 WButtonBar *help_bar;
984 Widget *md;
985 char *hlpfile = NULL;
986 char *filedata;
988 if (filename != NULL)
989 filedata = load_file (filename);
990 else
991 filedata = load_mc_home_file (mc_home, mc_home_alt, "mc.hlp", &hlpfile);
993 if (filedata == NULL)
994 message (D_ERROR, MSG_ERROR, _("Cannot open file %s\n%s"),
995 filename ? filename : hlpfile, unix_error_string (errno));
997 g_free (hlpfile);
999 if (filedata == NULL)
1000 return;
1002 translate_file (filedata);
1004 g_free (filedata);
1006 if (fdata == NULL)
1007 return;
1009 if ((node == NULL) || (*node == '\0'))
1010 node = "[main]";
1012 main_node = search_string (fdata, node);
1014 if (main_node == NULL)
1016 message (D_ERROR, MSG_ERROR, _("Cannot find node %s in help file"), node);
1018 /* Fallback to [main], return if it also cannot be found */
1019 main_node = search_string (fdata, "[main]");
1020 if (main_node == NULL)
1022 interactive_display_finish ();
1023 return;
1027 help_lines = min (LINES - 4, max (2 * LINES / 3, 18));
1029 whelp =
1030 create_dlg (TRUE, 0, 0, help_lines + 4, HELP_WINDOW_WIDTH + 4,
1031 help_colors, help_callback, "[Help]", _("Help"),
1032 DLG_TRYUP | DLG_CENTER | DLG_WANT_TAB);
1034 selected_item = search_string_node (main_node, STRING_LINK_START) - 1;
1035 currentpoint = main_node + 1; /* Skip the newline following the start of the node */
1037 for (history_ptr = HISTORY_SIZE; history_ptr;)
1039 history_ptr--;
1040 history[history_ptr].page = currentpoint;
1041 history[history_ptr].link = selected_item;
1044 help_bar = buttonbar_new (TRUE);
1045 help_bar->widget.y -= whelp->y;
1046 help_bar->widget.x -= whelp->x;
1048 md = mousedispatch_new (1, 1, help_lines, HELP_WINDOW_WIDTH - 2);
1050 add_widget (whelp, md);
1051 add_widget (whelp, help_bar);
1053 buttonbar_set_label (help_bar, 1, Q_ ("ButtonBar|Help"), help_map, NULL);
1054 buttonbar_set_label (help_bar, 2, Q_ ("ButtonBar|Index"), help_map, NULL);
1055 buttonbar_set_label (help_bar, 3, Q_ ("ButtonBar|Prev"), help_map, NULL);
1056 buttonbar_set_label (help_bar, 4, "", help_map, NULL);
1057 buttonbar_set_label (help_bar, 5, "", help_map, NULL);
1058 buttonbar_set_label (help_bar, 6, "", help_map, NULL);
1059 buttonbar_set_label (help_bar, 7, "", help_map, NULL);
1060 buttonbar_set_label (help_bar, 8, "", help_map, NULL);
1061 buttonbar_set_label (help_bar, 9, "", help_map, NULL);
1062 buttonbar_set_label (help_bar, 10, Q_ ("ButtonBar|Quit"), help_map, NULL);
1064 run_dlg (whelp);
1065 interactive_display_finish ();
1066 destroy_dlg (whelp);