Ticket #2170: Color collisions
[midnight-commander.git] / src / help.c
blobb5f4e1781b635521382f6543bac476dd402e30bc
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/tty/mouse.h"
58 #include "lib/skin.h"
59 #include "lib/strutil.h"
61 #include "dialog.h" /* For Dlg_head */
62 #include "widget.h" /* For Widget */
63 #include "wtools.h" /* For common_dialog_repaint() */
64 #include "cmddef.h"
65 #include "keybind.h"
66 #include "help.h"
67 #include "main.h"
69 #define MAXLINKNAME 80
70 #define HISTORY_SIZE 20
71 #define HELP_WINDOW_WIDTH min(80, COLS - 16)
73 #define STRING_LINK_START "\01"
74 #define STRING_LINK_POINTER "\02"
75 #define STRING_LINK_END "\03"
76 #define STRING_NODE_END "\04"
79 static char *fdata = NULL; /* Pointer to the loaded data file */
80 static int help_lines; /* Lines in help viewer */
81 static int history_ptr; /* For the history queue */
82 static const char *main_node; /* The main node */
83 static const char *last_shown = NULL; /* Last byte shown in a screen */
84 static gboolean end_of_node = FALSE; /* Flag: the last character of the node shown? */
85 static const char *currentpoint;
86 static const char *selected_item;
88 /* The widget variables */
89 static Dlg_head *whelp;
91 static struct
93 const char *page; /* Pointer to the selected page */
94 const char *link; /* Pointer to the selected link */
95 } history[HISTORY_SIZE];
97 /* Link areas for the mouse */
98 typedef struct Link_Area
100 int x1, y1, x2, y2;
101 const char *link_name;
102 } Link_Area;
104 static GSList *link_area = NULL;
105 static gboolean inside_link_area = FALSE;
107 static cb_ret_t help_callback (Dlg_head * h, Widget * sender, dlg_msg_t msg, int parm, void *data);
109 /* returns the position where text was found in the start buffer */
110 /* or 0 if not found */
111 static const char *
112 search_string (const char *start, const char *text)
114 const char *result = NULL;
115 char *local_text = g_strdup (text);
116 char *d = local_text;
117 const char *e = start;
119 /* fmt sometimes replaces a space with a newline in the help file */
120 /* Replace the newlines in the link name with spaces to correct the situation */
121 while (*d != '\0')
123 if (*d == '\n')
124 *d = ' ';
125 str_next_char (&d);
128 /* Do search */
129 for (d = local_text; *e; e++)
131 if (*d == *e)
132 d++;
133 else
134 d = local_text;
135 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++)
157 if (*d == *e)
158 d++;
159 else
160 d = text;
161 if (*d == '\0')
162 return e + 1;
165 return NULL;
168 /* Searches the_char in the buffer pointer by start and searches */
169 /* it can search forward (direction = 1) or backward (direction = -1) */
170 static const char *
171 search_char_node (const char *start, char the_char, int direction)
173 const char *e;
175 for (e = start; (*e != '\0') && (*e != CHAR_NODE_END); e += direction)
176 if (*e == the_char)
177 return e;
179 return NULL;
182 /* Returns the new current pointer when moved lines lines */
183 static const char *
184 move_forward2 (const char *c, int lines)
186 const char *p;
187 int line;
189 currentpoint = c;
190 for (line = 0, p = currentpoint; (*p != '\0') && (*p != CHAR_NODE_END); str_cnext_char (&p))
192 if (line == lines)
193 return currentpoint = p;
195 if (*p == '\n')
196 line++;
198 return currentpoint = c;
201 static const char *
202 move_backward2 (const char *c, int lines)
204 const char *p;
205 int line;
207 currentpoint = c;
208 for (line = 0, p = currentpoint; (*p != '\0') && ((int) (p - fdata) >= 0); str_cprev_char (&p))
210 if (*p == CHAR_NODE_END)
212 /* We reached the beginning of the node */
213 /* Skip the node headers */
214 while (*p != ']')
215 str_cnext_char (&p);
216 return currentpoint = p + 2; /* Skip the newline following the start of the node */
219 if (*(p - 1) == '\n')
220 line++;
221 if (line == lines)
222 return currentpoint = p;
224 return currentpoint = c;
227 static void
228 move_forward (int i)
230 if (!end_of_node)
231 currentpoint = move_forward2 (currentpoint, i);
234 static void
235 move_backward (int i)
237 currentpoint = move_backward2 (currentpoint, ++i);
240 static void
241 move_to_top (void)
243 while (((int) (currentpoint > fdata) > 0) && (*currentpoint != CHAR_NODE_END))
244 currentpoint--;
246 while (*currentpoint != ']')
247 currentpoint++;
248 currentpoint = currentpoint + 2; /* Skip the newline following the start of the node */
249 selected_item = NULL;
252 static void
253 move_to_bottom (void)
255 while ((*currentpoint != '\0') && (*currentpoint != CHAR_NODE_END))
256 currentpoint++;
257 currentpoint--;
258 move_backward (1);
261 static const char *
262 help_follow_link (const char *start, const char *lc_selected_item)
264 char link_name[MAXLINKNAME];
265 const char *p;
266 int i = 0;
268 if (lc_selected_item == NULL)
269 return start;
271 for (p = lc_selected_item; *p && *p != CHAR_NODE_END && *p != CHAR_LINK_POINTER; p++)
273 if (*p == CHAR_LINK_POINTER)
275 link_name[0] = '[';
276 for (i = 1; *p != CHAR_LINK_END && *p && *p != CHAR_NODE_END && i < MAXLINKNAME - 3;)
277 link_name[i++] = *++p;
278 link_name[i - 1] = ']';
279 link_name[i] = '\0';
280 p = search_string (fdata, link_name);
281 if (p != NULL)
283 p += 1; /* Skip the newline following the start of the node */
284 return p;
288 /* Create a replacement page with the error message */
289 return _("Help file format error\n");
292 static const char *
293 select_next_link (const char *current_link)
295 const char *p;
297 if (current_link == NULL)
298 return NULL;
300 p = search_string_node (current_link, STRING_LINK_END);
301 if (p == NULL)
302 return NULL;
303 p = search_string_node (p, STRING_LINK_START);
304 if (p == NULL)
305 return NULL;
306 return p - 1;
309 static const char *
310 select_prev_link (const char *current_link)
312 return current_link == NULL ? NULL : search_char_node (current_link - 1, CHAR_LINK_START, -1);
315 static void
316 start_link_area (int x, int y, const char *link_name)
318 Link_Area *la;
320 if (inside_link_area)
321 message (D_NORMAL, _("Warning"), _("Internal bug: Double start of link area"));
323 /* Allocate memory for a new link area */
324 la = g_new (Link_Area, 1);
325 /* Save the beginning coordinates of the link area */
326 la->x1 = x;
327 la->y1 = y;
328 /* Save the name of the destination anchor */
329 la->link_name = link_name;
330 link_area = g_slist_prepend (link_area, la);
332 inside_link_area = TRUE;
335 static void
336 end_link_area (int x, int y)
338 if (inside_link_area)
340 Link_Area *la = (Link_Area *) link_area->data;
341 /* Save the end coordinates of the link area */
342 la->x2 = x;
343 la->y2 = y;
344 inside_link_area = FALSE;
348 static void
349 clear_link_areas (void)
351 g_slist_foreach (link_area, (GFunc) g_free, NULL);
352 g_slist_free (link_area);
353 link_area = NULL;
354 inside_link_area = FALSE;
357 static void
358 help_print_word (Dlg_head * h, GString * word, int *col, int *line, gboolean add_space)
360 if (*line >= help_lines)
361 g_string_set_size (word, 0);
362 else
364 int w;
366 w = str_term_width1 (word->str);
367 if (*col + w >= HELP_WINDOW_WIDTH)
369 *col = 0;
370 (*line)++;
373 if (*line >= help_lines)
374 g_string_set_size (word, 0);
375 else
377 dlg_move (h, *line + 2, *col + 2);
378 tty_print_string (word->str);
379 g_string_set_size (word, 0);
380 *col += w;
384 if (add_space)
386 if (*col < HELP_WINDOW_WIDTH - 1)
388 tty_print_char (' ');
389 (*col)++;
391 else
393 *col = 0;
394 (*line)++;
399 static void
400 help_show (Dlg_head * h, const char *paint_start)
402 const char *p, *n;
403 int col, line, c;
404 gboolean painting = TRUE;
405 gboolean acs; /* Flag: Alternate character set active? */
406 gboolean repeat_paint;
407 int active_col, active_line; /* Active link position */
408 char buff[MB_LEN_MAX + 1];
409 GString *word;
411 word = g_string_sized_new (32);
413 tty_setcolor (HELP_NORMAL_COLOR);
416 line = col = active_col = active_line = 0;
417 repeat_paint = FALSE;
418 acs = FALSE;
420 clear_link_areas ();
421 if ((int) (selected_item - paint_start) < 0)
422 selected_item = NULL;
424 p = paint_start;
425 n = paint_start;
426 while ((n[0] != '\0') && (n[0] != CHAR_NODE_END) && (line < help_lines))
428 p = n;
429 n = str_cget_next_char (p);
430 memcpy (buff, p, n - p);
431 buff[n - p] = '\0';
433 c = (unsigned char) buff[0];
434 switch (c)
436 case CHAR_LINK_START:
437 if (selected_item == NULL)
438 selected_item = p;
439 if (p != selected_item)
440 tty_setcolor (HELP_LINK_COLOR);
441 else
443 tty_setcolor (HELP_SLINK_COLOR);
445 /* Store the coordinates of the link */
446 active_col = col + 2;
447 active_line = line + 2;
449 start_link_area (col, line, p);
450 break;
451 case CHAR_LINK_POINTER:
452 painting = FALSE;
453 end_link_area (col - 1, line);
454 break;
455 case CHAR_LINK_END:
456 painting = TRUE;
457 help_print_word (h, word, &col, &line, FALSE);
458 tty_setcolor (HELP_NORMAL_COLOR);
459 break;
460 case CHAR_ALTERNATE:
461 acs = TRUE;
462 break;
463 case CHAR_NORMAL:
464 acs = FALSE;
465 break;
466 case CHAR_VERSION:
467 dlg_move (h, line + 2, col + 2);
468 tty_print_string (VERSION);
469 col += str_term_width1 (VERSION);
470 break;
471 case CHAR_FONT_BOLD:
472 tty_setcolor (HELP_BOLD_COLOR);
473 break;
474 case CHAR_FONT_ITALIC:
475 tty_setcolor (HELP_ITALIC_COLOR);
476 break;
477 case CHAR_FONT_NORMAL:
478 help_print_word (h, word, &col, &line, FALSE);
479 tty_setcolor (HELP_NORMAL_COLOR);
480 break;
481 case '\n':
482 if (painting)
483 help_print_word (h, word, &col, &line, FALSE);
484 line++;
485 col = 0;
486 break;
487 case '\t':
488 col = (col / 8 + 1) * 8;
489 if (col >= HELP_WINDOW_WIDTH)
491 line++;
492 col = 8;
494 break;
495 case ' ':
496 /* word delimeter */
497 if (painting)
498 help_print_word (h, word, &col, &line, TRUE);
499 break;
500 default:
501 if (painting && (line < help_lines))
503 if (!acs)
504 /* accumulate symbols in a word */
505 g_string_append (word, buff);
506 else if (col < HELP_WINDOW_WIDTH)
508 dlg_move (h, line + 2, col + 2);
510 if ((c == ' ') || (c == '.'))
511 tty_print_char (c);
512 else
513 #ifndef HAVE_SLANG
514 tty_print_char (acs_map[c]);
515 #else
516 SLsmg_draw_object (h->y + line + 2, h->x + col + 2, c);
517 #endif
518 col++;
524 /* print last word */
525 if (n[0] == CHAR_NODE_END)
526 help_print_word (h, word, &col, &line, FALSE);
528 last_shown = p;
529 end_of_node = line < help_lines;
530 tty_setcolor (HELP_NORMAL_COLOR);
531 if ((int) (selected_item - last_shown) >= 0)
533 if ((link_area == NULL) || (link_area->data == NULL))
534 selected_item = NULL;
535 else
537 selected_item = ((Link_Area *) link_area->data)->link_name;
538 repeat_paint = TRUE;
542 while (repeat_paint);
544 g_string_free (word, TRUE);
546 /* Position the cursor over a nice link */
547 if (active_col)
548 dlg_move (h, active_line, active_col);
551 static int
552 help_event (Gpm_Event * event, void *vp)
554 Widget *w = vp;
555 GSList *current_area;
557 if ((event->type & GPM_UP) == 0)
558 return 0;
560 /* The event is relative to the dialog window, adjust it: */
561 event->x -= 2;
562 event->y -= 2;
564 if (event->buttons & GPM_B_RIGHT)
566 currentpoint = history[history_ptr].page;
567 selected_item = history[history_ptr].link;
568 history_ptr--;
569 if (history_ptr < 0)
570 history_ptr = HISTORY_SIZE - 1;
572 help_callback (w->owner, NULL, DLG_DRAW, 0, NULL);
573 return 0;
576 /* Test whether the mouse click is inside one of the link areas */
577 for (current_area = link_area; current_area != NULL; current_area = g_slist_next (current_area))
579 Link_Area *la = (Link_Area *) current_area->data;
580 /* Test one line link area */
581 if (event->y == la->y1 && event->x >= la->x1 && event->y == la->y2 && event->x <= la->x2)
582 break;
583 /* Test two line link area */
584 if (la->y1 + 1 == la->y2)
586 /* The first line */
587 if (event->y == la->y1 && event->x >= la->x1)
588 break;
589 /* The second line */
590 if (event->y == la->y2 && event->x <= la->x2)
591 break;
593 /* Mouse will not work with link areas of more than two lines */
596 /* Test whether a link area was found */
597 if (current_area != NULL)
599 Link_Area *la = (Link_Area *) current_area->data;
601 /* The click was inside a link area -> follow the link */
602 history_ptr = (history_ptr + 1) % HISTORY_SIZE;
603 history[history_ptr].page = currentpoint;
604 history[history_ptr].link = la->link_name;
605 currentpoint = help_follow_link (currentpoint, la->link_name);
606 selected_item = NULL;
608 else if (event->y < 0)
609 move_backward (help_lines - 1);
610 else if (event->y >= help_lines)
611 move_forward (help_lines - 1);
612 else if (event->y < help_lines / 2)
613 move_backward (1);
614 else
615 move_forward (1);
617 /* Show the new node */
618 help_callback (w->owner, NULL, DLG_DRAW, 0, NULL);
620 return 0;
623 /* show help */
624 static void
625 help_help (Dlg_head * h)
627 const char *p;
629 history_ptr = (history_ptr + 1) % HISTORY_SIZE;
630 history[history_ptr].page = currentpoint;
631 history[history_ptr].link = selected_item;
633 p = search_string (fdata, "[How to use help]");
634 if (p != NULL)
636 currentpoint = p + 1; /* Skip the newline following the start of the node */
637 selected_item = NULL;
638 help_callback (h, NULL, DLG_DRAW, 0, NULL);
642 static void
643 help_index (Dlg_head * h)
645 const char *new_item;
647 new_item = search_string (fdata, "[Contents]");
649 if (new_item == NULL)
650 message (D_ERROR, MSG_ERROR, _("Cannot find node %s in help file"), "[Contents]");
651 else
653 history_ptr = (history_ptr + 1) % HISTORY_SIZE;
654 history[history_ptr].page = currentpoint;
655 history[history_ptr].link = selected_item;
657 currentpoint = new_item + 1; /* Skip the newline following the start of the node */
658 selected_item = NULL;
659 help_callback (h, NULL, DLG_DRAW, 0, NULL);
663 static void
664 help_back (Dlg_head * h)
666 currentpoint = history[history_ptr].page;
667 selected_item = history[history_ptr].link;
668 history_ptr--;
669 if (history_ptr < 0)
670 history_ptr = HISTORY_SIZE - 1;
672 help_callback (h, NULL, DLG_DRAW, 0, NULL); /* FIXME: unneeded? */
675 static void
676 help_next_link (gboolean move_down)
678 const char *new_item;
680 new_item = select_next_link (selected_item);
681 if (new_item != NULL)
683 selected_item = new_item;
684 if ((int) (selected_item - last_shown) >= 0)
686 if (move_down)
687 move_forward (1);
688 else
689 selected_item = NULL;
692 else if (move_down)
693 move_forward (1);
694 else
695 selected_item = NULL;
698 static void
699 help_prev_link (gboolean move_up)
701 const char *new_item;
703 new_item = select_prev_link (selected_item);
704 selected_item = new_item;
705 if ((selected_item == NULL) || (selected_item < currentpoint))
707 if (move_up)
708 move_backward (1);
709 else if ((link_area != NULL) && (link_area->data != NULL))
710 selected_item = ((Link_Area *) link_area->data)->link_name;
711 else
712 selected_item = NULL;
716 static void
717 help_next_node (void)
719 const char *new_item;
721 new_item = currentpoint;
722 while ((*new_item != '\0') && (*new_item != CHAR_NODE_END))
723 new_item++;
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;
731 break;
735 static void
736 help_prev_node (void)
738 const char *new_item;
740 new_item = currentpoint;
741 while (((int) (new_item - fdata) > 1) && (*new_item != CHAR_NODE_END))
742 new_item--;
743 new_item--;
744 while (((int) (new_item - fdata) > 0) && (*new_item != CHAR_NODE_END))
745 new_item--;
746 while (*new_item != ']')
747 new_item++;
748 currentpoint = new_item + 2;
749 selected_item = NULL;
752 static void
753 help_select_link (void)
755 /* follow link */
756 if (selected_item == NULL)
758 #ifdef WE_WANT_TO_GO_BACKWARD_ON_KEY_RIGHT
759 /* Is there any reason why the right key would take us
760 * backward if there are no links selected?, I agree
761 * with Torben than doing nothing in this case is better
763 /* If there are no links, go backward in history */
764 history_ptr--;
765 if (history_ptr < 0)
766 history_ptr = HISTORY_SIZE - 1;
768 currentpoint = history[history_ptr].page;
769 selected_item = history[history_ptr].link;
770 #endif
772 else
774 history_ptr = (history_ptr + 1) % HISTORY_SIZE;
775 history[history_ptr].page = currentpoint;
776 history[history_ptr].link = selected_item;
777 currentpoint = help_follow_link (currentpoint, selected_item);
780 selected_item = NULL;
783 static cb_ret_t
784 help_execute_cmd (unsigned long command)
786 cb_ret_t ret = MSG_HANDLED;
788 switch (command)
790 case CK_HelpHelp:
791 help_help (whelp);
792 break;
793 case CK_HelpIndex:
794 help_index (whelp);
795 break;
796 case CK_HelpBack:
797 help_back (whelp);
798 break;
799 case CK_HelpMoveUp:
800 help_prev_link (TRUE);
801 break;
802 case CK_HelpMoveDown:
803 help_next_link (TRUE);
804 break;
805 case CK_HelpMovePgDn:
806 move_forward (help_lines - 1);
807 break;
808 case CK_HelpMovePgUp:
809 move_backward (help_lines - 1);
810 break;
811 case CK_HelpMoveHalfPgDn:
812 move_forward (help_lines / 2);
813 break;
814 case CK_HelpMoveHalfPgUp:
815 move_backward (help_lines / 2);
816 break;
817 case CK_HelpMoveTop:
818 move_to_top ();
819 break;
820 case CK_HelpMoveBottom:
821 move_to_bottom ();
822 break;
823 case CK_HelpSelectLink:
824 help_select_link ();
825 break;
826 case CK_HelpNextLink:
827 help_next_link (FALSE);
828 break;
829 case CK_HelpPrevLink:
830 help_prev_link (FALSE);
831 break;
832 case CK_HelpNextNode:
833 help_next_node ();
834 break;
835 case CK_HelpPrevNode:
836 help_prev_node ();
837 break;
838 case CK_HelpQuit:
839 dlg_stop (whelp);
840 break;
841 default:
842 ret = MSG_NOT_HANDLED;
845 return ret;
848 static cb_ret_t
849 help_handle_key (Dlg_head * h, int c)
851 unsigned long command;
853 command = lookup_keymap_command (help_map, c);
854 if ((command == CK_Ignore_Key) || (help_execute_cmd (command) == MSG_NOT_HANDLED))
855 return MSG_NOT_HANDLED;
857 help_callback (h, NULL, DLG_DRAW, 0, NULL);
858 return MSG_HANDLED;
861 static cb_ret_t
862 help_callback (Dlg_head * h, Widget * sender, dlg_msg_t msg, int parm, void *data)
864 WButtonBar *bb;
866 switch (msg)
868 case DLG_RESIZE:
869 help_lines = min (LINES - 4, max (2 * LINES / 3, 18));
870 dlg_set_size (h, help_lines + 4, HELP_WINDOW_WIDTH + 4);
871 bb = find_buttonbar (h);
872 widget_set_size (&bb->widget, LINES - 1, 0, 1, COLS);
873 return MSG_HANDLED;
875 case DLG_DRAW:
876 common_dialog_repaint (h);
877 help_show (h, currentpoint);
878 return MSG_HANDLED;
880 case DLG_KEY:
881 return help_handle_key (h, parm);
883 case DLG_ACTION:
884 /* command from buttonbar */
885 return help_execute_cmd (parm);
887 default:
888 return default_dlg_callback (h, sender, msg, parm, data);
892 static void
893 interactive_display_finish (void)
895 clear_link_areas ();
898 /* translate help file into terminal encoding */
899 static void
900 translate_file (char *filedata)
902 GIConv conv;
903 GString *translated_data;
905 /* initial allocation for largest whole help file */
906 translated_data = g_string_sized_new (32 * 1024);
908 conv = str_crt_conv_from ("UTF-8");
910 if (conv == INVALID_CONV)
911 g_string_free (translated_data, TRUE);
912 else
914 g_free (fdata);
916 if (str_convert (conv, filedata, translated_data) != ESTR_FAILURE)
917 fdata = g_string_free (translated_data, FALSE);
918 else
920 fdata = NULL;
921 g_string_free (translated_data, TRUE);
923 str_close_conv (conv);
927 static cb_ret_t
928 md_callback (Widget * w, widget_msg_t msg, int parm)
930 switch (msg)
932 case WIDGET_RESIZED:
933 w->lines = help_lines;
934 return MSG_HANDLED;
936 default:
937 return default_proc (msg, parm);
941 static Widget *
942 mousedispatch_new (int y, int x, int yl, int xl)
944 Widget *w = g_new (Widget, 1);
945 init_widget (w, y, x, yl, xl, md_callback, help_event);
946 return w;
949 void
950 interactive_display (const char *filename, const char *node)
952 const dlg_colors_t help_colors = {
953 HELP_NORMAL_COLOR, /* common text color */
954 0, /* unused in help */
955 HELP_BOLD_COLOR, /* bold text color */
956 0, /* unused in help */
957 HELP_TITLE_COLOR /* title color */
960 WButtonBar *help_bar;
961 Widget *md;
962 char *hlpfile = NULL;
963 char *filedata;
965 if (filename != NULL)
966 filedata = load_file (filename);
967 else
968 filedata = load_mc_home_file (mc_home, mc_home_alt, "mc.hlp", &hlpfile);
970 if (filedata == NULL)
971 message (D_ERROR, MSG_ERROR, _("Cannot open file %s\n%s"),
972 filename ? filename : hlpfile, unix_error_string (errno));
974 g_free (hlpfile);
976 if (filedata == NULL)
977 return;
979 translate_file (filedata);
981 g_free (filedata);
983 if (fdata == NULL)
984 return;
986 if ((node == NULL) || (*node == '\0'))
987 node = "[main]";
989 main_node = search_string (fdata, node);
991 if (main_node == NULL)
993 message (D_ERROR, MSG_ERROR, _("Cannot find node %s in help file"), node);
995 /* Fallback to [main], return if it also cannot be found */
996 main_node = search_string (fdata, "[main]");
997 if (main_node == NULL)
999 interactive_display_finish ();
1000 return;
1004 help_lines = min (LINES - 4, max (2 * LINES / 3, 18));
1006 whelp =
1007 create_dlg (TRUE, 0, 0, help_lines + 4, HELP_WINDOW_WIDTH + 4,
1008 help_colors, help_callback, "[Help]", _("Help"),
1009 DLG_TRYUP | DLG_CENTER | DLG_WANT_TAB);
1011 selected_item = search_string_node (main_node, STRING_LINK_START) - 1;
1012 currentpoint = main_node + 1; /* Skip the newline following the start of the node */
1014 for (history_ptr = HISTORY_SIZE; history_ptr;)
1016 history_ptr--;
1017 history[history_ptr].page = currentpoint;
1018 history[history_ptr].link = selected_item;
1021 help_bar = buttonbar_new (TRUE);
1022 help_bar->widget.y -= whelp->y;
1023 help_bar->widget.x -= whelp->x;
1025 md = mousedispatch_new (1, 1, help_lines, HELP_WINDOW_WIDTH - 2);
1027 add_widget (whelp, md);
1028 add_widget (whelp, help_bar);
1030 buttonbar_set_label (help_bar, 1, Q_ ("ButtonBar|Help"), help_map, NULL);
1031 buttonbar_set_label (help_bar, 2, Q_ ("ButtonBar|Index"), help_map, NULL);
1032 buttonbar_set_label (help_bar, 3, Q_ ("ButtonBar|Prev"), help_map, NULL);
1033 buttonbar_set_label (help_bar, 4, "", help_map, NULL);
1034 buttonbar_set_label (help_bar, 5, "", help_map, NULL);
1035 buttonbar_set_label (help_bar, 6, "", help_map, NULL);
1036 buttonbar_set_label (help_bar, 7, "", help_map, NULL);
1037 buttonbar_set_label (help_bar, 8, "", help_map, NULL);
1038 buttonbar_set_label (help_bar, 9, "", help_map, NULL);
1039 buttonbar_set_label (help_bar, 10, Q_ ("ButtonBar|Quit"), help_map, NULL);
1041 run_dlg (whelp);
1042 interactive_display_finish ();
1043 destroy_dlg (whelp);