Merge branch '2503_compute_totals'
[midnight-commander.git] / src / help.c
blob418290ac740b0ba1646b26df56da5997e2d398ef
1 /*
2 Hypertext file browser.
4 Copyright (C) 1994, 1995, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
5 2005, 2006, 2007, 2011
6 The Free Software Foundation, Inc.
8 This file is part of the Midnight Commander.
10 The Midnight Commander is free software: you can redistribute it
11 and/or modify it under the terms of the GNU General Public License as
12 published by the Free Software Foundation, either version 3 of the License,
13 or (at your option) any later version.
15 The Midnight Commander is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program. If not, see <http://www.gnu.org/licenses/>.
25 /** \file help.c
26 * \brief Source: hypertext file browser
28 * Implements the hypertext file viewer.
29 * The hypertext file is a file that may have one or more nodes. Each
30 * node ends with a ^D character and starts with a bracket, then the
31 * name of the node and then a closing bracket. Right after the closing
32 * bracket a newline is placed. This newline is not to be displayed by
33 * the help viewer and must be skipped - its sole purpose is to faciliate
34 * the work of the people managing the help file template (xnc.hlp) .
36 * Links in the hypertext file are specified like this: the text that
37 * will be highlighted should have a leading ^A, then it comes the
38 * text, then a ^B indicating that highlighting is done, then the name
39 * of the node you want to link to and then a ^C.
41 * The file must contain a ^D at the beginning and at the end of the
42 * file or the program will not be able to detect the end of file.
44 * Lazyness/widgeting attack: This file does use the dialog manager
45 * and uses mainly the dialog to achieve the help work. there is only
46 * one specialized widget and it's only used to forward the mouse messages
47 * to the appropiate routine.
51 #include <config.h>
53 #include <errno.h>
54 #include <stdio.h>
55 #include <sys/types.h>
56 #include <sys/stat.h>
58 #include "lib/global.h"
60 #include "lib/tty/tty.h"
61 #include "lib/tty/mouse.h"
62 #include "lib/skin.h"
63 #include "lib/strutil.h"
64 #include "lib/fileloc.h"
65 #include "lib/util.h"
66 #include "lib/widget.h"
67 #include "lib/event-types.h"
69 #include "keybind-defaults.h"
70 #include "keybind-defaults.h"
71 #include "help.h"
72 #include "main.h"
74 /*** global variables ****************************************************************************/
76 /*** file scope macro definitions ****************************************************************/
78 #define MAXLINKNAME 80
79 #define HISTORY_SIZE 20
80 #define HELP_WINDOW_WIDTH min(80, COLS - 16)
82 #define STRING_LINK_START "\01"
83 #define STRING_LINK_POINTER "\02"
84 #define STRING_LINK_END "\03"
85 #define STRING_NODE_END "\04"
87 /*** file scope type declarations ****************************************************************/
89 /* Link areas for the mouse */
90 typedef struct Link_Area
92 int x1, y1, x2, y2;
93 const char *link_name;
94 } Link_Area;
96 /*** file scope variables ************************************************************************/
98 static char *fdata = NULL; /* Pointer to the loaded data file */
99 static int help_lines; /* Lines in help viewer */
100 static int history_ptr; /* For the history queue */
101 static const char *main_node; /* The main node */
102 static const char *last_shown = NULL; /* Last byte shown in a screen */
103 static gboolean end_of_node = FALSE; /* Flag: the last character of the node shown? */
104 static const char *currentpoint;
105 static const char *selected_item;
107 /* The widget variables */
108 static Dlg_head *whelp;
110 static struct
112 const char *page; /* Pointer to the selected page */
113 const char *link; /* Pointer to the selected link */
114 } history[HISTORY_SIZE];
116 static GSList *link_area = NULL;
117 static gboolean inside_link_area = FALSE;
119 static cb_ret_t help_callback (Dlg_head * h, Widget * sender, dlg_msg_t msg, int parm, void *data);
121 /*** file scope functions ************************************************************************/
122 /* --------------------------------------------------------------------------------------------- */
124 /** returns the position where text was found in the start buffer
125 * or 0 if not found
127 static const char *
128 search_string (const char *start, const char *text)
130 const char *result = NULL;
131 char *local_text = g_strdup (text);
132 char *d = local_text;
133 const char *e = start;
135 /* fmt sometimes replaces a space with a newline in the help file */
136 /* Replace the newlines in the link name with spaces to correct the situation */
137 while (*d != '\0')
139 if (*d == '\n')
140 *d = ' ';
141 str_next_char (&d);
144 /* Do search */
145 for (d = local_text; *e; e++)
147 if (*d == *e)
148 d++;
149 else
150 d = local_text;
151 if (*d == '\0')
153 result = e + 1;
154 break;
158 g_free (local_text);
159 return result;
162 /* --------------------------------------------------------------------------------------------- */
163 /** Searches text in the buffer pointed by start. Search ends
164 * if the CHAR_NODE_END is found in the text.
165 * @returns 0 on failure
168 static const char *
169 search_string_node (const char *start, const char *text)
171 const char *d = text;
172 const char *e = start;
174 if (start != NULL)
175 for (; *e && *e != CHAR_NODE_END; e++)
177 if (*d == *e)
178 d++;
179 else
180 d = text;
181 if (*d == '\0')
182 return e + 1;
185 return NULL;
188 /* --------------------------------------------------------------------------------------------- */
189 /** Searches the_char in the buffer pointer by start and searches
190 * it can search forward (direction = 1) or backward (direction = -1)
193 static const char *
194 search_char_node (const char *start, char the_char, int direction)
196 const char *e;
198 for (e = start; (*e != '\0') && (*e != CHAR_NODE_END); e += direction)
199 if (*e == the_char)
200 return e;
202 return NULL;
205 /* --------------------------------------------------------------------------------------------- */
206 /** Returns the new current pointer when moved lines lines */
208 static const char *
209 move_forward2 (const char *c, int lines)
211 const char *p;
212 int line;
214 currentpoint = c;
215 for (line = 0, p = currentpoint; (*p != '\0') && (*p != CHAR_NODE_END); str_cnext_char (&p))
217 if (line == lines)
218 return currentpoint = p;
220 if (*p == '\n')
221 line++;
223 return currentpoint = c;
226 /* --------------------------------------------------------------------------------------------- */
228 static const char *
229 move_backward2 (const char *c, int lines)
231 const char *p;
232 int line;
234 currentpoint = c;
235 for (line = 0, p = currentpoint; (*p != '\0') && ((int) (p - fdata) >= 0); str_cprev_char (&p))
237 if (*p == CHAR_NODE_END)
239 /* We reached the beginning of the node */
240 /* Skip the node headers */
241 while (*p != ']')
242 str_cnext_char (&p);
243 return currentpoint = p + 2; /* Skip the newline following the start of the node */
246 if (*(p - 1) == '\n')
247 line++;
248 if (line == lines)
249 return currentpoint = p;
251 return currentpoint = c;
254 /* --------------------------------------------------------------------------------------------- */
256 static void
257 move_forward (int i)
259 if (!end_of_node)
260 currentpoint = move_forward2 (currentpoint, i);
263 /* --------------------------------------------------------------------------------------------- */
265 static void
266 move_backward (int i)
268 currentpoint = move_backward2 (currentpoint, ++i);
271 /* --------------------------------------------------------------------------------------------- */
273 static void
274 move_to_top (void)
276 while (((int) (currentpoint > fdata) > 0) && (*currentpoint != CHAR_NODE_END))
277 currentpoint--;
279 while (*currentpoint != ']')
280 currentpoint++;
281 currentpoint = currentpoint + 2; /* Skip the newline following the start of the node */
282 selected_item = NULL;
285 /* --------------------------------------------------------------------------------------------- */
287 static void
288 move_to_bottom (void)
290 while ((*currentpoint != '\0') && (*currentpoint != CHAR_NODE_END))
291 currentpoint++;
292 currentpoint--;
293 move_backward (1);
296 /* --------------------------------------------------------------------------------------------- */
298 static const char *
299 help_follow_link (const char *start, const char *lc_selected_item)
301 char link_name[MAXLINKNAME];
302 const char *p;
303 int i = 0;
305 if (lc_selected_item == NULL)
306 return start;
308 for (p = lc_selected_item; *p && *p != CHAR_NODE_END && *p != CHAR_LINK_POINTER; p++)
310 if (*p == CHAR_LINK_POINTER)
312 link_name[0] = '[';
313 for (i = 1; *p != CHAR_LINK_END && *p && *p != CHAR_NODE_END && i < MAXLINKNAME - 3;)
314 link_name[i++] = *++p;
315 link_name[i - 1] = ']';
316 link_name[i] = '\0';
317 p = search_string (fdata, link_name);
318 if (p != NULL)
320 p += 1; /* Skip the newline following the start of the node */
321 return p;
325 /* Create a replacement page with the error message */
326 return _("Help file format error\n");
329 /* --------------------------------------------------------------------------------------------- */
331 static const char *
332 select_next_link (const char *current_link)
334 const char *p;
336 if (current_link == NULL)
337 return NULL;
339 p = search_string_node (current_link, STRING_LINK_END);
340 if (p == NULL)
341 return NULL;
342 p = search_string_node (p, STRING_LINK_START);
343 if (p == NULL)
344 return NULL;
345 return p - 1;
348 /* --------------------------------------------------------------------------------------------- */
350 static const char *
351 select_prev_link (const char *current_link)
353 return current_link == NULL ? NULL : search_char_node (current_link - 1, CHAR_LINK_START, -1);
356 /* --------------------------------------------------------------------------------------------- */
358 static void
359 start_link_area (int x, int y, const char *link_name)
361 Link_Area *la;
363 if (inside_link_area)
364 message (D_NORMAL, _("Warning"), _("Internal bug: Double start of link area"));
366 /* Allocate memory for a new link area */
367 la = g_new (Link_Area, 1);
368 /* Save the beginning coordinates of the link area */
369 la->x1 = x;
370 la->y1 = y;
371 /* Save the name of the destination anchor */
372 la->link_name = link_name;
373 link_area = g_slist_prepend (link_area, la);
375 inside_link_area = TRUE;
378 /* --------------------------------------------------------------------------------------------- */
380 static void
381 end_link_area (int x, int y)
383 if (inside_link_area)
385 Link_Area *la = (Link_Area *) link_area->data;
386 /* Save the end coordinates of the link area */
387 la->x2 = x;
388 la->y2 = y;
389 inside_link_area = FALSE;
393 /* --------------------------------------------------------------------------------------------- */
395 static void
396 clear_link_areas (void)
398 g_slist_foreach (link_area, (GFunc) g_free, NULL);
399 g_slist_free (link_area);
400 link_area = NULL;
401 inside_link_area = FALSE;
404 /* --------------------------------------------------------------------------------------------- */
406 static void
407 help_print_word (Dlg_head * h, GString * word, int *col, int *line, gboolean add_space)
409 if (*line >= help_lines)
410 g_string_set_size (word, 0);
411 else
413 int w;
415 w = str_term_width1 (word->str);
416 if (*col + w >= HELP_WINDOW_WIDTH)
418 *col = 0;
419 (*line)++;
422 if (*line >= help_lines)
423 g_string_set_size (word, 0);
424 else
426 dlg_move (h, *line + 2, *col + 2);
427 tty_print_string (word->str);
428 g_string_set_size (word, 0);
429 *col += w;
433 if (add_space)
435 if (*col < HELP_WINDOW_WIDTH - 1)
437 tty_print_char (' ');
438 (*col)++;
440 else
442 *col = 0;
443 (*line)++;
448 /* --------------------------------------------------------------------------------------------- */
450 static void
451 help_show (Dlg_head * h, const char *paint_start)
453 const char *p, *n;
454 int col, line, c;
455 gboolean painting = TRUE;
456 gboolean acs; /* Flag: Alternate character set active? */
457 gboolean repeat_paint;
458 int active_col, active_line; /* Active link position */
459 char buff[MB_LEN_MAX + 1];
460 GString *word;
462 word = g_string_sized_new (32);
464 tty_setcolor (HELP_NORMAL_COLOR);
467 line = col = active_col = active_line = 0;
468 repeat_paint = FALSE;
469 acs = FALSE;
471 clear_link_areas ();
472 if ((int) (selected_item - paint_start) < 0)
473 selected_item = NULL;
475 p = paint_start;
476 n = paint_start;
477 while ((n[0] != '\0') && (n[0] != CHAR_NODE_END) && (line < help_lines))
479 p = n;
480 n = str_cget_next_char (p);
481 memcpy (buff, p, n - p);
482 buff[n - p] = '\0';
484 c = (unsigned char) buff[0];
485 switch (c)
487 case CHAR_LINK_START:
488 if (selected_item == NULL)
489 selected_item = p;
490 if (p != selected_item)
491 tty_setcolor (HELP_LINK_COLOR);
492 else
494 tty_setcolor (HELP_SLINK_COLOR);
496 /* Store the coordinates of the link */
497 active_col = col + 2;
498 active_line = line + 2;
500 start_link_area (col, line, p);
501 break;
502 case CHAR_LINK_POINTER:
503 painting = FALSE;
504 end_link_area (col - 1, line);
505 break;
506 case CHAR_LINK_END:
507 painting = TRUE;
508 help_print_word (h, word, &col, &line, FALSE);
509 tty_setcolor (HELP_NORMAL_COLOR);
510 break;
511 case CHAR_ALTERNATE:
512 acs = TRUE;
513 break;
514 case CHAR_NORMAL:
515 acs = FALSE;
516 break;
517 case CHAR_VERSION:
518 dlg_move (h, line + 2, col + 2);
519 tty_print_string (VERSION);
520 col += str_term_width1 (VERSION);
521 break;
522 case CHAR_FONT_BOLD:
523 tty_setcolor (HELP_BOLD_COLOR);
524 break;
525 case CHAR_FONT_ITALIC:
526 tty_setcolor (HELP_ITALIC_COLOR);
527 break;
528 case CHAR_FONT_NORMAL:
529 help_print_word (h, word, &col, &line, FALSE);
530 tty_setcolor (HELP_NORMAL_COLOR);
531 break;
532 case '\n':
533 if (painting)
534 help_print_word (h, word, &col, &line, FALSE);
535 line++;
536 col = 0;
537 break;
538 case '\t':
539 col = (col / 8 + 1) * 8;
540 if (col >= HELP_WINDOW_WIDTH)
542 line++;
543 col = 8;
545 break;
546 case ' ':
547 /* word delimeter */
548 if (painting)
549 help_print_word (h, word, &col, &line, TRUE);
550 break;
551 default:
552 if (painting && (line < help_lines))
554 if (!acs)
555 /* accumulate symbols in a word */
556 g_string_append (word, buff);
557 else if (col < HELP_WINDOW_WIDTH)
559 dlg_move (h, line + 2, col + 2);
561 if ((c == ' ') || (c == '.'))
562 tty_print_char (c);
563 else
564 #ifndef HAVE_SLANG
565 tty_print_char (acs_map[c]);
566 #else
567 SLsmg_draw_object (h->y + line + 2, h->x + col + 2, c);
568 #endif
569 col++;
575 /* print last word */
576 if (n[0] == CHAR_NODE_END)
577 help_print_word (h, word, &col, &line, FALSE);
579 last_shown = p;
580 end_of_node = line < help_lines;
581 tty_setcolor (HELP_NORMAL_COLOR);
582 if ((int) (selected_item - last_shown) >= 0)
584 if ((link_area == NULL) || (link_area->data == NULL))
585 selected_item = NULL;
586 else
588 selected_item = ((Link_Area *) link_area->data)->link_name;
589 repeat_paint = TRUE;
593 while (repeat_paint);
595 g_string_free (word, TRUE);
597 /* Position the cursor over a nice link */
598 if (active_col)
599 dlg_move (h, active_line, active_col);
602 /* --------------------------------------------------------------------------------------------- */
604 static int
605 help_event (Gpm_Event * event, void *vp)
607 Widget *w = vp;
608 GSList *current_area;
610 if ((event->type & GPM_UP) == 0)
611 return 0;
613 /* The event is relative to the dialog window, adjust it: */
614 event->x -= 2;
615 event->y -= 2;
617 if (event->buttons & GPM_B_RIGHT)
619 currentpoint = history[history_ptr].page;
620 selected_item = history[history_ptr].link;
621 history_ptr--;
622 if (history_ptr < 0)
623 history_ptr = HISTORY_SIZE - 1;
625 help_callback (w->owner, NULL, DLG_DRAW, 0, NULL);
626 return 0;
629 /* Test whether the mouse click is inside one of the link areas */
630 for (current_area = link_area; current_area != NULL; current_area = g_slist_next (current_area))
632 Link_Area *la = (Link_Area *) current_area->data;
633 /* Test one line link area */
634 if (event->y == la->y1 && event->x >= la->x1 && event->y == la->y2 && event->x <= la->x2)
635 break;
636 /* Test two line link area */
637 if (la->y1 + 1 == la->y2)
639 /* The first line */
640 if (event->y == la->y1 && event->x >= la->x1)
641 break;
642 /* The second line */
643 if (event->y == la->y2 && event->x <= la->x2)
644 break;
646 /* Mouse will not work with link areas of more than two lines */
649 /* Test whether a link area was found */
650 if (current_area != NULL)
652 Link_Area *la = (Link_Area *) current_area->data;
654 /* The click was inside a link area -> follow the link */
655 history_ptr = (history_ptr + 1) % HISTORY_SIZE;
656 history[history_ptr].page = currentpoint;
657 history[history_ptr].link = la->link_name;
658 currentpoint = help_follow_link (currentpoint, la->link_name);
659 selected_item = NULL;
661 else if (event->y < 0)
662 move_backward (help_lines - 1);
663 else if (event->y >= help_lines)
664 move_forward (help_lines - 1);
665 else if (event->y < help_lines / 2)
666 move_backward (1);
667 else
668 move_forward (1);
670 /* Show the new node */
671 help_callback (w->owner, NULL, DLG_DRAW, 0, NULL);
673 return 0;
676 /* --------------------------------------------------------------------------------------------- */
677 /** show help */
679 static void
680 help_help (Dlg_head * h)
682 const char *p;
684 history_ptr = (history_ptr + 1) % HISTORY_SIZE;
685 history[history_ptr].page = currentpoint;
686 history[history_ptr].link = selected_item;
688 p = search_string (fdata, "[How to use help]");
689 if (p != NULL)
691 currentpoint = p + 1; /* Skip the newline following the start of the node */
692 selected_item = NULL;
693 help_callback (h, NULL, DLG_DRAW, 0, NULL);
697 /* --------------------------------------------------------------------------------------------- */
699 static void
700 help_index (Dlg_head * h)
702 const char *new_item;
704 new_item = search_string (fdata, "[Contents]");
706 if (new_item == NULL)
707 message (D_ERROR, MSG_ERROR, _("Cannot find node %s in help file"), "[Contents]");
708 else
710 history_ptr = (history_ptr + 1) % HISTORY_SIZE;
711 history[history_ptr].page = currentpoint;
712 history[history_ptr].link = selected_item;
714 currentpoint = new_item + 1; /* Skip the newline following the start of the node */
715 selected_item = NULL;
716 help_callback (h, NULL, DLG_DRAW, 0, NULL);
720 /* --------------------------------------------------------------------------------------------- */
722 static void
723 help_back (Dlg_head * h)
725 currentpoint = history[history_ptr].page;
726 selected_item = history[history_ptr].link;
727 history_ptr--;
728 if (history_ptr < 0)
729 history_ptr = HISTORY_SIZE - 1;
731 help_callback (h, NULL, DLG_DRAW, 0, NULL); /* FIXME: unneeded? */
734 /* --------------------------------------------------------------------------------------------- */
736 static void
737 help_next_link (gboolean move_down)
739 const char *new_item;
741 new_item = select_next_link (selected_item);
742 if (new_item != NULL)
744 selected_item = new_item;
745 if ((int) (selected_item - last_shown) >= 0)
747 if (move_down)
748 move_forward (1);
749 else
750 selected_item = NULL;
753 else if (move_down)
754 move_forward (1);
755 else
756 selected_item = NULL;
759 /* --------------------------------------------------------------------------------------------- */
761 static void
762 help_prev_link (gboolean move_up)
764 const char *new_item;
766 new_item = select_prev_link (selected_item);
767 selected_item = new_item;
768 if ((selected_item == NULL) || (selected_item < currentpoint))
770 if (move_up)
771 move_backward (1);
772 else if ((link_area != NULL) && (link_area->data != NULL))
773 selected_item = ((Link_Area *) link_area->data)->link_name;
774 else
775 selected_item = NULL;
779 /* --------------------------------------------------------------------------------------------- */
781 static void
782 help_next_node (void)
784 const char *new_item;
786 new_item = currentpoint;
787 while ((*new_item != '\0') && (*new_item != CHAR_NODE_END))
788 new_item++;
790 if (*++new_item == '[')
791 while (*++new_item != '\0')
792 if ((*new_item == ']') && (*++new_item != '\0') && (*++new_item != '\0'))
794 currentpoint = new_item;
795 selected_item = NULL;
796 break;
800 /* --------------------------------------------------------------------------------------------- */
802 static void
803 help_prev_node (void)
805 const char *new_item;
807 new_item = currentpoint;
808 while (((int) (new_item - fdata) > 1) && (*new_item != CHAR_NODE_END))
809 new_item--;
810 new_item--;
811 while (((int) (new_item - fdata) > 0) && (*new_item != CHAR_NODE_END))
812 new_item--;
813 while (*new_item != ']')
814 new_item++;
815 currentpoint = new_item + 2;
816 selected_item = NULL;
819 /* --------------------------------------------------------------------------------------------- */
821 static void
822 help_select_link (void)
824 /* follow link */
825 if (selected_item == NULL)
827 #ifdef WE_WANT_TO_GO_BACKWARD_ON_KEY_RIGHT
828 /* Is there any reason why the right key would take us
829 * backward if there are no links selected?, I agree
830 * with Torben than doing nothing in this case is better
832 /* If there are no links, go backward in history */
833 history_ptr--;
834 if (history_ptr < 0)
835 history_ptr = HISTORY_SIZE - 1;
837 currentpoint = history[history_ptr].page;
838 selected_item = history[history_ptr].link;
839 #endif
841 else
843 history_ptr = (history_ptr + 1) % HISTORY_SIZE;
844 history[history_ptr].page = currentpoint;
845 history[history_ptr].link = selected_item;
846 currentpoint = help_follow_link (currentpoint, selected_item);
849 selected_item = NULL;
852 /* --------------------------------------------------------------------------------------------- */
854 static cb_ret_t
855 help_execute_cmd (unsigned long command)
857 cb_ret_t ret = MSG_HANDLED;
859 switch (command)
861 case CK_Help:
862 help_help (whelp);
863 break;
864 case CK_Index:
865 help_index (whelp);
866 break;
867 case CK_Back:
868 help_back (whelp);
869 break;
870 case CK_Up:
871 help_prev_link (TRUE);
872 break;
873 case CK_Down:
874 help_next_link (TRUE);
875 break;
876 case CK_PageDown:
877 move_forward (help_lines - 1);
878 break;
879 case CK_PageUp:
880 move_backward (help_lines - 1);
881 break;
882 case CK_HalfPageDown:
883 move_forward (help_lines / 2);
884 break;
885 case CK_HalfPageUp:
886 move_backward (help_lines / 2);
887 break;
888 case CK_Top:
889 move_to_top ();
890 break;
891 case CK_Bottom:
892 move_to_bottom ();
893 break;
894 case CK_Enter:
895 help_select_link ();
896 break;
897 case CK_LinkNext:
898 help_next_link (FALSE);
899 break;
900 case CK_LinkPrev:
901 help_prev_link (FALSE);
902 break;
903 case CK_NodeNext:
904 help_next_node ();
905 break;
906 case CK_NodePrev:
907 help_prev_node ();
908 break;
909 case CK_Quit:
910 dlg_stop (whelp);
911 break;
912 case CK_Cancel:
913 /* don't close help due to SIGINT */
914 break;
915 default:
916 ret = MSG_NOT_HANDLED;
919 return ret;
922 /* --------------------------------------------------------------------------------------------- */
924 static cb_ret_t
925 help_handle_key (Dlg_head * h, int c)
927 unsigned long command;
929 command = keybind_lookup_keymap_command (help_map, c);
930 if ((command == CK_IgnoreKey) || (help_execute_cmd (command) == MSG_NOT_HANDLED))
931 return MSG_NOT_HANDLED;
933 help_callback (h, NULL, DLG_DRAW, 0, NULL);
934 return MSG_HANDLED;
937 /* --------------------------------------------------------------------------------------------- */
939 static cb_ret_t
940 help_callback (Dlg_head * h, Widget * sender, dlg_msg_t msg, int parm, void *data)
942 WButtonBar *bb;
944 switch (msg)
946 case DLG_RESIZE:
947 help_lines = min (LINES - 4, max (2 * LINES / 3, 18));
948 dlg_set_size (h, help_lines + 4, HELP_WINDOW_WIDTH + 4);
949 bb = find_buttonbar (h);
950 widget_set_size (&bb->widget, LINES - 1, 0, 1, COLS);
951 return MSG_HANDLED;
953 case DLG_DRAW:
954 common_dialog_repaint (h);
955 help_show (h, currentpoint);
956 return MSG_HANDLED;
958 case DLG_KEY:
959 return help_handle_key (h, parm);
961 case DLG_ACTION:
962 /* shortcut */
963 if (sender == NULL)
964 return help_execute_cmd (parm);
965 /* message from buttonbar */
966 if (sender == (Widget *) find_buttonbar (h))
968 if (data != NULL)
969 return send_message ((Widget *) data, WIDGET_COMMAND, parm);
970 return help_execute_cmd (parm);
972 return MSG_NOT_HANDLED;
974 default:
975 return default_dlg_callback (h, sender, msg, parm, data);
979 /* --------------------------------------------------------------------------------------------- */
981 static void
982 interactive_display_finish (void)
984 clear_link_areas ();
987 /* --------------------------------------------------------------------------------------------- */
988 /** translate help file into terminal encoding */
990 static void
991 translate_file (char *filedata)
993 GIConv conv;
994 GString *translated_data;
996 /* initial allocation for largest whole help file */
997 translated_data = g_string_sized_new (32 * 1024);
999 conv = str_crt_conv_from ("UTF-8");
1001 if (conv == INVALID_CONV)
1002 g_string_free (translated_data, TRUE);
1003 else
1005 g_free (fdata);
1007 if (str_convert (conv, filedata, translated_data) != ESTR_FAILURE)
1008 fdata = g_string_free (translated_data, FALSE);
1009 else
1011 fdata = NULL;
1012 g_string_free (translated_data, TRUE);
1014 str_close_conv (conv);
1018 /* --------------------------------------------------------------------------------------------- */
1020 static cb_ret_t
1021 md_callback (Widget * w, widget_msg_t msg, int parm)
1023 switch (msg)
1025 case WIDGET_RESIZED:
1026 w->lines = help_lines;
1027 return MSG_HANDLED;
1029 default:
1030 return default_proc (msg, parm);
1034 /* --------------------------------------------------------------------------------------------- */
1036 static Widget *
1037 mousedispatch_new (int y, int x, int yl, int xl)
1039 Widget *w = g_new (Widget, 1);
1040 init_widget (w, y, x, yl, xl, md_callback, help_event);
1041 return w;
1044 /* --------------------------------------------------------------------------------------------- */
1045 /*** public functions ****************************************************************************/
1046 /* --------------------------------------------------------------------------------------------- */
1048 /* event callback */
1049 gboolean
1050 help_interactive_display (const gchar * event_group_name, const gchar * event_name,
1051 gpointer init_data, gpointer data)
1053 const dlg_colors_t help_colors = {
1054 HELP_NORMAL_COLOR, /* common text color */
1055 0, /* unused in help */
1056 HELP_BOLD_COLOR, /* bold text color */
1057 0, /* unused in help */
1058 HELP_TITLE_COLOR /* title color */
1061 WButtonBar *help_bar;
1062 Widget *md;
1063 char *hlpfile = NULL;
1064 char *filedata;
1065 ev_help_t *event_data = (ev_help_t *) data;
1067 (void) event_group_name;
1068 (void) event_name;
1069 (void) init_data;
1071 if (event_data->filename != NULL)
1072 g_file_get_contents (event_data->filename, &filedata, NULL, NULL);
1073 else
1074 filedata = load_mc_home_file (mc_global.share_data_dir, MC_HELP, &hlpfile);
1076 if (filedata == NULL)
1077 message (D_ERROR, MSG_ERROR, _("Cannot open file %s\n%s"),
1078 event_data->filename ? event_data->filename : hlpfile, unix_error_string (errno));
1080 g_free (hlpfile);
1082 if (filedata == NULL)
1083 return TRUE;
1085 translate_file (filedata);
1087 g_free (filedata);
1089 if (fdata == NULL)
1090 return TRUE;
1092 if ((event_data->node == NULL) || (*event_data->node == '\0'))
1093 event_data->node = "[main]";
1095 main_node = search_string (fdata, event_data->node);
1097 if (main_node == NULL)
1099 message (D_ERROR, MSG_ERROR, _("Cannot find node %s in help file"), event_data->node);
1101 /* Fallback to [main], return if it also cannot be found */
1102 main_node = search_string (fdata, "[main]");
1103 if (main_node == NULL)
1105 interactive_display_finish ();
1106 return TRUE;
1110 help_lines = min (LINES - 4, max (2 * LINES / 3, 18));
1112 whelp =
1113 create_dlg (TRUE, 0, 0, help_lines + 4, HELP_WINDOW_WIDTH + 4,
1114 help_colors, help_callback, "[Help]", _("Help"),
1115 DLG_TRYUP | DLG_CENTER | DLG_WANT_TAB);
1117 selected_item = search_string_node (main_node, STRING_LINK_START) - 1;
1118 currentpoint = main_node + 1; /* Skip the newline following the start of the node */
1120 for (history_ptr = HISTORY_SIZE; history_ptr;)
1122 history_ptr--;
1123 history[history_ptr].page = currentpoint;
1124 history[history_ptr].link = selected_item;
1127 help_bar = buttonbar_new (TRUE);
1128 help_bar->widget.y -= whelp->y;
1129 help_bar->widget.x -= whelp->x;
1131 md = mousedispatch_new (1, 1, help_lines, HELP_WINDOW_WIDTH - 2);
1133 add_widget (whelp, md);
1134 add_widget (whelp, help_bar);
1136 buttonbar_set_label (help_bar, 1, Q_ ("ButtonBar|Help"), help_map, NULL);
1137 buttonbar_set_label (help_bar, 2, Q_ ("ButtonBar|Index"), help_map, NULL);
1138 buttonbar_set_label (help_bar, 3, Q_ ("ButtonBar|Prev"), help_map, NULL);
1139 buttonbar_set_label (help_bar, 4, "", help_map, NULL);
1140 buttonbar_set_label (help_bar, 5, "", help_map, NULL);
1141 buttonbar_set_label (help_bar, 6, "", help_map, NULL);
1142 buttonbar_set_label (help_bar, 7, "", help_map, NULL);
1143 buttonbar_set_label (help_bar, 8, "", help_map, NULL);
1144 buttonbar_set_label (help_bar, 9, "", help_map, NULL);
1145 buttonbar_set_label (help_bar, 10, Q_ ("ButtonBar|Quit"), help_map, NULL);
1147 run_dlg (whelp);
1148 interactive_display_finish ();
1149 destroy_dlg (whelp);
1150 return TRUE;
1153 /* --------------------------------------------------------------------------------------------- */