Ticket #2541: MC saves configuration many times.
[midnight-commander.git] / src / help.c
blobf2b9cc210e0491bec6e81683b620843e85788ca0
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"
60 #include "lib/fileloc.h"
61 #include "lib/util.h"
62 #include "lib/widget.h"
63 #include "lib/event-types.h"
65 #include "keybind-defaults.h"
66 #include "keybind-defaults.h"
67 #include "help.h"
68 #include "main.h"
70 /*** global variables ****************************************************************************/
72 /*** file scope macro definitions ****************************************************************/
74 #define MAXLINKNAME 80
75 #define HISTORY_SIZE 20
76 #define HELP_WINDOW_WIDTH min(80, COLS - 16)
78 #define STRING_LINK_START "\01"
79 #define STRING_LINK_POINTER "\02"
80 #define STRING_LINK_END "\03"
81 #define STRING_NODE_END "\04"
83 /*** file scope type declarations ****************************************************************/
85 /* Link areas for the mouse */
86 typedef struct Link_Area
88 int x1, y1, x2, y2;
89 const char *link_name;
90 } Link_Area;
92 /*** file scope variables ************************************************************************/
94 static char *fdata = NULL; /* Pointer to the loaded data file */
95 static int help_lines; /* Lines in help viewer */
96 static int history_ptr; /* For the history queue */
97 static const char *main_node; /* The main node */
98 static const char *last_shown = NULL; /* Last byte shown in a screen */
99 static gboolean end_of_node = FALSE; /* Flag: the last character of the node shown? */
100 static const char *currentpoint;
101 static const char *selected_item;
103 /* The widget variables */
104 static Dlg_head *whelp;
106 static struct
108 const char *page; /* Pointer to the selected page */
109 const char *link; /* Pointer to the selected link */
110 } history[HISTORY_SIZE];
112 static GSList *link_area = NULL;
113 static gboolean inside_link_area = FALSE;
115 static cb_ret_t help_callback (Dlg_head * h, Widget * sender, dlg_msg_t msg, int parm, void *data);
117 /*** file scope functions ************************************************************************/
118 /* --------------------------------------------------------------------------------------------- */
120 /** returns the position where text was found in the start buffer
121 * or 0 if not found
123 static const char *
124 search_string (const char *start, const char *text)
126 const char *result = NULL;
127 char *local_text = g_strdup (text);
128 char *d = local_text;
129 const char *e = start;
131 /* fmt sometimes replaces a space with a newline in the help file */
132 /* Replace the newlines in the link name with spaces to correct the situation */
133 while (*d != '\0')
135 if (*d == '\n')
136 *d = ' ';
137 str_next_char (&d);
140 /* Do search */
141 for (d = local_text; *e; e++)
143 if (*d == *e)
144 d++;
145 else
146 d = local_text;
147 if (*d == '\0')
149 result = e + 1;
150 break;
154 g_free (local_text);
155 return result;
158 /* --------------------------------------------------------------------------------------------- */
159 /** Searches text in the buffer pointed by start. Search ends
160 * if the CHAR_NODE_END is found in the text.
161 * @returns 0 on failure
164 static const char *
165 search_string_node (const char *start, const char *text)
167 const char *d = text;
168 const char *e = start;
170 if (start != NULL)
171 for (; *e && *e != CHAR_NODE_END; e++)
173 if (*d == *e)
174 d++;
175 else
176 d = text;
177 if (*d == '\0')
178 return e + 1;
181 return NULL;
184 /* --------------------------------------------------------------------------------------------- */
185 /** Searches the_char in the buffer pointer by start and searches
186 * it can search forward (direction = 1) or backward (direction = -1)
189 static const char *
190 search_char_node (const char *start, char the_char, int direction)
192 const char *e;
194 for (e = start; (*e != '\0') && (*e != CHAR_NODE_END); e += direction)
195 if (*e == the_char)
196 return e;
198 return NULL;
201 /* --------------------------------------------------------------------------------------------- */
202 /** Returns the new current pointer when moved lines lines */
204 static const char *
205 move_forward2 (const char *c, int lines)
207 const char *p;
208 int line;
210 currentpoint = c;
211 for (line = 0, p = currentpoint; (*p != '\0') && (*p != CHAR_NODE_END); str_cnext_char (&p))
213 if (line == lines)
214 return currentpoint = p;
216 if (*p == '\n')
217 line++;
219 return currentpoint = c;
222 /* --------------------------------------------------------------------------------------------- */
224 static const char *
225 move_backward2 (const char *c, int lines)
227 const char *p;
228 int line;
230 currentpoint = c;
231 for (line = 0, p = currentpoint; (*p != '\0') && ((int) (p - fdata) >= 0); str_cprev_char (&p))
233 if (*p == CHAR_NODE_END)
235 /* We reached the beginning of the node */
236 /* Skip the node headers */
237 while (*p != ']')
238 str_cnext_char (&p);
239 return currentpoint = p + 2; /* Skip the newline following the start of the node */
242 if (*(p - 1) == '\n')
243 line++;
244 if (line == lines)
245 return currentpoint = p;
247 return currentpoint = c;
250 /* --------------------------------------------------------------------------------------------- */
252 static void
253 move_forward (int i)
255 if (!end_of_node)
256 currentpoint = move_forward2 (currentpoint, i);
259 /* --------------------------------------------------------------------------------------------- */
261 static void
262 move_backward (int i)
264 currentpoint = move_backward2 (currentpoint, ++i);
267 /* --------------------------------------------------------------------------------------------- */
269 static void
270 move_to_top (void)
272 while (((int) (currentpoint > fdata) > 0) && (*currentpoint != CHAR_NODE_END))
273 currentpoint--;
275 while (*currentpoint != ']')
276 currentpoint++;
277 currentpoint = currentpoint + 2; /* Skip the newline following the start of the node */
278 selected_item = NULL;
281 /* --------------------------------------------------------------------------------------------- */
283 static void
284 move_to_bottom (void)
286 while ((*currentpoint != '\0') && (*currentpoint != CHAR_NODE_END))
287 currentpoint++;
288 currentpoint--;
289 move_backward (1);
292 /* --------------------------------------------------------------------------------------------- */
294 static const char *
295 help_follow_link (const char *start, const char *lc_selected_item)
297 char link_name[MAXLINKNAME];
298 const char *p;
299 int i = 0;
301 if (lc_selected_item == NULL)
302 return start;
304 for (p = lc_selected_item; *p && *p != CHAR_NODE_END && *p != CHAR_LINK_POINTER; p++)
306 if (*p == CHAR_LINK_POINTER)
308 link_name[0] = '[';
309 for (i = 1; *p != CHAR_LINK_END && *p && *p != CHAR_NODE_END && i < MAXLINKNAME - 3;)
310 link_name[i++] = *++p;
311 link_name[i - 1] = ']';
312 link_name[i] = '\0';
313 p = search_string (fdata, link_name);
314 if (p != NULL)
316 p += 1; /* Skip the newline following the start of the node */
317 return p;
321 /* Create a replacement page with the error message */
322 return _("Help file format error\n");
325 /* --------------------------------------------------------------------------------------------- */
327 static const char *
328 select_next_link (const char *current_link)
330 const char *p;
332 if (current_link == NULL)
333 return NULL;
335 p = search_string_node (current_link, STRING_LINK_END);
336 if (p == NULL)
337 return NULL;
338 p = search_string_node (p, STRING_LINK_START);
339 if (p == NULL)
340 return NULL;
341 return p - 1;
344 /* --------------------------------------------------------------------------------------------- */
346 static const char *
347 select_prev_link (const char *current_link)
349 return current_link == NULL ? NULL : search_char_node (current_link - 1, CHAR_LINK_START, -1);
352 /* --------------------------------------------------------------------------------------------- */
354 static void
355 start_link_area (int x, int y, const char *link_name)
357 Link_Area *la;
359 if (inside_link_area)
360 message (D_NORMAL, _("Warning"), _("Internal bug: Double start of link area"));
362 /* Allocate memory for a new link area */
363 la = g_new (Link_Area, 1);
364 /* Save the beginning coordinates of the link area */
365 la->x1 = x;
366 la->y1 = y;
367 /* Save the name of the destination anchor */
368 la->link_name = link_name;
369 link_area = g_slist_prepend (link_area, la);
371 inside_link_area = TRUE;
374 /* --------------------------------------------------------------------------------------------- */
376 static void
377 end_link_area (int x, int y)
379 if (inside_link_area)
381 Link_Area *la = (Link_Area *) link_area->data;
382 /* Save the end coordinates of the link area */
383 la->x2 = x;
384 la->y2 = y;
385 inside_link_area = FALSE;
389 /* --------------------------------------------------------------------------------------------- */
391 static void
392 clear_link_areas (void)
394 g_slist_foreach (link_area, (GFunc) g_free, NULL);
395 g_slist_free (link_area);
396 link_area = NULL;
397 inside_link_area = FALSE;
400 /* --------------------------------------------------------------------------------------------- */
402 static void
403 help_print_word (Dlg_head * h, GString * word, int *col, int *line, gboolean add_space)
405 if (*line >= help_lines)
406 g_string_set_size (word, 0);
407 else
409 int w;
411 w = str_term_width1 (word->str);
412 if (*col + w >= HELP_WINDOW_WIDTH)
414 *col = 0;
415 (*line)++;
418 if (*line >= help_lines)
419 g_string_set_size (word, 0);
420 else
422 dlg_move (h, *line + 2, *col + 2);
423 tty_print_string (word->str);
424 g_string_set_size (word, 0);
425 *col += w;
429 if (add_space)
431 if (*col < HELP_WINDOW_WIDTH - 1)
433 tty_print_char (' ');
434 (*col)++;
436 else
438 *col = 0;
439 (*line)++;
444 /* --------------------------------------------------------------------------------------------- */
446 static void
447 help_show (Dlg_head * h, const char *paint_start)
449 const char *p, *n;
450 int col, line, c;
451 gboolean painting = TRUE;
452 gboolean acs; /* Flag: Alternate character set active? */
453 gboolean repeat_paint;
454 int active_col, active_line; /* Active link position */
455 char buff[MB_LEN_MAX + 1];
456 GString *word;
458 word = g_string_sized_new (32);
460 tty_setcolor (HELP_NORMAL_COLOR);
463 line = col = active_col = active_line = 0;
464 repeat_paint = FALSE;
465 acs = FALSE;
467 clear_link_areas ();
468 if ((int) (selected_item - paint_start) < 0)
469 selected_item = NULL;
471 p = paint_start;
472 n = paint_start;
473 while ((n[0] != '\0') && (n[0] != CHAR_NODE_END) && (line < help_lines))
475 p = n;
476 n = str_cget_next_char (p);
477 memcpy (buff, p, n - p);
478 buff[n - p] = '\0';
480 c = (unsigned char) buff[0];
481 switch (c)
483 case CHAR_LINK_START:
484 if (selected_item == NULL)
485 selected_item = p;
486 if (p != selected_item)
487 tty_setcolor (HELP_LINK_COLOR);
488 else
490 tty_setcolor (HELP_SLINK_COLOR);
492 /* Store the coordinates of the link */
493 active_col = col + 2;
494 active_line = line + 2;
496 start_link_area (col, line, p);
497 break;
498 case CHAR_LINK_POINTER:
499 painting = FALSE;
500 end_link_area (col - 1, line);
501 break;
502 case CHAR_LINK_END:
503 painting = TRUE;
504 help_print_word (h, word, &col, &line, FALSE);
505 tty_setcolor (HELP_NORMAL_COLOR);
506 break;
507 case CHAR_ALTERNATE:
508 acs = TRUE;
509 break;
510 case CHAR_NORMAL:
511 acs = FALSE;
512 break;
513 case CHAR_VERSION:
514 dlg_move (h, line + 2, col + 2);
515 tty_print_string (VERSION);
516 col += str_term_width1 (VERSION);
517 break;
518 case CHAR_FONT_BOLD:
519 tty_setcolor (HELP_BOLD_COLOR);
520 break;
521 case CHAR_FONT_ITALIC:
522 tty_setcolor (HELP_ITALIC_COLOR);
523 break;
524 case CHAR_FONT_NORMAL:
525 help_print_word (h, word, &col, &line, FALSE);
526 tty_setcolor (HELP_NORMAL_COLOR);
527 break;
528 case '\n':
529 if (painting)
530 help_print_word (h, word, &col, &line, FALSE);
531 line++;
532 col = 0;
533 break;
534 case '\t':
535 col = (col / 8 + 1) * 8;
536 if (col >= HELP_WINDOW_WIDTH)
538 line++;
539 col = 8;
541 break;
542 case ' ':
543 /* word delimeter */
544 if (painting)
545 help_print_word (h, word, &col, &line, TRUE);
546 break;
547 default:
548 if (painting && (line < help_lines))
550 if (!acs)
551 /* accumulate symbols in a word */
552 g_string_append (word, buff);
553 else if (col < HELP_WINDOW_WIDTH)
555 dlg_move (h, line + 2, col + 2);
557 if ((c == ' ') || (c == '.'))
558 tty_print_char (c);
559 else
560 #ifndef HAVE_SLANG
561 tty_print_char (acs_map[c]);
562 #else
563 SLsmg_draw_object (h->y + line + 2, h->x + col + 2, c);
564 #endif
565 col++;
571 /* print last word */
572 if (n[0] == CHAR_NODE_END)
573 help_print_word (h, word, &col, &line, FALSE);
575 last_shown = p;
576 end_of_node = line < help_lines;
577 tty_setcolor (HELP_NORMAL_COLOR);
578 if ((int) (selected_item - last_shown) >= 0)
580 if ((link_area == NULL) || (link_area->data == NULL))
581 selected_item = NULL;
582 else
584 selected_item = ((Link_Area *) link_area->data)->link_name;
585 repeat_paint = TRUE;
589 while (repeat_paint);
591 g_string_free (word, TRUE);
593 /* Position the cursor over a nice link */
594 if (active_col)
595 dlg_move (h, active_line, active_col);
598 /* --------------------------------------------------------------------------------------------- */
600 static int
601 help_event (Gpm_Event * event, void *vp)
603 Widget *w = vp;
604 GSList *current_area;
606 if ((event->type & GPM_UP) == 0)
607 return 0;
609 /* The event is relative to the dialog window, adjust it: */
610 event->x -= 2;
611 event->y -= 2;
613 if (event->buttons & GPM_B_RIGHT)
615 currentpoint = history[history_ptr].page;
616 selected_item = history[history_ptr].link;
617 history_ptr--;
618 if (history_ptr < 0)
619 history_ptr = HISTORY_SIZE - 1;
621 help_callback (w->owner, NULL, DLG_DRAW, 0, NULL);
622 return 0;
625 /* Test whether the mouse click is inside one of the link areas */
626 for (current_area = link_area; current_area != NULL; current_area = g_slist_next (current_area))
628 Link_Area *la = (Link_Area *) current_area->data;
629 /* Test one line link area */
630 if (event->y == la->y1 && event->x >= la->x1 && event->y == la->y2 && event->x <= la->x2)
631 break;
632 /* Test two line link area */
633 if (la->y1 + 1 == la->y2)
635 /* The first line */
636 if (event->y == la->y1 && event->x >= la->x1)
637 break;
638 /* The second line */
639 if (event->y == la->y2 && event->x <= la->x2)
640 break;
642 /* Mouse will not work with link areas of more than two lines */
645 /* Test whether a link area was found */
646 if (current_area != NULL)
648 Link_Area *la = (Link_Area *) current_area->data;
650 /* The click was inside a link area -> follow the link */
651 history_ptr = (history_ptr + 1) % HISTORY_SIZE;
652 history[history_ptr].page = currentpoint;
653 history[history_ptr].link = la->link_name;
654 currentpoint = help_follow_link (currentpoint, la->link_name);
655 selected_item = NULL;
657 else if (event->y < 0)
658 move_backward (help_lines - 1);
659 else if (event->y >= help_lines)
660 move_forward (help_lines - 1);
661 else if (event->y < help_lines / 2)
662 move_backward (1);
663 else
664 move_forward (1);
666 /* Show the new node */
667 help_callback (w->owner, NULL, DLG_DRAW, 0, NULL);
669 return 0;
672 /* --------------------------------------------------------------------------------------------- */
673 /** show help */
675 static void
676 help_help (Dlg_head * h)
678 const char *p;
680 history_ptr = (history_ptr + 1) % HISTORY_SIZE;
681 history[history_ptr].page = currentpoint;
682 history[history_ptr].link = selected_item;
684 p = search_string (fdata, "[How to use help]");
685 if (p != NULL)
687 currentpoint = p + 1; /* Skip the newline following the start of the node */
688 selected_item = NULL;
689 help_callback (h, NULL, DLG_DRAW, 0, NULL);
693 /* --------------------------------------------------------------------------------------------- */
695 static void
696 help_index (Dlg_head * h)
698 const char *new_item;
700 new_item = search_string (fdata, "[Contents]");
702 if (new_item == NULL)
703 message (D_ERROR, MSG_ERROR, _("Cannot find node %s in help file"), "[Contents]");
704 else
706 history_ptr = (history_ptr + 1) % HISTORY_SIZE;
707 history[history_ptr].page = currentpoint;
708 history[history_ptr].link = selected_item;
710 currentpoint = new_item + 1; /* Skip the newline following the start of the node */
711 selected_item = NULL;
712 help_callback (h, NULL, DLG_DRAW, 0, NULL);
716 /* --------------------------------------------------------------------------------------------- */
718 static void
719 help_back (Dlg_head * h)
721 currentpoint = history[history_ptr].page;
722 selected_item = history[history_ptr].link;
723 history_ptr--;
724 if (history_ptr < 0)
725 history_ptr = HISTORY_SIZE - 1;
727 help_callback (h, NULL, DLG_DRAW, 0, NULL); /* FIXME: unneeded? */
730 /* --------------------------------------------------------------------------------------------- */
732 static void
733 help_next_link (gboolean move_down)
735 const char *new_item;
737 new_item = select_next_link (selected_item);
738 if (new_item != NULL)
740 selected_item = new_item;
741 if ((int) (selected_item - last_shown) >= 0)
743 if (move_down)
744 move_forward (1);
745 else
746 selected_item = NULL;
749 else if (move_down)
750 move_forward (1);
751 else
752 selected_item = NULL;
755 /* --------------------------------------------------------------------------------------------- */
757 static void
758 help_prev_link (gboolean move_up)
760 const char *new_item;
762 new_item = select_prev_link (selected_item);
763 selected_item = new_item;
764 if ((selected_item == NULL) || (selected_item < currentpoint))
766 if (move_up)
767 move_backward (1);
768 else if ((link_area != NULL) && (link_area->data != NULL))
769 selected_item = ((Link_Area *) link_area->data)->link_name;
770 else
771 selected_item = NULL;
775 /* --------------------------------------------------------------------------------------------- */
777 static void
778 help_next_node (void)
780 const char *new_item;
782 new_item = currentpoint;
783 while ((*new_item != '\0') && (*new_item != CHAR_NODE_END))
784 new_item++;
786 if (*++new_item == '[')
787 while (*++new_item != '\0')
788 if ((*new_item == ']') && (*++new_item != '\0') && (*++new_item != '\0'))
790 currentpoint = new_item;
791 selected_item = NULL;
792 break;
796 /* --------------------------------------------------------------------------------------------- */
798 static void
799 help_prev_node (void)
801 const char *new_item;
803 new_item = currentpoint;
804 while (((int) (new_item - fdata) > 1) && (*new_item != CHAR_NODE_END))
805 new_item--;
806 new_item--;
807 while (((int) (new_item - fdata) > 0) && (*new_item != CHAR_NODE_END))
808 new_item--;
809 while (*new_item != ']')
810 new_item++;
811 currentpoint = new_item + 2;
812 selected_item = NULL;
815 /* --------------------------------------------------------------------------------------------- */
817 static void
818 help_select_link (void)
820 /* follow link */
821 if (selected_item == NULL)
823 #ifdef WE_WANT_TO_GO_BACKWARD_ON_KEY_RIGHT
824 /* Is there any reason why the right key would take us
825 * backward if there are no links selected?, I agree
826 * with Torben than doing nothing in this case is better
828 /* If there are no links, go backward in history */
829 history_ptr--;
830 if (history_ptr < 0)
831 history_ptr = HISTORY_SIZE - 1;
833 currentpoint = history[history_ptr].page;
834 selected_item = history[history_ptr].link;
835 #endif
837 else
839 history_ptr = (history_ptr + 1) % HISTORY_SIZE;
840 history[history_ptr].page = currentpoint;
841 history[history_ptr].link = selected_item;
842 currentpoint = help_follow_link (currentpoint, selected_item);
845 selected_item = NULL;
848 /* --------------------------------------------------------------------------------------------- */
850 static cb_ret_t
851 help_execute_cmd (unsigned long command)
853 cb_ret_t ret = MSG_HANDLED;
855 switch (command)
857 case CK_Help:
858 help_help (whelp);
859 break;
860 case CK_Index:
861 help_index (whelp);
862 break;
863 case CK_Back:
864 help_back (whelp);
865 break;
866 case CK_Up:
867 help_prev_link (TRUE);
868 break;
869 case CK_Down:
870 help_next_link (TRUE);
871 break;
872 case CK_PageDown:
873 move_forward (help_lines - 1);
874 break;
875 case CK_PageUp:
876 move_backward (help_lines - 1);
877 break;
878 case CK_HalfPageDown:
879 move_forward (help_lines / 2);
880 break;
881 case CK_HalfPageUp:
882 move_backward (help_lines / 2);
883 break;
884 case CK_Top:
885 move_to_top ();
886 break;
887 case CK_Bottom:
888 move_to_bottom ();
889 break;
890 case CK_Enter:
891 help_select_link ();
892 break;
893 case CK_LinkNext:
894 help_next_link (FALSE);
895 break;
896 case CK_LinkPrev:
897 help_prev_link (FALSE);
898 break;
899 case CK_NodeNext:
900 help_next_node ();
901 break;
902 case CK_NodePrev:
903 help_prev_node ();
904 break;
905 case CK_Quit:
906 dlg_stop (whelp);
907 break;
908 case CK_Cancel:
909 /* don't close help due to SIGINT */
910 break;
911 default:
912 ret = MSG_NOT_HANDLED;
915 return ret;
918 /* --------------------------------------------------------------------------------------------- */
920 static cb_ret_t
921 help_handle_key (Dlg_head * h, int c)
923 unsigned long command;
925 command = keybind_lookup_keymap_command (help_map, c);
926 if ((command == CK_IgnoreKey) || (help_execute_cmd (command) == MSG_NOT_HANDLED))
927 return MSG_NOT_HANDLED;
929 help_callback (h, NULL, DLG_DRAW, 0, NULL);
930 return MSG_HANDLED;
933 /* --------------------------------------------------------------------------------------------- */
935 static cb_ret_t
936 help_callback (Dlg_head * h, Widget * sender, dlg_msg_t msg, int parm, void *data)
938 WButtonBar *bb;
940 switch (msg)
942 case DLG_RESIZE:
943 help_lines = min (LINES - 4, max (2 * LINES / 3, 18));
944 dlg_set_size (h, help_lines + 4, HELP_WINDOW_WIDTH + 4);
945 bb = find_buttonbar (h);
946 widget_set_size (&bb->widget, LINES - 1, 0, 1, COLS);
947 return MSG_HANDLED;
949 case DLG_DRAW:
950 common_dialog_repaint (h);
951 help_show (h, currentpoint);
952 return MSG_HANDLED;
954 case DLG_KEY:
955 return help_handle_key (h, parm);
957 case DLG_ACTION:
958 /* shortcut */
959 if (sender == NULL)
960 return help_execute_cmd (parm);
961 /* message from buttonbar */
962 if (sender == (Widget *) find_buttonbar (h))
964 if (data != NULL)
965 return send_message ((Widget *) data, WIDGET_COMMAND, parm);
966 return help_execute_cmd (parm);
968 return MSG_NOT_HANDLED;
970 default:
971 return default_dlg_callback (h, sender, msg, parm, data);
975 /* --------------------------------------------------------------------------------------------- */
977 static void
978 interactive_display_finish (void)
980 clear_link_areas ();
983 /* --------------------------------------------------------------------------------------------- */
984 /** translate help file into terminal encoding */
986 static void
987 translate_file (char *filedata)
989 GIConv conv;
990 GString *translated_data;
992 /* initial allocation for largest whole help file */
993 translated_data = g_string_sized_new (32 * 1024);
995 conv = str_crt_conv_from ("UTF-8");
997 if (conv == INVALID_CONV)
998 g_string_free (translated_data, TRUE);
999 else
1001 g_free (fdata);
1003 if (str_convert (conv, filedata, translated_data) != ESTR_FAILURE)
1004 fdata = g_string_free (translated_data, FALSE);
1005 else
1007 fdata = NULL;
1008 g_string_free (translated_data, TRUE);
1010 str_close_conv (conv);
1014 /* --------------------------------------------------------------------------------------------- */
1016 static cb_ret_t
1017 md_callback (Widget * w, widget_msg_t msg, int parm)
1019 switch (msg)
1021 case WIDGET_RESIZED:
1022 w->lines = help_lines;
1023 return MSG_HANDLED;
1025 default:
1026 return default_proc (msg, parm);
1030 /* --------------------------------------------------------------------------------------------- */
1032 static Widget *
1033 mousedispatch_new (int y, int x, int yl, int xl)
1035 Widget *w = g_new (Widget, 1);
1036 init_widget (w, y, x, yl, xl, md_callback, help_event);
1037 return w;
1040 /* --------------------------------------------------------------------------------------------- */
1041 /*** public functions ****************************************************************************/
1042 /* --------------------------------------------------------------------------------------------- */
1044 /* event callback */
1045 gboolean
1046 help_interactive_display (const gchar * event_group_name, const gchar * event_name,
1047 gpointer init_data, gpointer data)
1049 const dlg_colors_t help_colors = {
1050 HELP_NORMAL_COLOR, /* common text color */
1051 0, /* unused in help */
1052 HELP_BOLD_COLOR, /* bold text color */
1053 0, /* unused in help */
1054 HELP_TITLE_COLOR /* title color */
1057 WButtonBar *help_bar;
1058 Widget *md;
1059 char *hlpfile = NULL;
1060 char *filedata;
1061 ev_help_t *event_data = (ev_help_t *) data;
1063 (void) event_group_name;
1064 (void) event_name;
1065 (void) init_data;
1067 if (event_data->filename != NULL)
1068 g_file_get_contents (event_data->filename, &filedata, NULL, NULL);
1069 else
1070 filedata = load_mc_home_file (mc_global.share_data_dir, MC_HELP, &hlpfile);
1072 if (filedata == NULL)
1073 message (D_ERROR, MSG_ERROR, _("Cannot open file %s\n%s"),
1074 event_data->filename ? event_data->filename : hlpfile, unix_error_string (errno));
1076 g_free (hlpfile);
1078 if (filedata == NULL)
1079 return TRUE;
1081 translate_file (filedata);
1083 g_free (filedata);
1085 if (fdata == NULL)
1086 return TRUE;
1088 if ((event_data->node == NULL) || (*event_data->node == '\0'))
1089 event_data->node = "[main]";
1091 main_node = search_string (fdata, event_data->node);
1093 if (main_node == NULL)
1095 message (D_ERROR, MSG_ERROR, _("Cannot find node %s in help file"), event_data->node);
1097 /* Fallback to [main], return if it also cannot be found */
1098 main_node = search_string (fdata, "[main]");
1099 if (main_node == NULL)
1101 interactive_display_finish ();
1102 return TRUE;
1106 help_lines = min (LINES - 4, max (2 * LINES / 3, 18));
1108 whelp =
1109 create_dlg (TRUE, 0, 0, help_lines + 4, HELP_WINDOW_WIDTH + 4,
1110 help_colors, help_callback, "[Help]", _("Help"),
1111 DLG_TRYUP | DLG_CENTER | DLG_WANT_TAB);
1113 selected_item = search_string_node (main_node, STRING_LINK_START) - 1;
1114 currentpoint = main_node + 1; /* Skip the newline following the start of the node */
1116 for (history_ptr = HISTORY_SIZE; history_ptr;)
1118 history_ptr--;
1119 history[history_ptr].page = currentpoint;
1120 history[history_ptr].link = selected_item;
1123 help_bar = buttonbar_new (TRUE);
1124 help_bar->widget.y -= whelp->y;
1125 help_bar->widget.x -= whelp->x;
1127 md = mousedispatch_new (1, 1, help_lines, HELP_WINDOW_WIDTH - 2);
1129 add_widget (whelp, md);
1130 add_widget (whelp, help_bar);
1132 buttonbar_set_label (help_bar, 1, Q_ ("ButtonBar|Help"), help_map, NULL);
1133 buttonbar_set_label (help_bar, 2, Q_ ("ButtonBar|Index"), help_map, NULL);
1134 buttonbar_set_label (help_bar, 3, Q_ ("ButtonBar|Prev"), help_map, NULL);
1135 buttonbar_set_label (help_bar, 4, "", help_map, NULL);
1136 buttonbar_set_label (help_bar, 5, "", help_map, NULL);
1137 buttonbar_set_label (help_bar, 6, "", help_map, NULL);
1138 buttonbar_set_label (help_bar, 7, "", help_map, NULL);
1139 buttonbar_set_label (help_bar, 8, "", help_map, NULL);
1140 buttonbar_set_label (help_bar, 9, "", help_map, NULL);
1141 buttonbar_set_label (help_bar, 10, Q_ ("ButtonBar|Quit"), help_map, NULL);
1143 run_dlg (whelp);
1144 interactive_display_finish ();
1145 destroy_dlg (whelp);
1146 return TRUE;
1149 /* --------------------------------------------------------------------------------------------- */