Merge branch '3205_eta'
[midnight-commander.git] / src / help.c
blob6f24205558414c62083c34cd706b51b82c26941a
1 /*
2 Hypertext file browser.
4 Copyright (C) 1994-2024
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 facilitate
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 * Laziness/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 <limits.h> /* MB_LEN_MAX */
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/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 "keymap.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 /*** forward declarations (file scope functions) *************************************************/
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 WDialog *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 /* --------------------------------------------------------------------------------------------- */
119 /*** file scope functions ************************************************************************/
120 /* --------------------------------------------------------------------------------------------- */
122 /** returns the position where text was found in the start buffer
123 * or 0 if not found
125 static const char *
126 search_string (const char *start, const char *text)
128 const char *result = NULL;
129 char *local_text;
130 char *d;
131 const char *e = start;
133 local_text = g_strdup (text);
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 for (d = local_text; *d != '\0'; str_next_char (&d))
138 if (*d == '\n')
139 *d = ' ';
141 /* Do search */
142 for (d = local_text; *e != '\0'; 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 if (start != NULL)
170 const char *d = text;
171 const char *e;
173 for (e = start; *e != '\0' && *e != CHAR_NODE_END; e++)
175 if (*d == *e)
176 d++;
177 else
178 d = text;
179 if (*d == '\0')
180 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 const char *p;
302 if (lc_selected_item == NULL)
303 return start;
305 for (p = lc_selected_item; *p != '\0' && *p != CHAR_NODE_END && *p != CHAR_LINK_POINTER; p++)
307 if (*p == CHAR_LINK_POINTER)
309 int i;
310 char link_name[MAXLINKNAME];
312 link_name[0] = '[';
313 for (i = 1;
314 *p != CHAR_LINK_END && *p != '\0' && *p != CHAR_NODE_END && i < MAXLINKNAME - 3;)
315 link_name[i++] = *++p;
316 link_name[i - 1] = ']';
317 link_name[i] = '\0';
318 p = search_string (fdata, link_name);
319 if (p != NULL)
321 p += 1; /* Skip the newline following the start of the node */
322 return p;
326 /* Create a replacement page with the error message */
327 return _("Help file format error\n");
330 /* --------------------------------------------------------------------------------------------- */
332 static const char *
333 select_next_link (const char *current_link)
335 const char *p;
337 if (current_link == NULL)
338 return NULL;
340 p = search_string_node (current_link, STRING_LINK_END);
341 if (p == NULL)
342 return NULL;
343 p = search_string_node (p, STRING_LINK_START);
344 if (p == NULL)
345 return NULL;
346 return p - 1;
349 /* --------------------------------------------------------------------------------------------- */
351 static const char *
352 select_prev_link (const char *current_link)
354 return current_link == NULL ? NULL : search_char_node (current_link - 1, CHAR_LINK_START, -1);
357 /* --------------------------------------------------------------------------------------------- */
359 static void
360 start_link_area (int x, int y, const char *link_name)
362 Link_Area *la;
364 if (inside_link_area)
365 message (D_NORMAL, _("Warning"), "%s", _("Internal bug: Double start of link area"));
367 /* Allocate memory for a new link area */
368 la = g_new (Link_Area, 1);
369 /* Save the beginning coordinates of the link area */
370 la->x1 = x;
371 la->y1 = y;
372 /* Save the name of the destination anchor */
373 la->link_name = link_name;
374 link_area = g_slist_prepend (link_area, la);
376 inside_link_area = TRUE;
379 /* --------------------------------------------------------------------------------------------- */
381 static void
382 end_link_area (int x, int y)
384 if (inside_link_area)
386 Link_Area *la = (Link_Area *) link_area->data;
387 /* Save the end coordinates of the link area */
388 la->x2 = x;
389 la->y2 = y;
390 inside_link_area = FALSE;
394 /* --------------------------------------------------------------------------------------------- */
396 static void
397 clear_link_areas (void)
399 g_clear_slist (&link_area, g_free);
400 inside_link_area = FALSE;
403 /* --------------------------------------------------------------------------------------------- */
405 static void
406 help_print_word (WDialog *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 widget_gotoyx (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 (WDialog *h, const char *paint_start)
452 gboolean painting = TRUE;
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 int line = 0;
464 int col = 0;
465 gboolean acs = FALSE; /* Flag: Is alternate character set active? */
466 const char *p, *n;
468 active_col = 0;
469 active_line = 0;
471 repeat_paint = FALSE;
473 clear_link_areas ();
474 if ((int) (selected_item - paint_start) < 0)
475 selected_item = NULL;
477 p = paint_start;
478 n = paint_start;
479 while ((n[0] != '\0') && (n[0] != CHAR_NODE_END) && (line < help_lines))
481 int c;
483 p = n;
484 n = str_cget_next_char (p);
485 memcpy (buff, p, n - p);
486 buff[n - p] = '\0';
488 c = (unsigned char) buff[0];
489 switch (c)
491 case CHAR_LINK_START:
492 if (selected_item == NULL)
493 selected_item = p;
494 if (p != selected_item)
495 tty_setcolor (HELP_LINK_COLOR);
496 else
498 tty_setcolor (HELP_SLINK_COLOR);
500 /* Store the coordinates of the link */
501 active_col = col + 2;
502 active_line = line + 2;
504 start_link_area (col, line, p);
505 break;
506 case CHAR_LINK_POINTER:
507 painting = FALSE;
508 break;
509 case CHAR_LINK_END:
510 painting = TRUE;
511 help_print_word (h, word, &col, &line, FALSE);
512 end_link_area (col - 1, line);
513 tty_setcolor (HELP_NORMAL_COLOR);
514 break;
515 case CHAR_ALTERNATE:
516 acs = TRUE;
517 break;
518 case CHAR_NORMAL:
519 acs = FALSE;
520 break;
521 case CHAR_VERSION:
522 widget_gotoyx (h, line + 2, col + 2);
523 tty_print_string (mc_global.mc_version);
524 col += str_term_width1 (mc_global.mc_version);
525 break;
526 case CHAR_FONT_BOLD:
527 tty_setcolor (HELP_BOLD_COLOR);
528 break;
529 case CHAR_FONT_ITALIC:
530 tty_setcolor (HELP_ITALIC_COLOR);
531 break;
532 case CHAR_FONT_NORMAL:
533 help_print_word (h, word, &col, &line, FALSE);
534 tty_setcolor (HELP_NORMAL_COLOR);
535 break;
536 case '\n':
537 if (painting)
538 help_print_word (h, word, &col, &line, FALSE);
539 line++;
540 col = 0;
541 break;
542 case ' ':
543 case '\t':
544 /* word delimiter */
545 if (painting)
547 help_print_word (h, word, &col, &line, c == ' ');
548 if (c == '\t')
550 col = (col / 8 + 1) * 8;
551 if (col >= HELP_WINDOW_WIDTH)
553 line++;
554 col = 8;
558 break;
559 default:
560 if (painting && (line < help_lines))
562 if (!acs)
563 /* accumulate symbols in a word */
564 g_string_append (word, buff);
565 else if (col < HELP_WINDOW_WIDTH)
567 widget_gotoyx (h, line + 2, col + 2);
569 if ((c == ' ') || (c == '.'))
570 tty_print_char (c);
571 else
572 #ifndef HAVE_SLANG
573 tty_print_char (acs_map[c]);
574 #else
575 SLsmg_draw_object (WIDGET (h)->rect.y + line + 2,
576 WIDGET (h)->rect.x + col + 2, c);
577 #endif
578 col++;
584 /* print last word */
585 if (n[0] == CHAR_NODE_END)
586 help_print_word (h, word, &col, &line, FALSE);
588 last_shown = p;
589 end_of_node = line < help_lines;
590 tty_setcolor (HELP_NORMAL_COLOR);
591 if ((int) (selected_item - last_shown) >= 0)
593 if ((link_area == NULL) || (link_area->data == NULL))
594 selected_item = NULL;
595 else
597 selected_item = ((Link_Area *) link_area->data)->link_name;
598 repeat_paint = TRUE;
602 while (repeat_paint);
604 g_string_free (word, TRUE);
606 /* Position the cursor over a nice link */
607 if (active_col != 0)
608 widget_gotoyx (h, active_line, active_col);
611 /* --------------------------------------------------------------------------------------------- */
612 /** show help */
614 static void
615 help_help (WDialog *h)
617 const char *p;
619 history_ptr = (history_ptr + 1) % HISTORY_SIZE;
620 history[history_ptr].page = currentpoint;
621 history[history_ptr].link = selected_item;
623 p = search_string (fdata, "[How to use help]");
624 if (p != NULL)
626 currentpoint = p + 1; /* Skip the newline following the start of the node */
627 selected_item = NULL;
628 widget_draw (WIDGET (h));
632 /* --------------------------------------------------------------------------------------------- */
634 static void
635 help_index (WDialog *h)
637 const char *new_item;
639 new_item = search_string (fdata, "[Contents]");
641 if (new_item == NULL)
642 message (D_ERROR, MSG_ERROR, _("Cannot find node %s in help file"), "[Contents]");
643 else
645 history_ptr = (history_ptr + 1) % HISTORY_SIZE;
646 history[history_ptr].page = currentpoint;
647 history[history_ptr].link = selected_item;
649 currentpoint = new_item + 1; /* Skip the newline following the start of the node */
650 selected_item = NULL;
651 widget_draw (WIDGET (h));
655 /* --------------------------------------------------------------------------------------------- */
657 static void
658 help_back (WDialog *h)
660 currentpoint = history[history_ptr].page;
661 selected_item = history[history_ptr].link;
662 history_ptr--;
663 if (history_ptr < 0)
664 history_ptr = HISTORY_SIZE - 1;
666 widget_draw (WIDGET (h)); /* FIXME: unneeded? */
669 /* --------------------------------------------------------------------------------------------- */
671 static void
672 help_next_link (gboolean move_down)
674 const char *new_item;
676 new_item = select_next_link (selected_item);
677 if (new_item != NULL)
679 selected_item = new_item;
680 if ((int) (selected_item - last_shown) >= 0)
682 if (move_down)
683 move_forward (1);
684 else
685 selected_item = NULL;
688 else if (move_down)
689 move_forward (1);
690 else
691 selected_item = NULL;
694 /* --------------------------------------------------------------------------------------------- */
696 static void
697 help_prev_link (gboolean move_up)
699 const char *new_item;
701 new_item = select_prev_link (selected_item);
702 selected_item = new_item;
703 if ((selected_item == NULL) || (selected_item < currentpoint))
705 if (move_up)
706 move_backward (1);
707 else if ((link_area != NULL) && (link_area->data != NULL))
708 selected_item = ((Link_Area *) link_area->data)->link_name;
709 else
710 selected_item = NULL;
714 /* --------------------------------------------------------------------------------------------- */
716 static void
717 help_next_node (void)
719 const char *new_item;
721 new_item = currentpoint;
722 while ((*new_item != '\0') && (*new_item != CHAR_NODE_END))
723 new_item++;
725 if (*++new_item == '[')
726 while (*++new_item != '\0')
727 if ((*new_item == ']') && (*++new_item != '\0') && (*++new_item != '\0'))
729 currentpoint = new_item;
730 selected_item = NULL;
731 break;
735 /* --------------------------------------------------------------------------------------------- */
737 static void
738 help_prev_node (void)
740 const char *new_item;
742 new_item = currentpoint;
743 while (((int) (new_item - fdata) > 1) && (*new_item != CHAR_NODE_END))
744 new_item--;
745 new_item--;
746 while (((int) (new_item - fdata) > 0) && (*new_item != CHAR_NODE_END))
747 new_item--;
748 while (*new_item != ']')
749 new_item++;
750 currentpoint = new_item + 2;
751 selected_item = NULL;
754 /* --------------------------------------------------------------------------------------------- */
756 static void
757 help_select_link (void)
759 /* follow link */
760 if (selected_item == NULL)
762 #ifdef WE_WANT_TO_GO_BACKWARD_ON_KEY_RIGHT
763 /* Is there any reason why the right key would take us
764 * backward if there are no links selected?, I agree
765 * with Torben than doing nothing in this case is better
767 /* If there are no links, go backward in history */
768 history_ptr--;
769 if (history_ptr < 0)
770 history_ptr = HISTORY_SIZE - 1;
772 currentpoint = history[history_ptr].page;
773 selected_item = history[history_ptr].link;
774 #endif
776 else
778 history_ptr = (history_ptr + 1) % HISTORY_SIZE;
779 history[history_ptr].page = currentpoint;
780 history[history_ptr].link = selected_item;
781 currentpoint = help_follow_link (currentpoint, selected_item);
784 selected_item = NULL;
787 /* --------------------------------------------------------------------------------------------- */
789 static cb_ret_t
790 help_execute_cmd (long command)
792 cb_ret_t ret = MSG_HANDLED;
794 switch (command)
796 case CK_Help:
797 help_help (whelp);
798 break;
799 case CK_Index:
800 help_index (whelp);
801 break;
802 case CK_Back:
803 help_back (whelp);
804 break;
805 case CK_Up:
806 help_prev_link (TRUE);
807 break;
808 case CK_Down:
809 help_next_link (TRUE);
810 break;
811 case CK_PageDown:
812 move_forward (help_lines - 1);
813 break;
814 case CK_PageUp:
815 move_backward (help_lines - 1);
816 break;
817 case CK_HalfPageDown:
818 move_forward (help_lines / 2);
819 break;
820 case CK_HalfPageUp:
821 move_backward (help_lines / 2);
822 break;
823 case CK_Top:
824 move_to_top ();
825 break;
826 case CK_Bottom:
827 move_to_bottom ();
828 break;
829 case CK_Enter:
830 help_select_link ();
831 break;
832 case CK_LinkNext:
833 help_next_link (FALSE);
834 break;
835 case CK_LinkPrev:
836 help_prev_link (FALSE);
837 break;
838 case CK_NodeNext:
839 help_next_node ();
840 break;
841 case CK_NodePrev:
842 help_prev_node ();
843 break;
844 case CK_Quit:
845 dlg_close (whelp);
846 break;
847 default:
848 ret = MSG_NOT_HANDLED;
851 return ret;
854 /* --------------------------------------------------------------------------------------------- */
856 static cb_ret_t
857 help_handle_key (WDialog *h, int key)
859 Widget *w = WIDGET (h);
860 long command;
862 command = widget_lookup_key (w, key);
863 if (command == CK_IgnoreKey)
864 return MSG_NOT_HANDLED;
866 return help_execute_cmd (command);
869 /* --------------------------------------------------------------------------------------------- */
871 static cb_ret_t
872 help_bg_callback (Widget *w, Widget *sender, widget_msg_t msg, int parm, void *data)
874 switch (msg)
876 case MSG_DRAW:
877 frame_callback (w, NULL, MSG_DRAW, 0, NULL);
878 help_show (DIALOG (w->owner), currentpoint);
879 return MSG_HANDLED;
881 default:
882 return frame_callback (w, sender, msg, parm, data);
886 /* --------------------------------------------------------------------------------------------- */
888 static cb_ret_t
889 help_resize (WDialog *h)
891 Widget *w = WIDGET (h);
892 WButtonBar *bb;
893 WRect r = w->rect;
895 help_lines = MIN (LINES - 4, MAX (2 * LINES / 3, 18));
896 r.lines = help_lines + 4;
897 r.cols = HELP_WINDOW_WIDTH + 4;
898 dlg_default_callback (w, NULL, MSG_RESIZE, 0, &r);
899 bb = buttonbar_find (h);
900 widget_set_size (WIDGET (bb), LINES - 1, 0, 1, COLS);
902 return MSG_HANDLED;
905 /* --------------------------------------------------------------------------------------------- */
907 static cb_ret_t
908 help_callback (Widget *w, Widget *sender, widget_msg_t msg, int parm, void *data)
910 WDialog *h = DIALOG (w);
912 switch (msg)
914 case MSG_RESIZE:
915 return help_resize (h);
917 case MSG_KEY:
919 cb_ret_t ret;
921 ret = help_handle_key (h, parm);
922 if (ret == MSG_HANDLED)
923 widget_draw (w);
925 return ret;
928 case MSG_ACTION:
929 /* Handle shortcuts and buttonbar. */
930 return help_execute_cmd (parm);
932 default:
933 return dlg_default_callback (w, sender, msg, parm, data);
937 /* --------------------------------------------------------------------------------------------- */
939 static void
940 interactive_display_finish (void)
942 clear_link_areas ();
945 /* --------------------------------------------------------------------------------------------- */
946 /** translate help file into terminal encoding */
948 static void
949 translate_file (char *filedata)
951 GIConv conv;
953 conv = str_crt_conv_from ("UTF-8");
954 if (conv != INVALID_CONV)
956 GString *translated_data;
957 gboolean nok;
959 g_free (fdata);
961 /* initial allocation for largest whole help file */
962 translated_data = g_string_sized_new (32 * 1024);
963 nok = (str_convert (conv, filedata, translated_data) == ESTR_FAILURE);
964 fdata = g_string_free (translated_data, nok);
966 str_close_conv (conv);
970 /* --------------------------------------------------------------------------------------------- */
972 static cb_ret_t
973 md_callback (Widget *w, Widget *sender, widget_msg_t msg, int parm, void *data)
975 switch (msg)
977 case MSG_RESIZE:
978 widget_default_callback (w, NULL, MSG_RESIZE, 0, data);
979 w->rect.lines = help_lines;
980 return MSG_HANDLED;
982 default:
983 return widget_default_callback (w, sender, msg, parm, data);
987 /* --------------------------------------------------------------------------------------------- */
989 static void
990 help_mouse_callback (Widget *w, mouse_msg_t msg, mouse_event_t *event)
992 int x, y;
993 GSList *current_area;
995 if (msg != MSG_MOUSE_CLICK)
996 return;
998 if ((event->buttons & GPM_B_RIGHT) != 0)
1000 /* Right button click */
1001 help_back (whelp);
1002 return;
1005 /* Left bytton click */
1007 /* The event is relative to the dialog window, adjust it: */
1008 x = event->x - 1;
1009 y = event->y - 1;
1011 /* Test whether the mouse click is inside one of the link areas */
1012 for (current_area = link_area; current_area != NULL; current_area = g_slist_next (current_area))
1014 Link_Area *la = (Link_Area *) current_area->data;
1016 /* Test one line link area */
1017 if (y == la->y1 && x >= la->x1 && y == la->y2 && x <= la->x2)
1018 break;
1020 /* Test two line link area */
1021 if (la->y1 + 1 == la->y2)
1023 /* The first line || The second line */
1024 if ((y == la->y1 && x >= la->x1) || (y == la->y2 && x <= la->x2))
1025 break;
1027 /* Mouse will not work with link areas of more than two lines */
1030 /* Test whether a link area was found */
1031 if (current_area != NULL)
1033 Link_Area *la = (Link_Area *) current_area->data;
1035 /* The click was inside a link area -> follow the link */
1036 history_ptr = (history_ptr + 1) % HISTORY_SIZE;
1037 history[history_ptr].page = currentpoint;
1038 history[history_ptr].link = la->link_name;
1039 currentpoint = help_follow_link (currentpoint, la->link_name);
1040 selected_item = NULL;
1042 else if (y < 0)
1043 move_backward (help_lines - 1);
1044 else if (y >= help_lines)
1045 move_forward (help_lines - 1);
1046 else if (y < help_lines / 2)
1047 move_backward (1);
1048 else
1049 move_forward (1);
1051 /* Show the new node */
1052 widget_draw (WIDGET (w->owner));
1055 /* --------------------------------------------------------------------------------------------- */
1057 static Widget *
1058 mousedispatch_new (const WRect *r)
1060 Widget *w;
1062 w = g_new0 (Widget, 1);
1063 widget_init (w, r, md_callback, help_mouse_callback);
1064 w->options |= WOP_SELECTABLE | WOP_WANT_CURSOR;
1066 return w;
1069 /* --------------------------------------------------------------------------------------------- */
1070 /*** public functions ****************************************************************************/
1071 /* --------------------------------------------------------------------------------------------- */
1073 /* event callback */
1074 gboolean
1075 help_interactive_display (const gchar *event_group_name, const gchar *event_name,
1076 gpointer init_data, gpointer data)
1078 const dlg_colors_t help_colors = {
1079 HELP_NORMAL_COLOR, /* common text color */
1080 0, /* unused in help */
1081 HELP_BOLD_COLOR, /* bold text color */
1082 0, /* unused in help */
1083 HELP_TITLE_COLOR /* title color */
1086 Widget *wh;
1087 WGroup *g;
1088 WButtonBar *help_bar;
1089 Widget *md;
1090 char *hlpfile = NULL;
1091 char *filedata;
1092 ev_help_t *event_data = (ev_help_t *) data;
1093 WRect r = { 1, 1, 1, 1 };
1095 (void) event_group_name;
1096 (void) event_name;
1097 (void) init_data;
1099 if (event_data->filename != NULL)
1100 g_file_get_contents (event_data->filename, &filedata, NULL, NULL);
1101 else
1102 filedata = load_mc_home_file (mc_global.share_data_dir, MC_HELP, &hlpfile, NULL);
1104 if (filedata == NULL)
1105 message (D_ERROR, MSG_ERROR, _("Cannot open file %s\n%s"),
1106 event_data->filename ? event_data->filename : hlpfile, unix_error_string (errno));
1108 g_free (hlpfile);
1110 if (filedata == NULL)
1111 return TRUE;
1113 translate_file (filedata);
1115 g_free (filedata);
1117 if (fdata == NULL)
1118 return TRUE;
1120 if ((event_data->node == NULL) || (*event_data->node == '\0'))
1121 event_data->node = "[main]";
1123 main_node = search_string (fdata, event_data->node);
1125 if (main_node == NULL)
1127 message (D_ERROR, MSG_ERROR, _("Cannot find node %s in help file"), event_data->node);
1129 /* Fallback to [main], return if it also cannot be found */
1130 main_node = search_string (fdata, "[main]");
1131 if (main_node == NULL)
1133 interactive_display_finish ();
1134 return TRUE;
1138 help_lines = MIN (LINES - 4, MAX (2 * LINES / 3, 18));
1140 whelp =
1141 dlg_create (TRUE, 0, 0, help_lines + 4, HELP_WINDOW_WIDTH + 4, WPOS_CENTER | WPOS_TRYUP,
1142 FALSE, help_colors, help_callback, NULL, "[Help]", _("Help"));
1143 wh = WIDGET (whelp);
1144 g = GROUP (whelp);
1145 wh->keymap = help_map;
1146 widget_want_tab (wh, TRUE);
1147 /* draw background */
1148 whelp->bg->callback = help_bg_callback;
1150 selected_item = search_string_node (main_node, STRING_LINK_START) - 1;
1151 currentpoint = main_node + 1; /* Skip the newline following the start of the node */
1153 for (history_ptr = HISTORY_SIZE - 1; history_ptr >= 0; history_ptr--)
1155 history[history_ptr].page = currentpoint;
1156 history[history_ptr].link = selected_item;
1159 help_bar = buttonbar_new ();
1160 WIDGET (help_bar)->rect.y -= wh->rect.y;
1161 WIDGET (help_bar)->rect.x -= wh->rect.x;
1163 r.lines = help_lines;
1164 r.cols = HELP_WINDOW_WIDTH - 2;
1165 md = mousedispatch_new (&r);
1167 group_add_widget (g, md);
1168 group_add_widget (g, help_bar); /* FIXME */
1170 buttonbar_set_label (help_bar, 1, Q_ ("ButtonBar|Help"), wh->keymap, NULL);
1171 buttonbar_set_label (help_bar, 2, Q_ ("ButtonBar|Index"), wh->keymap, NULL);
1172 buttonbar_set_label (help_bar, 3, Q_ ("ButtonBar|Prev"), wh->keymap, NULL);
1173 buttonbar_set_label (help_bar, 4, "", wh->keymap, NULL);
1174 buttonbar_set_label (help_bar, 5, "", wh->keymap, NULL);
1175 buttonbar_set_label (help_bar, 6, "", wh->keymap, NULL);
1176 buttonbar_set_label (help_bar, 7, "", wh->keymap, NULL);
1177 buttonbar_set_label (help_bar, 8, "", wh->keymap, NULL);
1178 buttonbar_set_label (help_bar, 9, "", wh->keymap, NULL);
1179 buttonbar_set_label (help_bar, 10, Q_ ("ButtonBar|Quit"), wh->keymap, NULL);
1181 dlg_run (whelp);
1182 interactive_display_finish ();
1183 widget_destroy (wh);
1184 return TRUE;
1187 /* --------------------------------------------------------------------------------------------- */