(mcview__get_nroff_real_len): immediately return 0 if viewer is not in nroff mode..
[midnight-commander.git] / src / help.c
blobbe1e9e845a2f5a968085dbddf48a671b932e6715
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"
64 #include "keybind-defaults.h"
65 #include "keybind-defaults.h"
66 #include "help.h"
67 #include "main.h"
69 /*** global variables ****************************************************************************/
71 /*** file scope macro definitions ****************************************************************/
73 #define MAXLINKNAME 80
74 #define HISTORY_SIZE 20
75 #define HELP_WINDOW_WIDTH min(80, COLS - 16)
77 #define STRING_LINK_START "\01"
78 #define STRING_LINK_POINTER "\02"
79 #define STRING_LINK_END "\03"
80 #define STRING_NODE_END "\04"
82 /*** file scope type declarations ****************************************************************/
84 /* Link areas for the mouse */
85 typedef struct Link_Area
87 int x1, y1, x2, y2;
88 const char *link_name;
89 } Link_Area;
91 /*** file scope variables ************************************************************************/
93 static char *fdata = NULL; /* Pointer to the loaded data file */
94 static int help_lines; /* Lines in help viewer */
95 static int history_ptr; /* For the history queue */
96 static const char *main_node; /* The main node */
97 static const char *last_shown = NULL; /* Last byte shown in a screen */
98 static gboolean end_of_node = FALSE; /* Flag: the last character of the node shown? */
99 static const char *currentpoint;
100 static const char *selected_item;
102 /* The widget variables */
103 static Dlg_head *whelp;
105 static struct
107 const char *page; /* Pointer to the selected page */
108 const char *link; /* Pointer to the selected link */
109 } history[HISTORY_SIZE];
111 static GSList *link_area = NULL;
112 static gboolean inside_link_area = FALSE;
114 static cb_ret_t help_callback (Dlg_head * h, Widget * sender, dlg_msg_t msg, int parm, void *data);
116 /*** file scope functions ************************************************************************/
117 /* --------------------------------------------------------------------------------------------- */
119 /** returns the position where text was found in the start buffer
120 * or 0 if not found
122 static const char *
123 search_string (const char *start, const char *text)
125 const char *result = NULL;
126 char *local_text = g_strdup (text);
127 char *d = local_text;
128 const char *e = start;
130 /* fmt sometimes replaces a space with a newline in the help file */
131 /* Replace the newlines in the link name with spaces to correct the situation */
132 while (*d != '\0')
134 if (*d == '\n')
135 *d = ' ';
136 str_next_char (&d);
139 /* Do search */
140 for (d = local_text; *e; e++)
142 if (*d == *e)
143 d++;
144 else
145 d = local_text;
146 if (*d == '\0')
148 result = e + 1;
149 break;
153 g_free (local_text);
154 return result;
157 /* --------------------------------------------------------------------------------------------- */
158 /** Searches text in the buffer pointed by start. Search ends
159 * if the CHAR_NODE_END is found in the text.
160 * @returns 0 on failure
163 static const char *
164 search_string_node (const char *start, const char *text)
166 const char *d = text;
167 const char *e = start;
169 if (start != NULL)
170 for (; *e && *e != CHAR_NODE_END; e++)
172 if (*d == *e)
173 d++;
174 else
175 d = text;
176 if (*d == '\0')
177 return e + 1;
180 return NULL;
183 /* --------------------------------------------------------------------------------------------- */
184 /** Searches the_char in the buffer pointer by start and searches
185 * it can search forward (direction = 1) or backward (direction = -1)
188 static const char *
189 search_char_node (const char *start, char the_char, int direction)
191 const char *e;
193 for (e = start; (*e != '\0') && (*e != CHAR_NODE_END); e += direction)
194 if (*e == the_char)
195 return e;
197 return NULL;
200 /* --------------------------------------------------------------------------------------------- */
201 /** Returns the new current pointer when moved lines lines */
203 static const char *
204 move_forward2 (const char *c, int lines)
206 const char *p;
207 int line;
209 currentpoint = c;
210 for (line = 0, p = currentpoint; (*p != '\0') && (*p != CHAR_NODE_END); str_cnext_char (&p))
212 if (line == lines)
213 return currentpoint = p;
215 if (*p == '\n')
216 line++;
218 return currentpoint = c;
221 /* --------------------------------------------------------------------------------------------- */
223 static const char *
224 move_backward2 (const char *c, int lines)
226 const char *p;
227 int line;
229 currentpoint = c;
230 for (line = 0, p = currentpoint; (*p != '\0') && ((int) (p - fdata) >= 0); str_cprev_char (&p))
232 if (*p == CHAR_NODE_END)
234 /* We reached the beginning of the node */
235 /* Skip the node headers */
236 while (*p != ']')
237 str_cnext_char (&p);
238 return currentpoint = p + 2; /* Skip the newline following the start of the node */
241 if (*(p - 1) == '\n')
242 line++;
243 if (line == lines)
244 return currentpoint = p;
246 return currentpoint = c;
249 /* --------------------------------------------------------------------------------------------- */
251 static void
252 move_forward (int i)
254 if (!end_of_node)
255 currentpoint = move_forward2 (currentpoint, i);
258 /* --------------------------------------------------------------------------------------------- */
260 static void
261 move_backward (int i)
263 currentpoint = move_backward2 (currentpoint, ++i);
266 /* --------------------------------------------------------------------------------------------- */
268 static void
269 move_to_top (void)
271 while (((int) (currentpoint > fdata) > 0) && (*currentpoint != CHAR_NODE_END))
272 currentpoint--;
274 while (*currentpoint != ']')
275 currentpoint++;
276 currentpoint = currentpoint + 2; /* Skip the newline following the start of the node */
277 selected_item = NULL;
280 /* --------------------------------------------------------------------------------------------- */
282 static void
283 move_to_bottom (void)
285 while ((*currentpoint != '\0') && (*currentpoint != CHAR_NODE_END))
286 currentpoint++;
287 currentpoint--;
288 move_backward (1);
291 /* --------------------------------------------------------------------------------------------- */
293 static const char *
294 help_follow_link (const char *start, const char *lc_selected_item)
296 char link_name[MAXLINKNAME];
297 const char *p;
298 int i = 0;
300 if (lc_selected_item == NULL)
301 return start;
303 for (p = lc_selected_item; *p && *p != CHAR_NODE_END && *p != CHAR_LINK_POINTER; p++)
305 if (*p == CHAR_LINK_POINTER)
307 link_name[0] = '[';
308 for (i = 1; *p != CHAR_LINK_END && *p && *p != CHAR_NODE_END && i < MAXLINKNAME - 3;)
309 link_name[i++] = *++p;
310 link_name[i - 1] = ']';
311 link_name[i] = '\0';
312 p = search_string (fdata, link_name);
313 if (p != NULL)
315 p += 1; /* Skip the newline following the start of the node */
316 return p;
320 /* Create a replacement page with the error message */
321 return _("Help file format error\n");
324 /* --------------------------------------------------------------------------------------------- */
326 static const char *
327 select_next_link (const char *current_link)
329 const char *p;
331 if (current_link == NULL)
332 return NULL;
334 p = search_string_node (current_link, STRING_LINK_END);
335 if (p == NULL)
336 return NULL;
337 p = search_string_node (p, STRING_LINK_START);
338 if (p == NULL)
339 return NULL;
340 return p - 1;
343 /* --------------------------------------------------------------------------------------------- */
345 static const char *
346 select_prev_link (const char *current_link)
348 return current_link == NULL ? NULL : search_char_node (current_link - 1, CHAR_LINK_START, -1);
351 /* --------------------------------------------------------------------------------------------- */
353 static void
354 start_link_area (int x, int y, const char *link_name)
356 Link_Area *la;
358 if (inside_link_area)
359 message (D_NORMAL, _("Warning"), _("Internal bug: Double start of link area"));
361 /* Allocate memory for a new link area */
362 la = g_new (Link_Area, 1);
363 /* Save the beginning coordinates of the link area */
364 la->x1 = x;
365 la->y1 = y;
366 /* Save the name of the destination anchor */
367 la->link_name = link_name;
368 link_area = g_slist_prepend (link_area, la);
370 inside_link_area = TRUE;
373 /* --------------------------------------------------------------------------------------------- */
375 static void
376 end_link_area (int x, int y)
378 if (inside_link_area)
380 Link_Area *la = (Link_Area *) link_area->data;
381 /* Save the end coordinates of the link area */
382 la->x2 = x;
383 la->y2 = y;
384 inside_link_area = FALSE;
388 /* --------------------------------------------------------------------------------------------- */
390 static void
391 clear_link_areas (void)
393 g_slist_foreach (link_area, (GFunc) g_free, NULL);
394 g_slist_free (link_area);
395 link_area = NULL;
396 inside_link_area = FALSE;
399 /* --------------------------------------------------------------------------------------------- */
401 static void
402 help_print_word (Dlg_head * h, GString * word, int *col, int *line, gboolean add_space)
404 if (*line >= help_lines)
405 g_string_set_size (word, 0);
406 else
408 int w;
410 w = str_term_width1 (word->str);
411 if (*col + w >= HELP_WINDOW_WIDTH)
413 *col = 0;
414 (*line)++;
417 if (*line >= help_lines)
418 g_string_set_size (word, 0);
419 else
421 dlg_move (h, *line + 2, *col + 2);
422 tty_print_string (word->str);
423 g_string_set_size (word, 0);
424 *col += w;
428 if (add_space)
430 if (*col < HELP_WINDOW_WIDTH - 1)
432 tty_print_char (' ');
433 (*col)++;
435 else
437 *col = 0;
438 (*line)++;
443 /* --------------------------------------------------------------------------------------------- */
445 static void
446 help_show (Dlg_head * h, const char *paint_start)
448 const char *p, *n;
449 int col, line, c;
450 gboolean painting = TRUE;
451 gboolean acs; /* Flag: Alternate character set active? */
452 gboolean repeat_paint;
453 int active_col, active_line; /* Active link position */
454 char buff[MB_LEN_MAX + 1];
455 GString *word;
457 word = g_string_sized_new (32);
459 tty_setcolor (HELP_NORMAL_COLOR);
462 line = col = active_col = active_line = 0;
463 repeat_paint = FALSE;
464 acs = FALSE;
466 clear_link_areas ();
467 if ((int) (selected_item - paint_start) < 0)
468 selected_item = NULL;
470 p = paint_start;
471 n = paint_start;
472 while ((n[0] != '\0') && (n[0] != CHAR_NODE_END) && (line < help_lines))
474 p = n;
475 n = str_cget_next_char (p);
476 memcpy (buff, p, n - p);
477 buff[n - p] = '\0';
479 c = (unsigned char) buff[0];
480 switch (c)
482 case CHAR_LINK_START:
483 if (selected_item == NULL)
484 selected_item = p;
485 if (p != selected_item)
486 tty_setcolor (HELP_LINK_COLOR);
487 else
489 tty_setcolor (HELP_SLINK_COLOR);
491 /* Store the coordinates of the link */
492 active_col = col + 2;
493 active_line = line + 2;
495 start_link_area (col, line, p);
496 break;
497 case CHAR_LINK_POINTER:
498 painting = FALSE;
499 end_link_area (col - 1, line);
500 break;
501 case CHAR_LINK_END:
502 painting = TRUE;
503 help_print_word (h, word, &col, &line, FALSE);
504 tty_setcolor (HELP_NORMAL_COLOR);
505 break;
506 case CHAR_ALTERNATE:
507 acs = TRUE;
508 break;
509 case CHAR_NORMAL:
510 acs = FALSE;
511 break;
512 case CHAR_VERSION:
513 dlg_move (h, line + 2, col + 2);
514 tty_print_string (VERSION);
515 col += str_term_width1 (VERSION);
516 break;
517 case CHAR_FONT_BOLD:
518 tty_setcolor (HELP_BOLD_COLOR);
519 break;
520 case CHAR_FONT_ITALIC:
521 tty_setcolor (HELP_ITALIC_COLOR);
522 break;
523 case CHAR_FONT_NORMAL:
524 help_print_word (h, word, &col, &line, FALSE);
525 tty_setcolor (HELP_NORMAL_COLOR);
526 break;
527 case '\n':
528 if (painting)
529 help_print_word (h, word, &col, &line, FALSE);
530 line++;
531 col = 0;
532 break;
533 case '\t':
534 col = (col / 8 + 1) * 8;
535 if (col >= HELP_WINDOW_WIDTH)
537 line++;
538 col = 8;
540 break;
541 case ' ':
542 /* word delimeter */
543 if (painting)
544 help_print_word (h, word, &col, &line, TRUE);
545 break;
546 default:
547 if (painting && (line < help_lines))
549 if (!acs)
550 /* accumulate symbols in a word */
551 g_string_append (word, buff);
552 else if (col < HELP_WINDOW_WIDTH)
554 dlg_move (h, line + 2, col + 2);
556 if ((c == ' ') || (c == '.'))
557 tty_print_char (c);
558 else
559 #ifndef HAVE_SLANG
560 tty_print_char (acs_map[c]);
561 #else
562 SLsmg_draw_object (h->y + line + 2, h->x + col + 2, c);
563 #endif
564 col++;
570 /* print last word */
571 if (n[0] == CHAR_NODE_END)
572 help_print_word (h, word, &col, &line, FALSE);
574 last_shown = p;
575 end_of_node = line < help_lines;
576 tty_setcolor (HELP_NORMAL_COLOR);
577 if ((int) (selected_item - last_shown) >= 0)
579 if ((link_area == NULL) || (link_area->data == NULL))
580 selected_item = NULL;
581 else
583 selected_item = ((Link_Area *) link_area->data)->link_name;
584 repeat_paint = TRUE;
588 while (repeat_paint);
590 g_string_free (word, TRUE);
592 /* Position the cursor over a nice link */
593 if (active_col)
594 dlg_move (h, active_line, active_col);
597 /* --------------------------------------------------------------------------------------------- */
599 static int
600 help_event (Gpm_Event * event, void *vp)
602 Widget *w = vp;
603 GSList *current_area;
605 if ((event->type & GPM_UP) == 0)
606 return 0;
608 /* The event is relative to the dialog window, adjust it: */
609 event->x -= 2;
610 event->y -= 2;
612 if (event->buttons & GPM_B_RIGHT)
614 currentpoint = history[history_ptr].page;
615 selected_item = history[history_ptr].link;
616 history_ptr--;
617 if (history_ptr < 0)
618 history_ptr = HISTORY_SIZE - 1;
620 help_callback (w->owner, NULL, DLG_DRAW, 0, NULL);
621 return 0;
624 /* Test whether the mouse click is inside one of the link areas */
625 for (current_area = link_area; current_area != NULL; current_area = g_slist_next (current_area))
627 Link_Area *la = (Link_Area *) current_area->data;
628 /* Test one line link area */
629 if (event->y == la->y1 && event->x >= la->x1 && event->y == la->y2 && event->x <= la->x2)
630 break;
631 /* Test two line link area */
632 if (la->y1 + 1 == la->y2)
634 /* The first line */
635 if (event->y == la->y1 && event->x >= la->x1)
636 break;
637 /* The second line */
638 if (event->y == la->y2 && event->x <= la->x2)
639 break;
641 /* Mouse will not work with link areas of more than two lines */
644 /* Test whether a link area was found */
645 if (current_area != NULL)
647 Link_Area *la = (Link_Area *) current_area->data;
649 /* The click was inside a link area -> follow the link */
650 history_ptr = (history_ptr + 1) % HISTORY_SIZE;
651 history[history_ptr].page = currentpoint;
652 history[history_ptr].link = la->link_name;
653 currentpoint = help_follow_link (currentpoint, la->link_name);
654 selected_item = NULL;
656 else if (event->y < 0)
657 move_backward (help_lines - 1);
658 else if (event->y >= help_lines)
659 move_forward (help_lines - 1);
660 else if (event->y < help_lines / 2)
661 move_backward (1);
662 else
663 move_forward (1);
665 /* Show the new node */
666 help_callback (w->owner, NULL, DLG_DRAW, 0, NULL);
668 return 0;
671 /* --------------------------------------------------------------------------------------------- */
672 /** show help */
674 static void
675 help_help (Dlg_head * h)
677 const char *p;
679 history_ptr = (history_ptr + 1) % HISTORY_SIZE;
680 history[history_ptr].page = currentpoint;
681 history[history_ptr].link = selected_item;
683 p = search_string (fdata, "[How to use help]");
684 if (p != NULL)
686 currentpoint = p + 1; /* Skip the newline following the start of the node */
687 selected_item = NULL;
688 help_callback (h, NULL, DLG_DRAW, 0, NULL);
692 /* --------------------------------------------------------------------------------------------- */
694 static void
695 help_index (Dlg_head * h)
697 const char *new_item;
699 new_item = search_string (fdata, "[Contents]");
701 if (new_item == NULL)
702 message (D_ERROR, MSG_ERROR, _("Cannot find node %s in help file"), "[Contents]");
703 else
705 history_ptr = (history_ptr + 1) % HISTORY_SIZE;
706 history[history_ptr].page = currentpoint;
707 history[history_ptr].link = selected_item;
709 currentpoint = new_item + 1; /* Skip the newline following the start of the node */
710 selected_item = NULL;
711 help_callback (h, NULL, DLG_DRAW, 0, NULL);
715 /* --------------------------------------------------------------------------------------------- */
717 static void
718 help_back (Dlg_head * h)
720 currentpoint = history[history_ptr].page;
721 selected_item = history[history_ptr].link;
722 history_ptr--;
723 if (history_ptr < 0)
724 history_ptr = HISTORY_SIZE - 1;
726 help_callback (h, NULL, DLG_DRAW, 0, NULL); /* FIXME: unneeded? */
729 /* --------------------------------------------------------------------------------------------- */
731 static void
732 help_next_link (gboolean move_down)
734 const char *new_item;
736 new_item = select_next_link (selected_item);
737 if (new_item != NULL)
739 selected_item = new_item;
740 if ((int) (selected_item - last_shown) >= 0)
742 if (move_down)
743 move_forward (1);
744 else
745 selected_item = NULL;
748 else if (move_down)
749 move_forward (1);
750 else
751 selected_item = NULL;
754 /* --------------------------------------------------------------------------------------------- */
756 static void
757 help_prev_link (gboolean move_up)
759 const char *new_item;
761 new_item = select_prev_link (selected_item);
762 selected_item = new_item;
763 if ((selected_item == NULL) || (selected_item < currentpoint))
765 if (move_up)
766 move_backward (1);
767 else if ((link_area != NULL) && (link_area->data != NULL))
768 selected_item = ((Link_Area *) link_area->data)->link_name;
769 else
770 selected_item = NULL;
774 /* --------------------------------------------------------------------------------------------- */
776 static void
777 help_next_node (void)
779 const char *new_item;
781 new_item = currentpoint;
782 while ((*new_item != '\0') && (*new_item != CHAR_NODE_END))
783 new_item++;
785 if (*++new_item == '[')
786 while (*++new_item != '\0')
787 if ((*new_item == ']') && (*++new_item != '\0') && (*++new_item != '\0'))
789 currentpoint = new_item;
790 selected_item = NULL;
791 break;
795 /* --------------------------------------------------------------------------------------------- */
797 static void
798 help_prev_node (void)
800 const char *new_item;
802 new_item = currentpoint;
803 while (((int) (new_item - fdata) > 1) && (*new_item != CHAR_NODE_END))
804 new_item--;
805 new_item--;
806 while (((int) (new_item - fdata) > 0) && (*new_item != CHAR_NODE_END))
807 new_item--;
808 while (*new_item != ']')
809 new_item++;
810 currentpoint = new_item + 2;
811 selected_item = NULL;
814 /* --------------------------------------------------------------------------------------------- */
816 static void
817 help_select_link (void)
819 /* follow link */
820 if (selected_item == NULL)
822 #ifdef WE_WANT_TO_GO_BACKWARD_ON_KEY_RIGHT
823 /* Is there any reason why the right key would take us
824 * backward if there are no links selected?, I agree
825 * with Torben than doing nothing in this case is better
827 /* If there are no links, go backward in history */
828 history_ptr--;
829 if (history_ptr < 0)
830 history_ptr = HISTORY_SIZE - 1;
832 currentpoint = history[history_ptr].page;
833 selected_item = history[history_ptr].link;
834 #endif
836 else
838 history_ptr = (history_ptr + 1) % HISTORY_SIZE;
839 history[history_ptr].page = currentpoint;
840 history[history_ptr].link = selected_item;
841 currentpoint = help_follow_link (currentpoint, selected_item);
844 selected_item = NULL;
847 /* --------------------------------------------------------------------------------------------- */
849 static cb_ret_t
850 help_execute_cmd (unsigned long command)
852 cb_ret_t ret = MSG_HANDLED;
854 switch (command)
856 case CK_HelpHelp:
857 help_help (whelp);
858 break;
859 case CK_HelpIndex:
860 help_index (whelp);
861 break;
862 case CK_HelpBack:
863 help_back (whelp);
864 break;
865 case CK_HelpMoveUp:
866 help_prev_link (TRUE);
867 break;
868 case CK_HelpMoveDown:
869 help_next_link (TRUE);
870 break;
871 case CK_HelpMovePgDn:
872 move_forward (help_lines - 1);
873 break;
874 case CK_HelpMovePgUp:
875 move_backward (help_lines - 1);
876 break;
877 case CK_HelpMoveHalfPgDn:
878 move_forward (help_lines / 2);
879 break;
880 case CK_HelpMoveHalfPgUp:
881 move_backward (help_lines / 2);
882 break;
883 case CK_HelpMoveTop:
884 move_to_top ();
885 break;
886 case CK_HelpMoveBottom:
887 move_to_bottom ();
888 break;
889 case CK_HelpSelectLink:
890 help_select_link ();
891 break;
892 case CK_HelpNextLink:
893 help_next_link (FALSE);
894 break;
895 case CK_HelpPrevLink:
896 help_prev_link (FALSE);
897 break;
898 case CK_HelpNextNode:
899 help_next_node ();
900 break;
901 case CK_HelpPrevNode:
902 help_prev_node ();
903 break;
904 case CK_HelpQuit:
905 dlg_stop (whelp);
906 break;
907 case CK_DialogCancel:
908 /* don't close help due to SIGINT */
909 break;
910 default:
911 ret = MSG_NOT_HANDLED;
914 return ret;
917 /* --------------------------------------------------------------------------------------------- */
919 static cb_ret_t
920 help_handle_key (Dlg_head * h, int c)
922 unsigned long command;
924 command = keybind_lookup_keymap_command (help_map, c);
925 if ((command == CK_Ignore_Key) || (help_execute_cmd (command) == MSG_NOT_HANDLED))
926 return MSG_NOT_HANDLED;
928 help_callback (h, NULL, DLG_DRAW, 0, NULL);
929 return MSG_HANDLED;
932 /* --------------------------------------------------------------------------------------------- */
934 static cb_ret_t
935 help_callback (Dlg_head * h, Widget * sender, dlg_msg_t msg, int parm, void *data)
937 WButtonBar *bb;
939 switch (msg)
941 case DLG_RESIZE:
942 help_lines = min (LINES - 4, max (2 * LINES / 3, 18));
943 dlg_set_size (h, help_lines + 4, HELP_WINDOW_WIDTH + 4);
944 bb = find_buttonbar (h);
945 widget_set_size (&bb->widget, LINES - 1, 0, 1, COLS);
946 return MSG_HANDLED;
948 case DLG_DRAW:
949 common_dialog_repaint (h);
950 help_show (h, currentpoint);
951 return MSG_HANDLED;
953 case DLG_KEY:
954 return help_handle_key (h, parm);
956 case DLG_ACTION:
957 /* shortcut */
958 if (sender == NULL)
959 return help_execute_cmd (parm);
960 /* message from buttonbar */
961 if (sender == (Widget *) find_buttonbar (h))
963 if (data != NULL)
964 return send_message ((Widget *) data, WIDGET_COMMAND, parm);
965 return help_execute_cmd (parm);
967 return MSG_NOT_HANDLED;
969 default:
970 return default_dlg_callback (h, sender, msg, parm, data);
974 /* --------------------------------------------------------------------------------------------- */
976 static void
977 interactive_display_finish (void)
979 clear_link_areas ();
982 /* --------------------------------------------------------------------------------------------- */
983 /** translate help file into terminal encoding */
985 static void
986 translate_file (char *filedata)
988 GIConv conv;
989 GString *translated_data;
991 /* initial allocation for largest whole help file */
992 translated_data = g_string_sized_new (32 * 1024);
994 conv = str_crt_conv_from ("UTF-8");
996 if (conv == INVALID_CONV)
997 g_string_free (translated_data, TRUE);
998 else
1000 g_free (fdata);
1002 if (str_convert (conv, filedata, translated_data) != ESTR_FAILURE)
1003 fdata = g_string_free (translated_data, FALSE);
1004 else
1006 fdata = NULL;
1007 g_string_free (translated_data, TRUE);
1009 str_close_conv (conv);
1013 /* --------------------------------------------------------------------------------------------- */
1015 static cb_ret_t
1016 md_callback (Widget * w, widget_msg_t msg, int parm)
1018 switch (msg)
1020 case WIDGET_RESIZED:
1021 w->lines = help_lines;
1022 return MSG_HANDLED;
1024 default:
1025 return default_proc (msg, parm);
1029 /* --------------------------------------------------------------------------------------------- */
1031 static Widget *
1032 mousedispatch_new (int y, int x, int yl, int xl)
1034 Widget *w = g_new (Widget, 1);
1035 init_widget (w, y, x, yl, xl, md_callback, help_event);
1036 return w;
1039 /* --------------------------------------------------------------------------------------------- */
1040 /*** public functions ****************************************************************************/
1041 /* --------------------------------------------------------------------------------------------- */
1043 void
1044 interactive_display (const char *filename, const char *node)
1046 const dlg_colors_t help_colors = {
1047 HELP_NORMAL_COLOR, /* common text color */
1048 0, /* unused in help */
1049 HELP_BOLD_COLOR, /* bold text color */
1050 0, /* unused in help */
1051 HELP_TITLE_COLOR /* title color */
1054 WButtonBar *help_bar;
1055 Widget *md;
1056 char *hlpfile = NULL;
1057 char *filedata;
1059 if (filename != NULL)
1060 g_file_get_contents (filename, &filedata, NULL, NULL);
1061 else
1062 filedata = load_mc_home_file (mc_home_alt, MC_HELP, &hlpfile);
1064 if (filedata == NULL)
1065 message (D_ERROR, MSG_ERROR, _("Cannot open file %s\n%s"),
1066 filename ? filename : hlpfile, unix_error_string (errno));
1068 g_free (hlpfile);
1070 if (filedata == NULL)
1071 return;
1073 translate_file (filedata);
1075 g_free (filedata);
1077 if (fdata == NULL)
1078 return;
1080 if ((node == NULL) || (*node == '\0'))
1081 node = "[main]";
1083 main_node = search_string (fdata, node);
1085 if (main_node == NULL)
1087 message (D_ERROR, MSG_ERROR, _("Cannot find node %s in help file"), node);
1089 /* Fallback to [main], return if it also cannot be found */
1090 main_node = search_string (fdata, "[main]");
1091 if (main_node == NULL)
1093 interactive_display_finish ();
1094 return;
1098 help_lines = min (LINES - 4, max (2 * LINES / 3, 18));
1100 whelp =
1101 create_dlg (TRUE, 0, 0, help_lines + 4, HELP_WINDOW_WIDTH + 4,
1102 help_colors, help_callback, "[Help]", _("Help"),
1103 DLG_TRYUP | DLG_CENTER | DLG_WANT_TAB);
1105 selected_item = search_string_node (main_node, STRING_LINK_START) - 1;
1106 currentpoint = main_node + 1; /* Skip the newline following the start of the node */
1108 for (history_ptr = HISTORY_SIZE; history_ptr;)
1110 history_ptr--;
1111 history[history_ptr].page = currentpoint;
1112 history[history_ptr].link = selected_item;
1115 help_bar = buttonbar_new (TRUE);
1116 help_bar->widget.y -= whelp->y;
1117 help_bar->widget.x -= whelp->x;
1119 md = mousedispatch_new (1, 1, help_lines, HELP_WINDOW_WIDTH - 2);
1121 add_widget (whelp, md);
1122 add_widget (whelp, help_bar);
1124 buttonbar_set_label (help_bar, 1, Q_ ("ButtonBar|Help"), help_map, NULL);
1125 buttonbar_set_label (help_bar, 2, Q_ ("ButtonBar|Index"), help_map, NULL);
1126 buttonbar_set_label (help_bar, 3, Q_ ("ButtonBar|Prev"), help_map, NULL);
1127 buttonbar_set_label (help_bar, 4, "", help_map, NULL);
1128 buttonbar_set_label (help_bar, 5, "", help_map, NULL);
1129 buttonbar_set_label (help_bar, 6, "", help_map, NULL);
1130 buttonbar_set_label (help_bar, 7, "", help_map, NULL);
1131 buttonbar_set_label (help_bar, 8, "", help_map, NULL);
1132 buttonbar_set_label (help_bar, 9, "", help_map, NULL);
1133 buttonbar_set_label (help_bar, 10, Q_ ("ButtonBar|Quit"), help_map, NULL);
1135 run_dlg (whelp);
1136 interactive_display_finish ();
1137 destroy_dlg (whelp);
1140 /* --------------------------------------------------------------------------------------------- */