Ticket #1648: implemented single-line boxes.
[kaloumi3.git] / src / help.c
blobb9efcebd7e3124ff42a3d3185228fdbf63de034b
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/skin.h"
58 #include "lib/tty/mouse.h"
59 #include "lib/tty/key.h"
60 #include "lib/strutil.h"
62 #include "dialog.h" /* For Dlg_head */
63 #include "widget.h" /* For Widget */
64 #include "wtools.h" /* For common_dialog_repaint() */
65 #include "cmddef.h"
66 #include "keybind.h"
67 #include "help.h"
68 #include "main.h"
70 const global_keymap_t *help_map;
72 #define MAXLINKNAME 80
73 #define HISTORY_SIZE 20
74 #define HELP_WINDOW_WIDTH (COLS - 16)
76 #define STRING_LINK_START "\01"
77 #define STRING_LINK_POINTER "\02"
78 #define STRING_LINK_END "\03"
79 #define STRING_NODE_END "\04"
82 static char *fdata = NULL; /* Pointer to the loaded data file */
83 static int help_lines; /* Lines in help viewer */
84 static int history_ptr; /* For the history queue */
85 static const char *main_node; /* The main node */
86 static const char *last_shown = NULL; /* Last byte shown in a screen */
87 static gboolean end_of_node = FALSE; /* Flag: the last character of the node shown? */
88 static const char *currentpoint;
89 static const char *selected_item;
91 /* The widget variables */
92 static Dlg_head *whelp;
94 static struct
96 const char *page; /* Pointer to the selected page */
97 const char *link; /* Pointer to the selected link */
98 } history[HISTORY_SIZE];
100 /* Link areas for the mouse */
101 typedef struct Link_Area
103 int x1, y1, x2, y2;
104 const char *link_name;
105 } Link_Area;
107 static GSList *link_area = NULL;
108 static gboolean inside_link_area = FALSE;
110 static cb_ret_t help_callback (Dlg_head * h, Widget * sender, dlg_msg_t msg, int parm, void *data);
112 /* returns the position where text was found in the start buffer */
113 /* or 0 if not found */
114 static const char *
115 search_string (const char *start, const char *text)
117 const char *result = NULL;
118 char *local_text = g_strdup (text);
119 char *d = local_text;
120 const char *e = start;
122 /* fmt sometimes replaces a space with a newline in the help file */
123 /* Replace the newlines in the link name with spaces to correct the situation */
124 while (*d != '\0')
126 if (*d == '\n')
127 *d = ' ';
128 str_next_char (&d);
131 /* Do search */
132 for (d = local_text; *e; e++)
134 if (*d == *e)
135 d++;
136 else
137 d = local_text;
138 if (*d == '\0')
140 result = e + 1;
141 break;
145 g_free (local_text);
146 return result;
149 /* Searches text in the buffer pointed by start. Search ends */
150 /* if the CHAR_NODE_END is found in the text. Returns 0 on failure */
151 static const char *
152 search_string_node (const char *start, const char *text)
154 const char *d = text;
155 const char *e = start;
157 if (start != NULL)
158 for (; *e && *e != CHAR_NODE_END; e++)
160 if (*d == *e)
161 d++;
162 else
163 d = text;
164 if (*d == '\0')
165 return e + 1;
168 return NULL;
171 /* Searches the_char in the buffer pointer by start and searches */
172 /* it can search forward (direction = 1) or backward (direction = -1) */
173 static const char *
174 search_char_node (const char *start, char the_char, int direction)
176 const char *e;
178 for (e = start; (*e != '\0') && (*e != CHAR_NODE_END); e += direction)
179 if (*e == the_char)
180 return e;
182 return NULL;
185 /* Returns the new current pointer when moved lines lines */
186 static const char *
187 move_forward2 (const char *c, int lines)
189 const char *p;
190 int line;
192 currentpoint = c;
193 for (line = 0, p = currentpoint; (*p != '\0') && (*p != CHAR_NODE_END); str_cnext_char (&p))
195 if (line == lines)
196 return currentpoint = p;
198 if (*p == '\n')
199 line++;
201 return currentpoint = c;
204 static const char *
205 move_backward2 (const char *c, int lines)
207 const char *p;
208 int line;
210 currentpoint = c;
211 for (line = 0, p = currentpoint; (*p != '\0') && ((int) (p - fdata) >= 0); str_cprev_char (&p))
213 if (*p == CHAR_NODE_END)
215 /* We reached the beginning of the node */
216 /* Skip the node headers */
217 while (*p != ']')
218 str_cnext_char (&p);
219 return currentpoint = p + 2; /* Skip the newline following the start of the node */
222 if (*(p - 1) == '\n')
223 line++;
224 if (line == lines)
225 return currentpoint = p;
227 return currentpoint = c;
230 static void
231 move_forward (int i)
233 if (!end_of_node)
234 currentpoint = move_forward2 (currentpoint, i);
237 static void
238 move_backward (int i)
240 currentpoint = move_backward2 (currentpoint, ++i);
243 static void
244 move_to_top (void)
246 while (((int) (currentpoint > fdata) > 0) && (*currentpoint != CHAR_NODE_END))
247 currentpoint--;
249 while (*currentpoint != ']')
250 currentpoint++;
251 currentpoint = currentpoint + 2; /* Skip the newline following the start of the node */
252 selected_item = NULL;
255 static void
256 move_to_bottom (void)
258 while ((*currentpoint != '\0') && (*currentpoint != CHAR_NODE_END))
259 currentpoint++;
260 currentpoint--;
261 move_backward (1);
264 static const char *
265 help_follow_link (const char *start, const char *lc_selected_item)
267 char link_name[MAXLINKNAME];
268 const char *p;
269 int i = 0;
271 if (lc_selected_item == NULL)
272 return start;
274 for (p = lc_selected_item; *p && *p != CHAR_NODE_END && *p != CHAR_LINK_POINTER; p++)
276 if (*p == CHAR_LINK_POINTER)
278 link_name[0] = '[';
279 for (i = 1; *p != CHAR_LINK_END && *p && *p != CHAR_NODE_END && i < MAXLINKNAME - 3;)
280 link_name[i++] = *++p;
281 link_name[i - 1] = ']';
282 link_name[i] = '\0';
283 p = search_string (fdata, link_name);
284 if (p != NULL)
286 p += 1; /* Skip the newline following the start of the node */
287 return p;
291 /* Create a replacement page with the error message */
292 return _(" Help file format error\n");
295 static const char *
296 select_next_link (const char *current_link)
298 const char *p;
300 if (current_link == NULL)
301 return NULL;
303 p = search_string_node (current_link, STRING_LINK_END);
304 if (p == NULL)
305 return NULL;
306 p = search_string_node (p, STRING_LINK_START);
307 if (p == NULL)
308 return NULL;
309 return p - 1;
312 static const char *
313 select_prev_link (const char *current_link)
315 return current_link == NULL ? NULL : search_char_node (current_link - 1, CHAR_LINK_START, -1);
318 static void
319 start_link_area (int x, int y, const char *link_name)
321 Link_Area *la;
323 if (inside_link_area)
324 message (D_NORMAL, _("Warning"), _(" Internal bug: Double start of link area "));
326 /* Allocate memory for a new link area */
327 la = g_new (Link_Area, 1);
328 /* Save the beginning coordinates of the link area */
329 la->x1 = x;
330 la->y1 = y;
331 /* Save the name of the destination anchor */
332 la->link_name = link_name;
333 link_area = g_slist_prepend (link_area, la);
335 inside_link_area = TRUE;
338 static void
339 end_link_area (int x, int y)
341 if (inside_link_area)
343 Link_Area *la = (Link_Area *) link_area->data;
344 /* Save the end coordinates of the link area */
345 la->x2 = x;
346 la->y2 = y;
347 inside_link_area = FALSE;
351 static void
352 clear_link_areas (void)
354 g_slist_foreach (link_area, (GFunc) g_free, NULL);
355 g_slist_free (link_area);
356 link_area = NULL;
357 inside_link_area = FALSE;
360 static void
361 help_print_word (Dlg_head * h, GString * word, int *col, int *line, gboolean add_space)
363 if (*line >= help_lines)
364 g_string_set_size (word, 0);
365 else
367 int w;
369 w = str_term_width1 (word->str);
370 if (*col + w >= HELP_WINDOW_WIDTH)
372 *col = 0;
373 (*line)++;
376 if (*line >= help_lines)
377 g_string_set_size (word, 0);
378 else
380 dlg_move (h, *line + 2, *col + 2);
381 tty_print_string (word->str);
382 g_string_set_size (word, 0);
383 *col += w;
387 if (add_space)
389 if (*col < HELP_WINDOW_WIDTH - 1)
391 tty_print_char (' ');
392 (*col)++;
394 else
396 *col = 0;
397 (*line)++;
402 static void
403 help_show (Dlg_head * h, const char *paint_start)
405 const char *p, *n;
406 int col, line, c;
407 gboolean painting = TRUE;
408 gboolean acs; /* Flag: Alternate character set active? */
409 gboolean repeat_paint;
410 int active_col, active_line; /* Active link position */
411 char buff[MB_LEN_MAX + 1];
412 GString *word;
414 word = g_string_sized_new (32);
416 tty_setcolor (HELP_NORMAL_COLOR);
419 line = col = active_col = active_line = 0;
420 repeat_paint = FALSE;
421 acs = FALSE;
423 clear_link_areas ();
424 if ((int) (selected_item - paint_start) < 0)
425 selected_item = NULL;
427 p = paint_start;
428 n = paint_start;
429 while ((n[0] != '\0') && (n[0] != CHAR_NODE_END) && (line < help_lines))
431 p = n;
432 n = str_cget_next_char (p);
433 memcpy (buff, p, n - p);
434 buff[n - p] = '\0';
436 c = (unsigned char) buff[0];
437 switch (c)
439 case CHAR_LINK_START:
440 if (selected_item == NULL)
441 selected_item = p;
442 if (p != selected_item)
443 tty_setcolor (HELP_LINK_COLOR);
444 else
446 tty_setcolor (HELP_SLINK_COLOR);
448 /* Store the coordinates of the link */
449 active_col = col + 2;
450 active_line = line + 2;
452 start_link_area (col, line, p);
453 break;
454 case CHAR_LINK_POINTER:
455 painting = FALSE;
456 end_link_area (col - 1, line);
457 break;
458 case CHAR_LINK_END:
459 painting = TRUE;
460 help_print_word (h, word, &col, &line, FALSE);
461 tty_setcolor (HELP_NORMAL_COLOR);
462 break;
463 case CHAR_ALTERNATE:
464 acs = TRUE;
465 break;
466 case CHAR_NORMAL:
467 acs = FALSE;
468 break;
469 case CHAR_VERSION:
470 dlg_move (h, line + 2, col + 2);
471 tty_print_string (VERSION);
472 col += str_term_width1 (VERSION);
473 break;
474 case CHAR_FONT_BOLD:
475 tty_setcolor (HELP_BOLD_COLOR);
476 break;
477 case CHAR_FONT_ITALIC:
478 tty_setcolor (HELP_ITALIC_COLOR);
479 break;
480 case CHAR_FONT_NORMAL:
481 help_print_word (h, word, &col, &line, FALSE);
482 tty_setcolor (HELP_NORMAL_COLOR);
483 break;
484 case '\n':
485 if (painting)
486 help_print_word (h, word, &col, &line, FALSE);
487 line++;
488 col = 0;
489 break;
490 case '\t':
491 col = (col / 8 + 1) * 8;
492 if (col >= HELP_WINDOW_WIDTH)
494 line++;
495 col = 8;
497 break;
498 case ' ':
499 /* word delimeter */
500 if (painting)
501 help_print_word (h, word, &col, &line, TRUE);
502 break;
503 default:
504 if (painting && (line < help_lines))
506 if (!acs)
507 /* accumulate symbols in a word */
508 g_string_append (word, buff);
509 else if (col < HELP_WINDOW_WIDTH)
511 dlg_move (h, line + 2, col + 2);
513 if ((c == ' ') || (c == '.'))
514 tty_print_char (c);
515 else
516 #ifndef HAVE_SLANG
517 tty_print_char (acs_map[c]);
518 #else
519 SLsmg_draw_object (h->y + line + 2, h->x + col + 2, c);
520 #endif
521 col++;
527 /* print last word */
528 if (n[0] == CHAR_NODE_END)
529 help_print_word (h, word, &col, &line, FALSE);
531 last_shown = p;
532 end_of_node = line < help_lines;
533 tty_setcolor (HELP_NORMAL_COLOR);
534 if ((int) (selected_item - last_shown) >= 0)
536 if ((link_area == NULL) || (link_area->data == NULL))
537 selected_item = NULL;
538 else
540 selected_item = ((Link_Area *) link_area->data)->link_name;
541 repeat_paint = TRUE;
545 while (repeat_paint);
547 g_string_free (word, TRUE);
549 /* Position the cursor over a nice link */
550 if (active_col)
551 dlg_move (h, active_line, active_col);
554 static int
555 help_event (Gpm_Event * event, void *vp)
557 Widget *w = vp;
558 GSList *current_area;
560 if ((event->type & GPM_UP) == 0)
561 return 0;
563 /* The event is relative to the dialog window, adjust it: */
564 event->x -= 2;
565 event->y -= 2;
567 if (event->buttons & GPM_B_RIGHT)
569 currentpoint = history[history_ptr].page;
570 selected_item = history[history_ptr].link;
571 history_ptr--;
572 if (history_ptr < 0)
573 history_ptr = HISTORY_SIZE - 1;
575 help_callback (w->parent, NULL, DLG_DRAW, 0, NULL);
576 return 0;
579 /* Test whether the mouse click is inside one of the link areas */
580 for (current_area = link_area; current_area != NULL; current_area = g_slist_next (current_area))
582 Link_Area *la = (Link_Area *) current_area->data;
583 /* Test one line link area */
584 if (event->y == la->y1 && event->x >= la->x1 && event->y == la->y2 && event->x <= la->x2)
585 break;
586 /* Test two line link area */
587 if (la->y1 + 1 == la->y2)
589 /* The first line */
590 if (event->y == la->y1 && event->x >= la->x1)
591 break;
592 /* The second line */
593 if (event->y == la->y2 && event->x <= la->x2)
594 break;
596 /* Mouse will not work with link areas of more than two lines */
599 /* Test whether a link area was found */
600 if (current_area != NULL)
602 Link_Area *la = (Link_Area *) current_area->data;
604 /* The click was inside a link area -> follow the link */
605 history_ptr = (history_ptr + 1) % HISTORY_SIZE;
606 history[history_ptr].page = currentpoint;
607 history[history_ptr].link = la->link_name;
608 currentpoint = help_follow_link (currentpoint, la->link_name);
609 selected_item = NULL;
611 else if (event->y < 0)
612 move_backward (help_lines - 1);
613 else if (event->y >= help_lines)
614 move_forward (help_lines - 1);
615 else if (event->y < help_lines / 2)
616 move_backward (1);
617 else
618 move_forward (1);
620 /* Show the new node */
621 help_callback (w->parent, NULL, DLG_DRAW, 0, NULL);
623 return 0;
626 /* show help */
627 static void
628 help_help (Dlg_head * h)
630 const char *p;
632 history_ptr = (history_ptr + 1) % HISTORY_SIZE;
633 history[history_ptr].page = currentpoint;
634 history[history_ptr].link = selected_item;
636 p = search_string (fdata, "[How to use help]");
637 if (p != NULL)
639 currentpoint = p + 1; /* Skip the newline following the start of the node */
640 selected_item = NULL;
641 help_callback (h, NULL, DLG_DRAW, 0, NULL);
645 static void
646 help_index (Dlg_head * h)
648 const char *new_item;
650 new_item = search_string (fdata, "[Contents]");
652 if (new_item == NULL)
653 message (D_ERROR, MSG_ERROR, _(" Cannot find node %s in help file "), "[Contents]");
654 else
656 history_ptr = (history_ptr + 1) % HISTORY_SIZE;
657 history[history_ptr].page = currentpoint;
658 history[history_ptr].link = selected_item;
660 currentpoint = new_item + 1; /* Skip the newline following the start of the node */
661 selected_item = NULL;
662 help_callback (h, NULL, DLG_DRAW, 0, NULL);
666 static void
667 help_back (Dlg_head * h)
669 currentpoint = history[history_ptr].page;
670 selected_item = history[history_ptr].link;
671 history_ptr--;
672 if (history_ptr < 0)
673 history_ptr = HISTORY_SIZE - 1;
675 help_callback (h, NULL, DLG_DRAW, 0, NULL); /* FIXME: unneeded? */
678 static void
679 help_cmk_move_backward (void *vp, int lines)
681 (void) &vp;
682 move_backward (lines);
685 static void
686 help_cmk_move_forward (void *vp, int lines)
688 (void) &vp;
689 move_forward (lines);
692 static void
693 help_cmk_moveto_top (void *vp, int lines)
695 (void) &vp;
696 (void) &lines;
697 move_to_top ();
700 static void
701 help_cmk_moveto_bottom (void *vp, int lines)
703 (void) &vp;
704 (void) &lines;
705 move_to_bottom ();
708 static void
709 help_next_link (gboolean move_down)
711 const char *new_item;
713 new_item = select_next_link (selected_item);
714 if (new_item != NULL)
716 selected_item = new_item;
717 if ((int) (selected_item - last_shown) >= 0)
719 if (move_down)
720 move_forward (1);
721 else
722 selected_item = NULL;
725 else if (move_down)
726 move_forward (1);
727 else
728 selected_item = NULL;
731 static void
732 help_prev_link (gboolean move_up)
734 const char *new_item;
736 new_item = select_prev_link (selected_item);
737 selected_item = new_item;
738 if ((selected_item == NULL) || (selected_item < currentpoint))
740 if (move_up)
741 move_backward (1);
742 else if ((link_area != NULL) && (link_area->data != NULL))
743 selected_item = ((Link_Area *) link_area->data)->link_name;
744 else
745 selected_item = NULL;
749 static void
750 help_next_node (void)
752 const char *new_item;
754 new_item = currentpoint;
755 while ((*new_item != '\0') && (*new_item != CHAR_NODE_END))
756 new_item++;
758 if (*++new_item == '[')
759 while (*++new_item != '\0')
760 if ((*new_item == ']') && (*++new_item != '\0') && (*++new_item != '\0'))
762 currentpoint = new_item;
763 selected_item = NULL;
764 break;
768 static void
769 help_prev_node (void)
771 const char *new_item;
773 new_item = currentpoint;
774 while (((int) (new_item - fdata) > 1) && (*new_item != CHAR_NODE_END))
775 new_item--;
776 new_item--;
777 while (((int) (new_item - fdata) > 0) && (*new_item != CHAR_NODE_END))
778 new_item--;
779 while (*new_item != ']')
780 new_item++;
781 currentpoint = new_item + 2;
782 selected_item = NULL;
785 static void
786 help_select_link (void)
788 /* follow link */
789 if (selected_item == NULL)
791 #ifdef WE_WANT_TO_GO_BACKWARD_ON_KEY_RIGHT
792 /* Is there any reason why the right key would take us
793 * backward if there are no links selected?, I agree
794 * with Torben than doing nothing in this case is better
796 /* If there are no links, go backward in history */
797 history_ptr--;
798 if (history_ptr < 0)
799 history_ptr = HISTORY_SIZE - 1;
801 currentpoint = history[history_ptr].page;
802 selected_item = history[history_ptr].link;
803 #endif
805 else
807 history_ptr = (history_ptr + 1) % HISTORY_SIZE;
808 history[history_ptr].page = currentpoint;
809 history[history_ptr].link = selected_item;
810 currentpoint = help_follow_link (currentpoint, selected_item);
813 selected_item = NULL;
816 static cb_ret_t
817 help_execute_cmd (unsigned long command)
819 cb_ret_t ret = MSG_HANDLED;
821 switch (command)
823 case CK_HelpHelp:
824 help_help (whelp);
825 break;
826 case CK_HelpIndex:
827 help_index (whelp);
828 break;
829 case CK_HelpBack:
830 help_back (whelp);
831 break;
832 case CK_HelpMoveUp:
833 help_prev_link (TRUE);
834 break;
835 case CK_HelpMoveDown:
836 help_next_link (TRUE);
837 break;
838 case CK_HelpSelectLink:
839 help_select_link ();
840 break;
841 case CK_HelpNextLink:
842 help_next_link (FALSE);
843 break;
844 case CK_HelpPrevLink:
845 help_prev_link (FALSE);
846 break;
847 case CK_HelpNextNode:
848 help_next_node ();
849 break;
850 case CK_HelpPrevNode:
851 help_prev_node ();
852 break;
853 case CK_HelpQuit:
854 dlg_stop (whelp);
855 break;
856 default:
857 ret = MSG_NOT_HANDLED;
860 return ret;
863 static cb_ret_t
864 help_handle_key (Dlg_head * h, int c)
866 if (c != KEY_UP && c != KEY_DOWN &&
867 check_movement_keys (c, help_lines, NULL,
868 help_cmk_move_backward,
869 help_cmk_move_forward,
870 help_cmk_moveto_top, help_cmk_moveto_bottom) == MSG_HANDLED)
872 /* Nothing */ ;
874 else
876 unsigned long command;
878 command = lookup_keymap_command (help_map, c);
879 if ((command == CK_Ignore_Key) || (help_execute_cmd (command) == MSG_NOT_HANDLED))
880 return MSG_NOT_HANDLED;
883 help_callback (h, NULL, DLG_DRAW, 0, NULL);
884 return MSG_HANDLED;
887 static cb_ret_t
888 help_callback (Dlg_head * h, Widget * sender, dlg_msg_t msg, int parm, void *data)
890 WButtonBar *bb;
892 switch (msg)
894 case DLG_RESIZE:
895 help_lines = min (LINES - 4, max (2 * LINES / 3, 18));
896 dlg_set_size (h, help_lines + 4, HELP_WINDOW_WIDTH + 4);
897 bb = find_buttonbar (h);
898 widget_set_size (&bb->widget, LINES - 1, 0, 1, COLS);
899 return MSG_HANDLED;
901 case DLG_DRAW:
902 common_dialog_repaint (h);
903 help_show (h, currentpoint);
904 return MSG_HANDLED;
906 case DLG_KEY:
907 return help_handle_key (h, parm);
909 case DLG_ACTION:
910 /* command from buttonbar */
911 return help_execute_cmd (parm);
913 default:
914 return default_dlg_callback (h, sender, msg, parm, data);
918 static void
919 interactive_display_finish (void)
921 clear_link_areas ();
924 /* translate help file into terminal encoding */
925 static void
926 translate_file (char *filedata)
928 GIConv conv;
929 GString *translated_data;
931 /* initial allocation for largest whole help file */
932 translated_data = g_string_sized_new (32 * 1024);
934 conv = str_crt_conv_from ("UTF-8");
936 if (conv == INVALID_CONV)
937 g_string_free (translated_data, TRUE);
938 else
940 g_free (fdata);
942 if (str_convert (conv, filedata, translated_data) != ESTR_FAILURE)
943 fdata = g_string_free (translated_data, FALSE);
944 else
946 fdata = NULL;
947 g_string_free (translated_data, TRUE);
949 str_close_conv (conv);
953 static cb_ret_t
954 md_callback (Widget * w, widget_msg_t msg, int parm)
956 switch (msg)
958 case WIDGET_RESIZED:
959 w->lines = help_lines;
960 return MSG_HANDLED;
962 default:
963 return default_proc (msg, parm);
967 static Widget *
968 mousedispatch_new (int y, int x, int yl, int xl)
970 Widget *w = g_new (Widget, 1);
971 init_widget (w, y, x, yl, xl, md_callback, help_event);
972 return w;
975 void
976 interactive_display (const char *filename, const char *node)
978 const int help_colors[DLG_COLOR_NUM] = {
979 HELP_NORMAL_COLOR, /* common text color */
980 0, /* unused in help */
981 HELP_BOLD_COLOR, /* title color */
982 0 /* unused in help */
985 WButtonBar *help_bar;
986 Widget *md;
987 char *hlpfile = NULL;
988 char *filedata;
990 if (filename != NULL)
991 filedata = load_file (filename);
992 else
993 filedata = load_mc_home_file (mc_home, mc_home_alt, "mc.hlp", &hlpfile);
995 if (filedata == NULL)
996 message (D_ERROR, MSG_ERROR, _(" Cannot open file %s \n %s "),
997 filename ? filename : hlpfile, unix_error_string (errno));
999 g_free (hlpfile);
1001 if (filedata == NULL)
1002 return;
1004 translate_file (filedata);
1006 g_free (filedata);
1008 if (fdata == NULL)
1009 return;
1011 if ((node == NULL) || (*node == '\0'))
1012 node = "[main]";
1014 main_node = search_string (fdata, node);
1016 if (main_node == NULL)
1018 message (D_ERROR, MSG_ERROR, _(" Cannot find node %s in help file "), node);
1020 /* Fallback to [main], return if it also cannot be found */
1021 main_node = search_string (fdata, "[main]");
1022 if (main_node == NULL)
1024 interactive_display_finish ();
1025 return;
1029 help_lines = min (LINES - 4, max (2 * LINES / 3, 18));
1031 whelp =
1032 create_dlg (0, 0, help_lines + 4, HELP_WINDOW_WIDTH + 4,
1033 help_colors, help_callback, "[Help]", _("Help"),
1034 DLG_TRYUP | DLG_CENTER | DLG_WANT_TAB);
1036 selected_item = search_string_node (main_node, STRING_LINK_START) - 1;
1037 currentpoint = main_node + 1; /* Skip the newline following the start of the node */
1039 for (history_ptr = HISTORY_SIZE; history_ptr;)
1041 history_ptr--;
1042 history[history_ptr].page = currentpoint;
1043 history[history_ptr].link = selected_item;
1046 help_bar = buttonbar_new (TRUE);
1047 help_bar->widget.y -= whelp->y;
1048 help_bar->widget.x -= whelp->x;
1050 md = mousedispatch_new (1, 1, help_lines, HELP_WINDOW_WIDTH - 2);
1052 add_widget (whelp, md);
1053 add_widget (whelp, help_bar);
1055 buttonbar_set_label (help_bar, 1, Q_ ("ButtonBar|Help"), help_map, NULL);
1056 buttonbar_set_label (help_bar, 2, Q_ ("ButtonBar|Index"), help_map, NULL);
1057 buttonbar_set_label (help_bar, 3, Q_ ("ButtonBar|Prev"), help_map, NULL);
1058 buttonbar_set_label (help_bar, 4, "", help_map, NULL);
1059 buttonbar_set_label (help_bar, 5, "", help_map, NULL);
1060 buttonbar_set_label (help_bar, 6, "", help_map, NULL);
1061 buttonbar_set_label (help_bar, 7, "", help_map, NULL);
1062 buttonbar_set_label (help_bar, 8, "", help_map, NULL);
1063 buttonbar_set_label (help_bar, 9, "", help_map, NULL);
1064 buttonbar_set_label (help_bar, 10, Q_ ("ButtonBar|Quit"), help_map, NULL);
1066 run_dlg (whelp);
1067 interactive_display_finish ();
1068 destroy_dlg (whelp);