Merge branch '3578_mcext_fixes'
[midnight-commander.git] / src / help.c
blob7dacdfa8418f501b046e5495191d0d14d0413205
1 /*
2 Hypertext file browser.
4 Copyright (C) 1994-2016
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/>.
24 /** \file help.c
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 faciliate
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 * Lazyness/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.
50 #include <config.h>
52 #include <errno.h>
53 #include <stdio.h>
54 #include <sys/types.h>
55 #include <sys/stat.h>
57 #include "lib/global.h"
59 #include "lib/tty/tty.h"
60 #include "lib/tty/mouse.h"
61 #include "lib/skin.h"
62 #include "lib/strutil.h"
63 #include "lib/fileloc.h"
64 #include "lib/util.h"
65 #include "lib/widget.h"
66 #include "lib/event-types.h"
68 #include "keybind-defaults.h"
69 #include "help.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
89 int x1, y1, x2, y2;
90 const char *link_name;
91 } Link_Area;
93 /*** file scope variables ************************************************************************/
95 static char *fdata = NULL; /* Pointer to the loaded data file */
96 static int help_lines; /* Lines in help viewer */
97 static int history_ptr; /* For the history queue */
98 static const char *main_node; /* The main node */
99 static const char *last_shown = NULL; /* Last byte shown in a screen */
100 static gboolean end_of_node = FALSE; /* Flag: the last character of the node shown? */
101 static const char *currentpoint;
102 static const char *selected_item;
104 /* The widget variables */
105 static WDialog *whelp;
107 static struct
109 const char *page; /* Pointer to the selected page */
110 const char *link; /* Pointer to the selected link */
111 } history[HISTORY_SIZE];
113 static GSList *link_area = NULL;
114 static gboolean inside_link_area = FALSE;
116 static cb_ret_t help_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data);
118 /*** file scope functions ************************************************************************/
119 /* --------------------------------------------------------------------------------------------- */
121 /** returns the position where text was found in the start buffer
122 * or 0 if not found
124 static const char *
125 search_string (const char *start, const char *text)
127 const char *result = NULL;
128 char *local_text = g_strdup (text);
129 char *d = local_text;
130 const char *e = start;
132 /* fmt sometimes replaces a space with a newline in the help file */
133 /* Replace the newlines in the link name with spaces to correct the situation */
134 while (*d != '\0')
136 if (*d == '\n')
137 *d = ' ';
138 str_next_char (&d);
141 /* Do search */
142 for (d = local_text; *e; e++)
144 if (*d == *e)
145 d++;
146 else
147 d = local_text;
148 if (*d == '\0')
150 result = e + 1;
151 break;
155 g_free (local_text);
156 return result;
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
165 static const char *
166 search_string_node (const char *start, const char *text)
168 const char *d = text;
169 const char *e = start;
171 if (start != NULL)
172 for (; *e && *e != CHAR_NODE_END; e++)
174 if (*d == *e)
175 d++;
176 else
177 d = text;
178 if (*d == '\0')
179 return e + 1;
182 return NULL;
185 /* --------------------------------------------------------------------------------------------- */
186 /** Searches the_char in the buffer pointer by start and searches
187 * it can search forward (direction = 1) or backward (direction = -1)
190 static const char *
191 search_char_node (const char *start, char the_char, int direction)
193 const char *e;
195 for (e = start; (*e != '\0') && (*e != CHAR_NODE_END); e += direction)
196 if (*e == the_char)
197 return e;
199 return NULL;
202 /* --------------------------------------------------------------------------------------------- */
203 /** Returns the new current pointer when moved lines lines */
205 static const char *
206 move_forward2 (const char *c, int lines)
208 const char *p;
209 int line;
211 currentpoint = c;
212 for (line = 0, p = currentpoint; (*p != '\0') && (*p != CHAR_NODE_END); str_cnext_char (&p))
214 if (line == lines)
215 return currentpoint = p;
217 if (*p == '\n')
218 line++;
220 return currentpoint = c;
223 /* --------------------------------------------------------------------------------------------- */
225 static const char *
226 move_backward2 (const char *c, int lines)
228 const char *p;
229 int line;
231 currentpoint = c;
232 for (line = 0, p = currentpoint; (*p != '\0') && ((int) (p - fdata) >= 0); str_cprev_char (&p))
234 if (*p == CHAR_NODE_END)
236 /* We reached the beginning of the node */
237 /* Skip the node headers */
238 while (*p != ']')
239 str_cnext_char (&p);
240 return currentpoint = p + 2; /* Skip the newline following the start of the node */
243 if (*(p - 1) == '\n')
244 line++;
245 if (line == lines)
246 return currentpoint = p;
248 return currentpoint = c;
251 /* --------------------------------------------------------------------------------------------- */
253 static void
254 move_forward (int i)
256 if (!end_of_node)
257 currentpoint = move_forward2 (currentpoint, i);
260 /* --------------------------------------------------------------------------------------------- */
262 static void
263 move_backward (int i)
265 currentpoint = move_backward2 (currentpoint, ++i);
268 /* --------------------------------------------------------------------------------------------- */
270 static void
271 move_to_top (void)
273 while (((int) (currentpoint > fdata) > 0) && (*currentpoint != CHAR_NODE_END))
274 currentpoint--;
276 while (*currentpoint != ']')
277 currentpoint++;
278 currentpoint = currentpoint + 2; /* Skip the newline following the start of the node */
279 selected_item = NULL;
282 /* --------------------------------------------------------------------------------------------- */
284 static void
285 move_to_bottom (void)
287 while ((*currentpoint != '\0') && (*currentpoint != CHAR_NODE_END))
288 currentpoint++;
289 currentpoint--;
290 move_backward (1);
293 /* --------------------------------------------------------------------------------------------- */
295 static const char *
296 help_follow_link (const char *start, const char *lc_selected_item)
298 const char *p;
300 if (lc_selected_item == NULL)
301 return start;
303 for (p = lc_selected_item; *p && *p != CHAR_NODE_END && *p != CHAR_LINK_POINTER; p++)
305 if (*p == CHAR_LINK_POINTER)
307 int i;
308 char link_name[MAXLINKNAME];
310 link_name[0] = '[';
311 for (i = 1; *p != CHAR_LINK_END && *p && *p != CHAR_NODE_END && i < MAXLINKNAME - 3;)
312 link_name[i++] = *++p;
313 link_name[i - 1] = ']';
314 link_name[i] = '\0';
315 p = search_string (fdata, link_name);
316 if (p != NULL)
318 p += 1; /* Skip the newline following the start of the node */
319 return p;
323 /* Create a replacement page with the error message */
324 return _("Help file format error\n");
327 /* --------------------------------------------------------------------------------------------- */
329 static const char *
330 select_next_link (const char *current_link)
332 const char *p;
334 if (current_link == NULL)
335 return NULL;
337 p = search_string_node (current_link, STRING_LINK_END);
338 if (p == NULL)
339 return NULL;
340 p = search_string_node (p, STRING_LINK_START);
341 if (p == NULL)
342 return NULL;
343 return p - 1;
346 /* --------------------------------------------------------------------------------------------- */
348 static const char *
349 select_prev_link (const char *current_link)
351 return current_link == NULL ? NULL : search_char_node (current_link - 1, CHAR_LINK_START, -1);
354 /* --------------------------------------------------------------------------------------------- */
356 static void
357 start_link_area (int x, int y, const char *link_name)
359 Link_Area *la;
361 if (inside_link_area)
362 message (D_NORMAL, _("Warning"), _("Internal bug: Double start of link area"));
364 /* Allocate memory for a new link area */
365 la = g_new (Link_Area, 1);
366 /* Save the beginning coordinates of the link area */
367 la->x1 = x;
368 la->y1 = y;
369 /* Save the name of the destination anchor */
370 la->link_name = link_name;
371 link_area = g_slist_prepend (link_area, la);
373 inside_link_area = TRUE;
376 /* --------------------------------------------------------------------------------------------- */
378 static void
379 end_link_area (int x, int y)
381 if (inside_link_area)
383 Link_Area *la = (Link_Area *) link_area->data;
384 /* Save the end coordinates of the link area */
385 la->x2 = x;
386 la->y2 = y;
387 inside_link_area = FALSE;
391 /* --------------------------------------------------------------------------------------------- */
393 static void
394 clear_link_areas (void)
396 g_slist_free_full (link_area, g_free);
397 link_area = NULL;
398 inside_link_area = FALSE;
401 /* --------------------------------------------------------------------------------------------- */
403 static void
404 help_print_word (WDialog * h, GString * word, int *col, int *line, gboolean add_space)
406 if (*line >= help_lines)
407 g_string_set_size (word, 0);
408 else
410 int w;
412 w = str_term_width1 (word->str);
413 if (*col + w >= HELP_WINDOW_WIDTH)
415 *col = 0;
416 (*line)++;
419 if (*line >= help_lines)
420 g_string_set_size (word, 0);
421 else
423 widget_move (h, *line + 2, *col + 2);
424 tty_print_string (word->str);
425 g_string_set_size (word, 0);
426 *col += w;
430 if (add_space)
432 if (*col < HELP_WINDOW_WIDTH - 1)
434 tty_print_char (' ');
435 (*col)++;
437 else
439 *col = 0;
440 (*line)++;
445 /* --------------------------------------------------------------------------------------------- */
447 static void
448 help_show (WDialog * h, const char *paint_start)
450 const char *p, *n;
451 int col, line, c;
452 gboolean painting = TRUE;
453 gboolean acs; /* Flag: Alternate character set active? */
454 gboolean repeat_paint;
455 int active_col, active_line; /* Active link position */
456 char buff[MB_LEN_MAX + 1];
457 GString *word;
459 word = g_string_sized_new (32);
461 tty_setcolor (HELP_NORMAL_COLOR);
464 line = col = active_col = active_line = 0;
465 repeat_paint = FALSE;
466 acs = FALSE;
468 clear_link_areas ();
469 if ((int) (selected_item - paint_start) < 0)
470 selected_item = NULL;
472 p = paint_start;
473 n = paint_start;
474 while ((n[0] != '\0') && (n[0] != CHAR_NODE_END) && (line < help_lines))
476 p = n;
477 n = str_cget_next_char (p);
478 memcpy (buff, p, n - p);
479 buff[n - p] = '\0';
481 c = (unsigned char) buff[0];
482 switch (c)
484 case CHAR_LINK_START:
485 if (selected_item == NULL)
486 selected_item = p;
487 if (p != selected_item)
488 tty_setcolor (HELP_LINK_COLOR);
489 else
491 tty_setcolor (HELP_SLINK_COLOR);
493 /* Store the coordinates of the link */
494 active_col = col + 2;
495 active_line = line + 2;
497 start_link_area (col, line, p);
498 break;
499 case CHAR_LINK_POINTER:
500 painting = FALSE;
501 break;
502 case CHAR_LINK_END:
503 painting = TRUE;
504 help_print_word (h, word, &col, &line, FALSE);
505 end_link_area (col - 1, line);
506 tty_setcolor (HELP_NORMAL_COLOR);
507 break;
508 case CHAR_ALTERNATE:
509 acs = TRUE;
510 break;
511 case CHAR_NORMAL:
512 acs = FALSE;
513 break;
514 case CHAR_VERSION:
515 widget_move (h, line + 2, col + 2);
516 tty_print_string (VERSION);
517 col += str_term_width1 (VERSION);
518 break;
519 case CHAR_FONT_BOLD:
520 tty_setcolor (HELP_BOLD_COLOR);
521 break;
522 case CHAR_FONT_ITALIC:
523 tty_setcolor (HELP_ITALIC_COLOR);
524 break;
525 case CHAR_FONT_NORMAL:
526 help_print_word (h, word, &col, &line, FALSE);
527 tty_setcolor (HELP_NORMAL_COLOR);
528 break;
529 case '\n':
530 if (painting)
531 help_print_word (h, word, &col, &line, FALSE);
532 line++;
533 col = 0;
534 break;
535 case '\t':
536 col = (col / 8 + 1) * 8;
537 if (col >= HELP_WINDOW_WIDTH)
539 line++;
540 col = 8;
542 break;
543 case ' ':
544 /* word delimiter */
545 if (painting)
546 help_print_word (h, word, &col, &line, TRUE);
547 break;
548 default:
549 if (painting && (line < help_lines))
551 if (!acs)
552 /* accumulate symbols in a word */
553 g_string_append (word, buff);
554 else if (col < HELP_WINDOW_WIDTH)
556 widget_move (h, line + 2, col + 2);
558 if ((c == ' ') || (c == '.'))
559 tty_print_char (c);
560 else
561 #ifndef HAVE_SLANG
562 tty_print_char (acs_map[c]);
563 #else
564 SLsmg_draw_object (WIDGET (h)->y + line + 2, WIDGET (h)->x + col + 2,
566 #endif
567 col++;
573 /* print last word */
574 if (n[0] == CHAR_NODE_END)
575 help_print_word (h, word, &col, &line, FALSE);
577 last_shown = p;
578 end_of_node = line < help_lines;
579 tty_setcolor (HELP_NORMAL_COLOR);
580 if ((int) (selected_item - last_shown) >= 0)
582 if ((link_area == NULL) || (link_area->data == NULL))
583 selected_item = NULL;
584 else
586 selected_item = ((Link_Area *) link_area->data)->link_name;
587 repeat_paint = TRUE;
591 while (repeat_paint);
593 g_string_free (word, TRUE);
595 /* Position the cursor over a nice link */
596 if (active_col)
597 widget_move (h, active_line, active_col);
600 /* --------------------------------------------------------------------------------------------- */
602 static int
603 help_event (Gpm_Event * event, void *vp)
605 Widget *w = WIDGET (vp);
606 GSList *current_area;
607 Gpm_Event local;
609 if (!mouse_global_in_widget (event, w))
610 return MOU_UNHANDLED;
612 if ((event->type & GPM_UP) == 0)
613 return MOU_NORMAL;
615 local = mouse_get_local (event, w);
617 /* The event is relative to the dialog window, adjust it: */
618 local.x -= 2;
619 local.y -= 2;
621 if ((local.buttons & GPM_B_RIGHT) != 0)
623 currentpoint = history[history_ptr].page;
624 selected_item = history[history_ptr].link;
625 history_ptr--;
626 if (history_ptr < 0)
627 history_ptr = HISTORY_SIZE - 1;
629 send_message (w->owner, NULL, MSG_DRAW, 0, NULL);
630 return MOU_NORMAL;
633 /* Test whether the mouse click is inside one of the link areas */
634 for (current_area = link_area; current_area != NULL; current_area = g_slist_next (current_area))
636 Link_Area *la = (Link_Area *) current_area->data;
638 /* Test one line link area */
639 if (local.y == la->y1 && local.x >= la->x1 && local.y == la->y2 && local.x <= la->x2)
640 break;
642 /* Test two line link area */
643 if (la->y1 + 1 == la->y2)
645 /* The first line */
646 if (local.y == la->y1 && local.x >= la->x1)
647 break;
648 /* The second line */
649 if (local.y == la->y2 && local.x <= la->x2)
650 break;
652 /* Mouse will not work with link areas of more than two lines */
655 /* Test whether a link area was found */
656 if (current_area != NULL)
658 Link_Area *la = (Link_Area *) current_area->data;
660 /* The click was inside a link area -> follow the link */
661 history_ptr = (history_ptr + 1) % HISTORY_SIZE;
662 history[history_ptr].page = currentpoint;
663 history[history_ptr].link = la->link_name;
664 currentpoint = help_follow_link (currentpoint, la->link_name);
665 selected_item = NULL;
667 else if (local.y < 0)
668 move_backward (help_lines - 1);
669 else if (local.y >= help_lines)
670 move_forward (help_lines - 1);
671 else if (local.y < help_lines / 2)
672 move_backward (1);
673 else
674 move_forward (1);
676 /* Show the new node */
677 send_message (w->owner, NULL, MSG_DRAW, 0, NULL);
679 return MOU_NORMAL;
682 /* --------------------------------------------------------------------------------------------- */
683 /** show help */
685 static void
686 help_help (WDialog * h)
688 const char *p;
690 history_ptr = (history_ptr + 1) % HISTORY_SIZE;
691 history[history_ptr].page = currentpoint;
692 history[history_ptr].link = selected_item;
694 p = search_string (fdata, "[How to use help]");
695 if (p != NULL)
697 currentpoint = p + 1; /* Skip the newline following the start of the node */
698 selected_item = NULL;
699 send_message (h, NULL, MSG_DRAW, 0, NULL);
703 /* --------------------------------------------------------------------------------------------- */
705 static void
706 help_index (WDialog * h)
708 const char *new_item;
710 new_item = search_string (fdata, "[Contents]");
712 if (new_item == NULL)
713 message (D_ERROR, MSG_ERROR, _("Cannot find node %s in help file"), "[Contents]");
714 else
716 history_ptr = (history_ptr + 1) % HISTORY_SIZE;
717 history[history_ptr].page = currentpoint;
718 history[history_ptr].link = selected_item;
720 currentpoint = new_item + 1; /* Skip the newline following the start of the node */
721 selected_item = NULL;
722 send_message (h, NULL, MSG_DRAW, 0, NULL);
726 /* --------------------------------------------------------------------------------------------- */
728 static void
729 help_back (WDialog * h)
731 currentpoint = history[history_ptr].page;
732 selected_item = history[history_ptr].link;
733 history_ptr--;
734 if (history_ptr < 0)
735 history_ptr = HISTORY_SIZE - 1;
737 send_message (h, NULL, MSG_DRAW, 0, NULL); /* FIXME: unneeded? */
740 /* --------------------------------------------------------------------------------------------- */
742 static void
743 help_next_link (gboolean move_down)
745 const char *new_item;
747 new_item = select_next_link (selected_item);
748 if (new_item != NULL)
750 selected_item = new_item;
751 if ((int) (selected_item - last_shown) >= 0)
753 if (move_down)
754 move_forward (1);
755 else
756 selected_item = NULL;
759 else if (move_down)
760 move_forward (1);
761 else
762 selected_item = NULL;
765 /* --------------------------------------------------------------------------------------------- */
767 static void
768 help_prev_link (gboolean move_up)
770 const char *new_item;
772 new_item = select_prev_link (selected_item);
773 selected_item = new_item;
774 if ((selected_item == NULL) || (selected_item < currentpoint))
776 if (move_up)
777 move_backward (1);
778 else if ((link_area != NULL) && (link_area->data != NULL))
779 selected_item = ((Link_Area *) link_area->data)->link_name;
780 else
781 selected_item = NULL;
785 /* --------------------------------------------------------------------------------------------- */
787 static void
788 help_next_node (void)
790 const char *new_item;
792 new_item = currentpoint;
793 while ((*new_item != '\0') && (*new_item != CHAR_NODE_END))
794 new_item++;
796 if (*++new_item == '[')
797 while (*++new_item != '\0')
798 if ((*new_item == ']') && (*++new_item != '\0') && (*++new_item != '\0'))
800 currentpoint = new_item;
801 selected_item = NULL;
802 break;
806 /* --------------------------------------------------------------------------------------------- */
808 static void
809 help_prev_node (void)
811 const char *new_item;
813 new_item = currentpoint;
814 while (((int) (new_item - fdata) > 1) && (*new_item != CHAR_NODE_END))
815 new_item--;
816 new_item--;
817 while (((int) (new_item - fdata) > 0) && (*new_item != CHAR_NODE_END))
818 new_item--;
819 while (*new_item != ']')
820 new_item++;
821 currentpoint = new_item + 2;
822 selected_item = NULL;
825 /* --------------------------------------------------------------------------------------------- */
827 static void
828 help_select_link (void)
830 /* follow link */
831 if (selected_item == NULL)
833 #ifdef WE_WANT_TO_GO_BACKWARD_ON_KEY_RIGHT
834 /* Is there any reason why the right key would take us
835 * backward if there are no links selected?, I agree
836 * with Torben than doing nothing in this case is better
838 /* If there are no links, go backward in history */
839 history_ptr--;
840 if (history_ptr < 0)
841 history_ptr = HISTORY_SIZE - 1;
843 currentpoint = history[history_ptr].page;
844 selected_item = history[history_ptr].link;
845 #endif
847 else
849 history_ptr = (history_ptr + 1) % HISTORY_SIZE;
850 history[history_ptr].page = currentpoint;
851 history[history_ptr].link = selected_item;
852 currentpoint = help_follow_link (currentpoint, selected_item);
855 selected_item = NULL;
858 /* --------------------------------------------------------------------------------------------- */
860 static cb_ret_t
861 help_execute_cmd (long command)
863 cb_ret_t ret = MSG_HANDLED;
865 switch (command)
867 case CK_Help:
868 help_help (whelp);
869 break;
870 case CK_Index:
871 help_index (whelp);
872 break;
873 case CK_Back:
874 help_back (whelp);
875 break;
876 case CK_Up:
877 help_prev_link (TRUE);
878 break;
879 case CK_Down:
880 help_next_link (TRUE);
881 break;
882 case CK_PageDown:
883 move_forward (help_lines - 1);
884 break;
885 case CK_PageUp:
886 move_backward (help_lines - 1);
887 break;
888 case CK_HalfPageDown:
889 move_forward (help_lines / 2);
890 break;
891 case CK_HalfPageUp:
892 move_backward (help_lines / 2);
893 break;
894 case CK_Top:
895 move_to_top ();
896 break;
897 case CK_Bottom:
898 move_to_bottom ();
899 break;
900 case CK_Enter:
901 help_select_link ();
902 break;
903 case CK_LinkNext:
904 help_next_link (FALSE);
905 break;
906 case CK_LinkPrev:
907 help_prev_link (FALSE);
908 break;
909 case CK_NodeNext:
910 help_next_node ();
911 break;
912 case CK_NodePrev:
913 help_prev_node ();
914 break;
915 case CK_Quit:
916 dlg_stop (whelp);
917 break;
918 default:
919 ret = MSG_NOT_HANDLED;
922 return ret;
925 /* --------------------------------------------------------------------------------------------- */
927 static cb_ret_t
928 help_handle_key (WDialog * h, int c)
930 long command;
932 command = keybind_lookup_keymap_command (help_map, c);
933 if ((command == CK_IgnoreKey) || (help_execute_cmd (command) == MSG_NOT_HANDLED))
934 return MSG_NOT_HANDLED;
936 send_message (h, NULL, MSG_DRAW, 0, NULL);
937 return MSG_HANDLED;
940 /* --------------------------------------------------------------------------------------------- */
942 static cb_ret_t
943 help_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data)
945 WDialog *h = DIALOG (w);
947 switch (msg)
949 case MSG_RESIZE:
951 WButtonBar *bb;
953 help_lines = min (LINES - 4, max (2 * LINES / 3, 18));
954 dlg_set_size (h, help_lines + 4, HELP_WINDOW_WIDTH + 4);
955 bb = find_buttonbar (h);
956 widget_set_size (WIDGET (bb), LINES - 1, 0, 1, COLS);
957 return MSG_HANDLED;
960 case MSG_DRAW:
961 dlg_default_repaint (h);
962 help_show (h, currentpoint);
963 return MSG_HANDLED;
965 case MSG_KEY:
966 return help_handle_key (h, parm);
968 case MSG_ACTION:
969 /* shortcut */
970 if (sender == NULL)
971 return help_execute_cmd (parm);
972 /* message from buttonbar */
973 if (sender == WIDGET (find_buttonbar (h)))
975 if (data != NULL)
976 return send_message (data, NULL, MSG_ACTION, parm, NULL);
977 return help_execute_cmd (parm);
979 return MSG_NOT_HANDLED;
981 default:
982 return dlg_default_callback (w, sender, msg, parm, data);
986 /* --------------------------------------------------------------------------------------------- */
988 static void
989 interactive_display_finish (void)
991 clear_link_areas ();
994 /* --------------------------------------------------------------------------------------------- */
995 /** translate help file into terminal encoding */
997 static void
998 translate_file (char *filedata)
1000 GIConv conv;
1001 GString *translated_data;
1003 /* initial allocation for largest whole help file */
1004 translated_data = g_string_sized_new (32 * 1024);
1006 conv = str_crt_conv_from ("UTF-8");
1008 if (conv == INVALID_CONV)
1009 g_string_free (translated_data, TRUE);
1010 else
1012 g_free (fdata);
1014 if (str_convert (conv, filedata, translated_data) != ESTR_FAILURE)
1015 fdata = g_string_free (translated_data, FALSE);
1016 else
1018 fdata = NULL;
1019 g_string_free (translated_data, TRUE);
1021 str_close_conv (conv);
1025 /* --------------------------------------------------------------------------------------------- */
1027 static cb_ret_t
1028 md_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data)
1030 switch (msg)
1032 case MSG_RESIZE:
1033 w->lines = help_lines;
1034 return MSG_HANDLED;
1036 default:
1037 return widget_default_callback (w, sender, msg, parm, data);
1041 /* --------------------------------------------------------------------------------------------- */
1043 static Widget *
1044 mousedispatch_new (int y, int x, int yl, int xl)
1046 Widget *w;
1048 w = g_new (Widget, 1);
1049 widget_init (w, y, x, yl, xl, md_callback, help_event);
1050 return w;
1053 /* --------------------------------------------------------------------------------------------- */
1054 /*** public functions ****************************************************************************/
1055 /* --------------------------------------------------------------------------------------------- */
1057 /* event callback */
1058 gboolean
1059 help_interactive_display (const gchar * event_group_name, const gchar * event_name,
1060 gpointer init_data, gpointer data)
1062 const dlg_colors_t help_colors = {
1063 HELP_NORMAL_COLOR, /* common text color */
1064 0, /* unused in help */
1065 HELP_BOLD_COLOR, /* bold text color */
1066 0, /* unused in help */
1067 HELP_TITLE_COLOR /* title color */
1070 WButtonBar *help_bar;
1071 Widget *md;
1072 char *hlpfile = NULL;
1073 char *filedata;
1074 ev_help_t *event_data = (ev_help_t *) data;
1076 (void) event_group_name;
1077 (void) event_name;
1078 (void) init_data;
1080 if (event_data->filename != NULL)
1081 g_file_get_contents (event_data->filename, &filedata, NULL, NULL);
1082 else
1083 filedata = load_mc_home_file (mc_global.share_data_dir, MC_HELP, &hlpfile);
1085 if (filedata == NULL)
1086 message (D_ERROR, MSG_ERROR, _("Cannot open file %s\n%s"),
1087 event_data->filename ? event_data->filename : hlpfile, unix_error_string (errno));
1089 g_free (hlpfile);
1091 if (filedata == NULL)
1092 return TRUE;
1094 translate_file (filedata);
1096 g_free (filedata);
1098 if (fdata == NULL)
1099 return TRUE;
1101 if ((event_data->node == NULL) || (*event_data->node == '\0'))
1102 event_data->node = "[main]";
1104 main_node = search_string (fdata, event_data->node);
1106 if (main_node == NULL)
1108 message (D_ERROR, MSG_ERROR, _("Cannot find node %s in help file"), event_data->node);
1110 /* Fallback to [main], return if it also cannot be found */
1111 main_node = search_string (fdata, "[main]");
1112 if (main_node == NULL)
1114 interactive_display_finish ();
1115 return TRUE;
1119 help_lines = min (LINES - 4, max (2 * LINES / 3, 18));
1121 whelp =
1122 dlg_create (TRUE, 0, 0, help_lines + 4, HELP_WINDOW_WIDTH + 4,
1123 help_colors, help_callback, NULL, "[Help]", _("Help"),
1124 DLG_TRYUP | DLG_CENTER | DLG_WANT_TAB);
1126 selected_item = search_string_node (main_node, STRING_LINK_START) - 1;
1127 currentpoint = main_node + 1; /* Skip the newline following the start of the node */
1129 for (history_ptr = HISTORY_SIZE; history_ptr;)
1131 history_ptr--;
1132 history[history_ptr].page = currentpoint;
1133 history[history_ptr].link = selected_item;
1136 help_bar = buttonbar_new (TRUE);
1137 WIDGET (help_bar)->y -= WIDGET (whelp)->y;
1138 WIDGET (help_bar)->x -= WIDGET (whelp)->x;
1140 md = mousedispatch_new (1, 1, help_lines, HELP_WINDOW_WIDTH - 2);
1142 add_widget (whelp, md);
1143 add_widget (whelp, help_bar);
1145 buttonbar_set_label (help_bar, 1, Q_ ("ButtonBar|Help"), help_map, NULL);
1146 buttonbar_set_label (help_bar, 2, Q_ ("ButtonBar|Index"), help_map, NULL);
1147 buttonbar_set_label (help_bar, 3, Q_ ("ButtonBar|Prev"), help_map, NULL);
1148 buttonbar_set_label (help_bar, 4, "", help_map, NULL);
1149 buttonbar_set_label (help_bar, 5, "", help_map, NULL);
1150 buttonbar_set_label (help_bar, 6, "", help_map, NULL);
1151 buttonbar_set_label (help_bar, 7, "", help_map, NULL);
1152 buttonbar_set_label (help_bar, 8, "", help_map, NULL);
1153 buttonbar_set_label (help_bar, 9, "", help_map, NULL);
1154 buttonbar_set_label (help_bar, 10, Q_ ("ButtonBar|Quit"), help_map, NULL);
1156 dlg_run (whelp);
1157 interactive_display_finish ();
1158 dlg_destroy (whelp);
1159 return TRUE;
1162 /* --------------------------------------------------------------------------------------------- */