Fix of DOXYGEN docs: @return instead of @returns
[midnight-commander.git] / src / help.c
blob2390f5ae5cfd33c62790febf25fd4d8cb1770adb
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"
73 /*** global variables ****************************************************************************/
75 /*** file scope macro definitions ****************************************************************/
77 #define MAXLINKNAME 80
78 #define HISTORY_SIZE 20
79 #define HELP_WINDOW_WIDTH min(80, COLS - 16)
81 #define STRING_LINK_START "\01"
82 #define STRING_LINK_POINTER "\02"
83 #define STRING_LINK_END "\03"
84 #define STRING_NODE_END "\04"
86 /*** file scope type declarations ****************************************************************/
88 /* Link areas for the mouse */
89 typedef struct Link_Area
91 int x1, y1, x2, y2;
92 const char *link_name;
93 } Link_Area;
95 /*** file scope variables ************************************************************************/
97 static char *fdata = NULL; /* Pointer to the loaded data file */
98 static int help_lines; /* Lines in help viewer */
99 static int history_ptr; /* For the history queue */
100 static const char *main_node; /* The main node */
101 static const char *last_shown = NULL; /* Last byte shown in a screen */
102 static gboolean end_of_node = FALSE; /* Flag: the last character of the node shown? */
103 static const char *currentpoint;
104 static const char *selected_item;
106 /* The widget variables */
107 static Dlg_head *whelp;
109 static struct
111 const char *page; /* Pointer to the selected page */
112 const char *link; /* Pointer to the selected link */
113 } history[HISTORY_SIZE];
115 static GSList *link_area = NULL;
116 static gboolean inside_link_area = FALSE;
118 static cb_ret_t help_callback (Dlg_head * h, Widget * sender, dlg_msg_t msg, int parm, void *data);
120 /*** file scope functions ************************************************************************/
121 /* --------------------------------------------------------------------------------------------- */
123 /** returns the position where text was found in the start buffer
124 * or 0 if not found
126 static const char *
127 search_string (const char *start, const char *text)
129 const char *result = NULL;
130 char *local_text = g_strdup (text);
131 char *d = local_text;
132 const char *e = start;
134 /* fmt sometimes replaces a space with a newline in the help file */
135 /* Replace the newlines in the link name with spaces to correct the situation */
136 while (*d != '\0')
138 if (*d == '\n')
139 *d = ' ';
140 str_next_char (&d);
143 /* Do search */
144 for (d = local_text; *e; e++)
146 if (*d == *e)
147 d++;
148 else
149 d = local_text;
150 if (*d == '\0')
152 result = e + 1;
153 break;
157 g_free (local_text);
158 return result;
161 /* --------------------------------------------------------------------------------------------- */
162 /** Searches text in the buffer pointed by start. Search ends
163 * if the CHAR_NODE_END is found in the text.
164 * @return NULL on failure
167 static const char *
168 search_string_node (const char *start, const char *text)
170 const char *d = text;
171 const char *e = start;
173 if (start != NULL)
174 for (; *e && *e != CHAR_NODE_END; e++)
176 if (*d == *e)
177 d++;
178 else
179 d = text;
180 if (*d == '\0')
181 return e + 1;
184 return NULL;
187 /* --------------------------------------------------------------------------------------------- */
188 /** Searches the_char in the buffer pointer by start and searches
189 * it can search forward (direction = 1) or backward (direction = -1)
192 static const char *
193 search_char_node (const char *start, char the_char, int direction)
195 const char *e;
197 for (e = start; (*e != '\0') && (*e != CHAR_NODE_END); e += direction)
198 if (*e == the_char)
199 return e;
201 return NULL;
204 /* --------------------------------------------------------------------------------------------- */
205 /** Returns the new current pointer when moved lines lines */
207 static const char *
208 move_forward2 (const char *c, int lines)
210 const char *p;
211 int line;
213 currentpoint = c;
214 for (line = 0, p = currentpoint; (*p != '\0') && (*p != CHAR_NODE_END); str_cnext_char (&p))
216 if (line == lines)
217 return currentpoint = p;
219 if (*p == '\n')
220 line++;
222 return currentpoint = c;
225 /* --------------------------------------------------------------------------------------------- */
227 static const char *
228 move_backward2 (const char *c, int lines)
230 const char *p;
231 int line;
233 currentpoint = c;
234 for (line = 0, p = currentpoint; (*p != '\0') && ((int) (p - fdata) >= 0); str_cprev_char (&p))
236 if (*p == CHAR_NODE_END)
238 /* We reached the beginning of the node */
239 /* Skip the node headers */
240 while (*p != ']')
241 str_cnext_char (&p);
242 return currentpoint = p + 2; /* Skip the newline following the start of the node */
245 if (*(p - 1) == '\n')
246 line++;
247 if (line == lines)
248 return currentpoint = p;
250 return currentpoint = c;
253 /* --------------------------------------------------------------------------------------------- */
255 static void
256 move_forward (int i)
258 if (!end_of_node)
259 currentpoint = move_forward2 (currentpoint, i);
262 /* --------------------------------------------------------------------------------------------- */
264 static void
265 move_backward (int i)
267 currentpoint = move_backward2 (currentpoint, ++i);
270 /* --------------------------------------------------------------------------------------------- */
272 static void
273 move_to_top (void)
275 while (((int) (currentpoint > fdata) > 0) && (*currentpoint != CHAR_NODE_END))
276 currentpoint--;
278 while (*currentpoint != ']')
279 currentpoint++;
280 currentpoint = currentpoint + 2; /* Skip the newline following the start of the node */
281 selected_item = NULL;
284 /* --------------------------------------------------------------------------------------------- */
286 static void
287 move_to_bottom (void)
289 while ((*currentpoint != '\0') && (*currentpoint != CHAR_NODE_END))
290 currentpoint++;
291 currentpoint--;
292 move_backward (1);
295 /* --------------------------------------------------------------------------------------------- */
297 static const char *
298 help_follow_link (const char *start, const char *lc_selected_item)
300 char link_name[MAXLINKNAME];
301 const char *p;
302 int i = 0;
304 if (lc_selected_item == NULL)
305 return start;
307 for (p = lc_selected_item; *p && *p != CHAR_NODE_END && *p != CHAR_LINK_POINTER; p++)
309 if (*p == CHAR_LINK_POINTER)
311 link_name[0] = '[';
312 for (i = 1; *p != CHAR_LINK_END && *p && *p != CHAR_NODE_END && i < MAXLINKNAME - 3;)
313 link_name[i++] = *++p;
314 link_name[i - 1] = ']';
315 link_name[i] = '\0';
316 p = search_string (fdata, link_name);
317 if (p != NULL)
319 p += 1; /* Skip the newline following the start of the node */
320 return p;
324 /* Create a replacement page with the error message */
325 return _("Help file format error\n");
328 /* --------------------------------------------------------------------------------------------- */
330 static const char *
331 select_next_link (const char *current_link)
333 const char *p;
335 if (current_link == NULL)
336 return NULL;
338 p = search_string_node (current_link, STRING_LINK_END);
339 if (p == NULL)
340 return NULL;
341 p = search_string_node (p, STRING_LINK_START);
342 if (p == NULL)
343 return NULL;
344 return p - 1;
347 /* --------------------------------------------------------------------------------------------- */
349 static const char *
350 select_prev_link (const char *current_link)
352 return current_link == NULL ? NULL : search_char_node (current_link - 1, CHAR_LINK_START, -1);
355 /* --------------------------------------------------------------------------------------------- */
357 static void
358 start_link_area (int x, int y, const char *link_name)
360 Link_Area *la;
362 if (inside_link_area)
363 message (D_NORMAL, _("Warning"), _("Internal bug: Double start of link area"));
365 /* Allocate memory for a new link area */
366 la = g_new (Link_Area, 1);
367 /* Save the beginning coordinates of the link area */
368 la->x1 = x;
369 la->y1 = y;
370 /* Save the name of the destination anchor */
371 la->link_name = link_name;
372 link_area = g_slist_prepend (link_area, la);
374 inside_link_area = TRUE;
377 /* --------------------------------------------------------------------------------------------- */
379 static void
380 end_link_area (int x, int y)
382 if (inside_link_area)
384 Link_Area *la = (Link_Area *) link_area->data;
385 /* Save the end coordinates of the link area */
386 la->x2 = x;
387 la->y2 = y;
388 inside_link_area = FALSE;
392 /* --------------------------------------------------------------------------------------------- */
394 static void
395 clear_link_areas (void)
397 g_slist_foreach (link_area, (GFunc) g_free, NULL);
398 g_slist_free (link_area);
399 link_area = NULL;
400 inside_link_area = FALSE;
403 /* --------------------------------------------------------------------------------------------- */
405 static void
406 help_print_word (Dlg_head * h, GString * word, int *col, int *line, gboolean add_space)
408 if (*line >= help_lines)
409 g_string_set_size (word, 0);
410 else
412 int w;
414 w = str_term_width1 (word->str);
415 if (*col + w >= HELP_WINDOW_WIDTH)
417 *col = 0;
418 (*line)++;
421 if (*line >= help_lines)
422 g_string_set_size (word, 0);
423 else
425 dlg_move (h, *line + 2, *col + 2);
426 tty_print_string (word->str);
427 g_string_set_size (word, 0);
428 *col += w;
432 if (add_space)
434 if (*col < HELP_WINDOW_WIDTH - 1)
436 tty_print_char (' ');
437 (*col)++;
439 else
441 *col = 0;
442 (*line)++;
447 /* --------------------------------------------------------------------------------------------- */
449 static void
450 help_show (Dlg_head * h, const char *paint_start)
452 const char *p, *n;
453 int col, line, c;
454 gboolean painting = TRUE;
455 gboolean acs; /* Flag: Alternate character set active? */
456 gboolean repeat_paint;
457 int active_col, active_line; /* Active link position */
458 char buff[MB_LEN_MAX + 1];
459 GString *word;
461 word = g_string_sized_new (32);
463 tty_setcolor (HELP_NORMAL_COLOR);
466 line = col = active_col = active_line = 0;
467 repeat_paint = FALSE;
468 acs = FALSE;
470 clear_link_areas ();
471 if ((int) (selected_item - paint_start) < 0)
472 selected_item = NULL;
474 p = paint_start;
475 n = paint_start;
476 while ((n[0] != '\0') && (n[0] != CHAR_NODE_END) && (line < help_lines))
478 p = n;
479 n = str_cget_next_char (p);
480 memcpy (buff, p, n - p);
481 buff[n - p] = '\0';
483 c = (unsigned char) buff[0];
484 switch (c)
486 case CHAR_LINK_START:
487 if (selected_item == NULL)
488 selected_item = p;
489 if (p != selected_item)
490 tty_setcolor (HELP_LINK_COLOR);
491 else
493 tty_setcolor (HELP_SLINK_COLOR);
495 /* Store the coordinates of the link */
496 active_col = col + 2;
497 active_line = line + 2;
499 start_link_area (col, line, p);
500 break;
501 case CHAR_LINK_POINTER:
502 painting = FALSE;
503 break;
504 case CHAR_LINK_END:
505 painting = TRUE;
506 help_print_word (h, word, &col, &line, FALSE);
507 end_link_area (col - 1, line);
508 tty_setcolor (HELP_NORMAL_COLOR);
509 break;
510 case CHAR_ALTERNATE:
511 acs = TRUE;
512 break;
513 case CHAR_NORMAL:
514 acs = FALSE;
515 break;
516 case CHAR_VERSION:
517 dlg_move (h, line + 2, col + 2);
518 tty_print_string (VERSION);
519 col += str_term_width1 (VERSION);
520 break;
521 case CHAR_FONT_BOLD:
522 tty_setcolor (HELP_BOLD_COLOR);
523 break;
524 case CHAR_FONT_ITALIC:
525 tty_setcolor (HELP_ITALIC_COLOR);
526 break;
527 case CHAR_FONT_NORMAL:
528 help_print_word (h, word, &col, &line, FALSE);
529 tty_setcolor (HELP_NORMAL_COLOR);
530 break;
531 case '\n':
532 if (painting)
533 help_print_word (h, word, &col, &line, FALSE);
534 line++;
535 col = 0;
536 break;
537 case '\t':
538 col = (col / 8 + 1) * 8;
539 if (col >= HELP_WINDOW_WIDTH)
541 line++;
542 col = 8;
544 break;
545 case ' ':
546 /* word delimeter */
547 if (painting)
548 help_print_word (h, word, &col, &line, TRUE);
549 break;
550 default:
551 if (painting && (line < help_lines))
553 if (!acs)
554 /* accumulate symbols in a word */
555 g_string_append (word, buff);
556 else if (col < HELP_WINDOW_WIDTH)
558 dlg_move (h, line + 2, col + 2);
560 if ((c == ' ') || (c == '.'))
561 tty_print_char (c);
562 else
563 #ifndef HAVE_SLANG
564 tty_print_char (acs_map[c]);
565 #else
566 SLsmg_draw_object (h->y + line + 2, h->x + col + 2, c);
567 #endif
568 col++;
574 /* print last word */
575 if (n[0] == CHAR_NODE_END)
576 help_print_word (h, word, &col, &line, FALSE);
578 last_shown = p;
579 end_of_node = line < help_lines;
580 tty_setcolor (HELP_NORMAL_COLOR);
581 if ((int) (selected_item - last_shown) >= 0)
583 if ((link_area == NULL) || (link_area->data == NULL))
584 selected_item = NULL;
585 else
587 selected_item = ((Link_Area *) link_area->data)->link_name;
588 repeat_paint = TRUE;
592 while (repeat_paint);
594 g_string_free (word, TRUE);
596 /* Position the cursor over a nice link */
597 if (active_col)
598 dlg_move (h, active_line, active_col);
601 /* --------------------------------------------------------------------------------------------- */
603 static int
604 help_event (Gpm_Event * event, void *vp)
606 Widget *w = (Widget *) vp;
607 GSList *current_area;
608 Gpm_Event local;
610 if (!mouse_global_in_widget (event, w))
611 return MOU_UNHANDLED;
613 if ((event->type & GPM_UP) == 0)
614 return MOU_NORMAL;
616 local = mouse_get_local (event, w);
618 /* The event is relative to the dialog window, adjust it: */
619 local.x -= 2;
620 local.y -= 2;
622 if ((local.buttons & GPM_B_RIGHT) != 0)
624 currentpoint = history[history_ptr].page;
625 selected_item = history[history_ptr].link;
626 history_ptr--;
627 if (history_ptr < 0)
628 history_ptr = HISTORY_SIZE - 1;
630 help_callback (w->owner, NULL, DLG_DRAW, 0, NULL);
631 return MOU_NORMAL;
634 /* Test whether the mouse click is inside one of the link areas */
635 for (current_area = link_area; current_area != NULL; current_area = g_slist_next (current_area))
637 Link_Area *la = (Link_Area *) current_area->data;
639 /* Test one line link area */
640 if (local.y == la->y1 && local.x >= la->x1 && local.y == la->y2 && local.x <= la->x2)
641 break;
643 /* Test two line link area */
644 if (la->y1 + 1 == la->y2)
646 /* The first line */
647 if (local.y == la->y1 && local.x >= la->x1)
648 break;
649 /* The second line */
650 if (local.y == la->y2 && local.x <= la->x2)
651 break;
653 /* Mouse will not work with link areas of more than two lines */
656 /* Test whether a link area was found */
657 if (current_area != NULL)
659 Link_Area *la = (Link_Area *) current_area->data;
661 /* The click was inside a link area -> follow the link */
662 history_ptr = (history_ptr + 1) % HISTORY_SIZE;
663 history[history_ptr].page = currentpoint;
664 history[history_ptr].link = la->link_name;
665 currentpoint = help_follow_link (currentpoint, la->link_name);
666 selected_item = NULL;
668 else if (local.y < 0)
669 move_backward (help_lines - 1);
670 else if (local.y >= help_lines)
671 move_forward (help_lines - 1);
672 else if (local.y < help_lines / 2)
673 move_backward (1);
674 else
675 move_forward (1);
677 /* Show the new node */
678 help_callback (w->owner, NULL, DLG_DRAW, 0, NULL);
680 return MOU_NORMAL;
683 /* --------------------------------------------------------------------------------------------- */
684 /** show help */
686 static void
687 help_help (Dlg_head * h)
689 const char *p;
691 history_ptr = (history_ptr + 1) % HISTORY_SIZE;
692 history[history_ptr].page = currentpoint;
693 history[history_ptr].link = selected_item;
695 p = search_string (fdata, "[How to use help]");
696 if (p != NULL)
698 currentpoint = p + 1; /* Skip the newline following the start of the node */
699 selected_item = NULL;
700 help_callback (h, NULL, DLG_DRAW, 0, NULL);
704 /* --------------------------------------------------------------------------------------------- */
706 static void
707 help_index (Dlg_head * h)
709 const char *new_item;
711 new_item = search_string (fdata, "[Contents]");
713 if (new_item == NULL)
714 message (D_ERROR, MSG_ERROR, _("Cannot find node %s in help file"), "[Contents]");
715 else
717 history_ptr = (history_ptr + 1) % HISTORY_SIZE;
718 history[history_ptr].page = currentpoint;
719 history[history_ptr].link = selected_item;
721 currentpoint = new_item + 1; /* Skip the newline following the start of the node */
722 selected_item = NULL;
723 help_callback (h, NULL, DLG_DRAW, 0, NULL);
727 /* --------------------------------------------------------------------------------------------- */
729 static void
730 help_back (Dlg_head * h)
732 currentpoint = history[history_ptr].page;
733 selected_item = history[history_ptr].link;
734 history_ptr--;
735 if (history_ptr < 0)
736 history_ptr = HISTORY_SIZE - 1;
738 help_callback (h, NULL, DLG_DRAW, 0, NULL); /* FIXME: unneeded? */
741 /* --------------------------------------------------------------------------------------------- */
743 static void
744 help_next_link (gboolean move_down)
746 const char *new_item;
748 new_item = select_next_link (selected_item);
749 if (new_item != NULL)
751 selected_item = new_item;
752 if ((int) (selected_item - last_shown) >= 0)
754 if (move_down)
755 move_forward (1);
756 else
757 selected_item = NULL;
760 else if (move_down)
761 move_forward (1);
762 else
763 selected_item = NULL;
766 /* --------------------------------------------------------------------------------------------- */
768 static void
769 help_prev_link (gboolean move_up)
771 const char *new_item;
773 new_item = select_prev_link (selected_item);
774 selected_item = new_item;
775 if ((selected_item == NULL) || (selected_item < currentpoint))
777 if (move_up)
778 move_backward (1);
779 else if ((link_area != NULL) && (link_area->data != NULL))
780 selected_item = ((Link_Area *) link_area->data)->link_name;
781 else
782 selected_item = NULL;
786 /* --------------------------------------------------------------------------------------------- */
788 static void
789 help_next_node (void)
791 const char *new_item;
793 new_item = currentpoint;
794 while ((*new_item != '\0') && (*new_item != CHAR_NODE_END))
795 new_item++;
797 if (*++new_item == '[')
798 while (*++new_item != '\0')
799 if ((*new_item == ']') && (*++new_item != '\0') && (*++new_item != '\0'))
801 currentpoint = new_item;
802 selected_item = NULL;
803 break;
807 /* --------------------------------------------------------------------------------------------- */
809 static void
810 help_prev_node (void)
812 const char *new_item;
814 new_item = currentpoint;
815 while (((int) (new_item - fdata) > 1) && (*new_item != CHAR_NODE_END))
816 new_item--;
817 new_item--;
818 while (((int) (new_item - fdata) > 0) && (*new_item != CHAR_NODE_END))
819 new_item--;
820 while (*new_item != ']')
821 new_item++;
822 currentpoint = new_item + 2;
823 selected_item = NULL;
826 /* --------------------------------------------------------------------------------------------- */
828 static void
829 help_select_link (void)
831 /* follow link */
832 if (selected_item == NULL)
834 #ifdef WE_WANT_TO_GO_BACKWARD_ON_KEY_RIGHT
835 /* Is there any reason why the right key would take us
836 * backward if there are no links selected?, I agree
837 * with Torben than doing nothing in this case is better
839 /* If there are no links, go backward in history */
840 history_ptr--;
841 if (history_ptr < 0)
842 history_ptr = HISTORY_SIZE - 1;
844 currentpoint = history[history_ptr].page;
845 selected_item = history[history_ptr].link;
846 #endif
848 else
850 history_ptr = (history_ptr + 1) % HISTORY_SIZE;
851 history[history_ptr].page = currentpoint;
852 history[history_ptr].link = selected_item;
853 currentpoint = help_follow_link (currentpoint, selected_item);
856 selected_item = NULL;
859 /* --------------------------------------------------------------------------------------------- */
861 static cb_ret_t
862 help_execute_cmd (unsigned long command)
864 cb_ret_t ret = MSG_HANDLED;
866 switch (command)
868 case CK_Help:
869 help_help (whelp);
870 break;
871 case CK_Index:
872 help_index (whelp);
873 break;
874 case CK_Back:
875 help_back (whelp);
876 break;
877 case CK_Up:
878 help_prev_link (TRUE);
879 break;
880 case CK_Down:
881 help_next_link (TRUE);
882 break;
883 case CK_PageDown:
884 move_forward (help_lines - 1);
885 break;
886 case CK_PageUp:
887 move_backward (help_lines - 1);
888 break;
889 case CK_HalfPageDown:
890 move_forward (help_lines / 2);
891 break;
892 case CK_HalfPageUp:
893 move_backward (help_lines / 2);
894 break;
895 case CK_Top:
896 move_to_top ();
897 break;
898 case CK_Bottom:
899 move_to_bottom ();
900 break;
901 case CK_Enter:
902 help_select_link ();
903 break;
904 case CK_LinkNext:
905 help_next_link (FALSE);
906 break;
907 case CK_LinkPrev:
908 help_prev_link (FALSE);
909 break;
910 case CK_NodeNext:
911 help_next_node ();
912 break;
913 case CK_NodePrev:
914 help_prev_node ();
915 break;
916 case CK_Quit:
917 dlg_stop (whelp);
918 break;
919 default:
920 ret = MSG_NOT_HANDLED;
923 return ret;
926 /* --------------------------------------------------------------------------------------------- */
928 static cb_ret_t
929 help_handle_key (Dlg_head * h, int c)
931 unsigned long command;
933 command = keybind_lookup_keymap_command (help_map, c);
934 if ((command == CK_IgnoreKey) || (help_execute_cmd (command) == MSG_NOT_HANDLED))
935 return MSG_NOT_HANDLED;
937 help_callback (h, NULL, DLG_DRAW, 0, NULL);
938 return MSG_HANDLED;
941 /* --------------------------------------------------------------------------------------------- */
943 static cb_ret_t
944 help_callback (Dlg_head * h, Widget * sender, dlg_msg_t msg, int parm, void *data)
946 WButtonBar *bb;
948 switch (msg)
950 case DLG_RESIZE:
951 help_lines = min (LINES - 4, max (2 * LINES / 3, 18));
952 dlg_set_size (h, help_lines + 4, HELP_WINDOW_WIDTH + 4);
953 bb = find_buttonbar (h);
954 widget_set_size (&bb->widget, LINES - 1, 0, 1, COLS);
955 return MSG_HANDLED;
957 case DLG_DRAW:
958 common_dialog_repaint (h);
959 help_show (h, currentpoint);
960 return MSG_HANDLED;
962 case DLG_KEY:
963 return help_handle_key (h, parm);
965 case DLG_ACTION:
966 /* shortcut */
967 if (sender == NULL)
968 return help_execute_cmd (parm);
969 /* message from buttonbar */
970 if (sender == (Widget *) find_buttonbar (h))
972 if (data != NULL)
973 return send_message ((Widget *) data, WIDGET_COMMAND, parm);
974 return help_execute_cmd (parm);
976 return MSG_NOT_HANDLED;
978 default:
979 return default_dlg_callback (h, sender, msg, parm, data);
983 /* --------------------------------------------------------------------------------------------- */
985 static void
986 interactive_display_finish (void)
988 clear_link_areas ();
991 /* --------------------------------------------------------------------------------------------- */
992 /** translate help file into terminal encoding */
994 static void
995 translate_file (char *filedata)
997 GIConv conv;
998 GString *translated_data;
1000 /* initial allocation for largest whole help file */
1001 translated_data = g_string_sized_new (32 * 1024);
1003 conv = str_crt_conv_from ("UTF-8");
1005 if (conv == INVALID_CONV)
1006 g_string_free (translated_data, TRUE);
1007 else
1009 g_free (fdata);
1011 if (str_convert (conv, filedata, translated_data) != ESTR_FAILURE)
1012 fdata = g_string_free (translated_data, FALSE);
1013 else
1015 fdata = NULL;
1016 g_string_free (translated_data, TRUE);
1018 str_close_conv (conv);
1022 /* --------------------------------------------------------------------------------------------- */
1024 static cb_ret_t
1025 md_callback (Widget * w, widget_msg_t msg, int parm)
1027 switch (msg)
1029 case WIDGET_RESIZED:
1030 w->lines = help_lines;
1031 return MSG_HANDLED;
1033 default:
1034 return default_proc (msg, parm);
1038 /* --------------------------------------------------------------------------------------------- */
1040 static Widget *
1041 mousedispatch_new (int y, int x, int yl, int xl)
1043 Widget *w = g_new (Widget, 1);
1044 init_widget (w, y, x, yl, xl, md_callback, help_event);
1045 return w;
1048 /* --------------------------------------------------------------------------------------------- */
1049 /*** public functions ****************************************************************************/
1050 /* --------------------------------------------------------------------------------------------- */
1052 /* event callback */
1053 gboolean
1054 help_interactive_display (const gchar * event_group_name, const gchar * event_name,
1055 gpointer init_data, gpointer data)
1057 const dlg_colors_t help_colors = {
1058 HELP_NORMAL_COLOR, /* common text color */
1059 0, /* unused in help */
1060 HELP_BOLD_COLOR, /* bold text color */
1061 0, /* unused in help */
1062 HELP_TITLE_COLOR /* title color */
1065 WButtonBar *help_bar;
1066 Widget *md;
1067 char *hlpfile = NULL;
1068 char *filedata;
1069 ev_help_t *event_data = (ev_help_t *) data;
1071 (void) event_group_name;
1072 (void) event_name;
1073 (void) init_data;
1075 if (event_data->filename != NULL)
1076 g_file_get_contents (event_data->filename, &filedata, NULL, NULL);
1077 else
1078 filedata = load_mc_home_file (mc_global.share_data_dir, MC_HELP, &hlpfile);
1080 if (filedata == NULL)
1081 message (D_ERROR, MSG_ERROR, _("Cannot open file %s\n%s"),
1082 event_data->filename ? event_data->filename : hlpfile, unix_error_string (errno));
1084 g_free (hlpfile);
1086 if (filedata == NULL)
1087 return TRUE;
1089 translate_file (filedata);
1091 g_free (filedata);
1093 if (fdata == NULL)
1094 return TRUE;
1096 if ((event_data->node == NULL) || (*event_data->node == '\0'))
1097 event_data->node = "[main]";
1099 main_node = search_string (fdata, event_data->node);
1101 if (main_node == NULL)
1103 message (D_ERROR, MSG_ERROR, _("Cannot find node %s in help file"), event_data->node);
1105 /* Fallback to [main], return if it also cannot be found */
1106 main_node = search_string (fdata, "[main]");
1107 if (main_node == NULL)
1109 interactive_display_finish ();
1110 return TRUE;
1114 help_lines = min (LINES - 4, max (2 * LINES / 3, 18));
1116 whelp =
1117 create_dlg (TRUE, 0, 0, help_lines + 4, HELP_WINDOW_WIDTH + 4,
1118 help_colors, help_callback, NULL, "[Help]", _("Help"),
1119 DLG_TRYUP | DLG_CENTER | DLG_WANT_TAB);
1121 selected_item = search_string_node (main_node, STRING_LINK_START) - 1;
1122 currentpoint = main_node + 1; /* Skip the newline following the start of the node */
1124 for (history_ptr = HISTORY_SIZE; history_ptr;)
1126 history_ptr--;
1127 history[history_ptr].page = currentpoint;
1128 history[history_ptr].link = selected_item;
1131 help_bar = buttonbar_new (TRUE);
1132 help_bar->widget.y -= whelp->y;
1133 help_bar->widget.x -= whelp->x;
1135 md = mousedispatch_new (1, 1, help_lines, HELP_WINDOW_WIDTH - 2);
1137 add_widget (whelp, md);
1138 add_widget (whelp, help_bar);
1140 buttonbar_set_label (help_bar, 1, Q_ ("ButtonBar|Help"), help_map, NULL);
1141 buttonbar_set_label (help_bar, 2, Q_ ("ButtonBar|Index"), help_map, NULL);
1142 buttonbar_set_label (help_bar, 3, Q_ ("ButtonBar|Prev"), help_map, NULL);
1143 buttonbar_set_label (help_bar, 4, "", help_map, NULL);
1144 buttonbar_set_label (help_bar, 5, "", help_map, NULL);
1145 buttonbar_set_label (help_bar, 6, "", help_map, NULL);
1146 buttonbar_set_label (help_bar, 7, "", help_map, NULL);
1147 buttonbar_set_label (help_bar, 8, "", help_map, NULL);
1148 buttonbar_set_label (help_bar, 9, "", help_map, NULL);
1149 buttonbar_set_label (help_bar, 10, Q_ ("ButtonBar|Quit"), help_map, NULL);
1151 run_dlg (whelp);
1152 interactive_display_finish ();
1153 destroy_dlg (whelp);
1154 return TRUE;
1157 /* --------------------------------------------------------------------------------------------- */