diff options
Diffstat (limited to 'src/help.c')
-rw-r--r-- | src/help.c | 1180 |
1 files changed, 1180 insertions, 0 deletions
diff --git a/src/help.c b/src/help.c new file mode 100644 index 0000000..45edef4 --- /dev/null +++ b/src/help.c @@ -0,0 +1,1180 @@ +/* + Hypertext file browser. + + Copyright (C) 1994-2022 + Free Software Foundation, Inc. + + This file is part of the Midnight Commander. + + The Midnight Commander is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + The Midnight Commander is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + + +/** \file help.c + * \brief Source: hypertext file browser + * + * Implements the hypertext file viewer. + * The hypertext file is a file that may have one or more nodes. Each + * node ends with a ^D character and starts with a bracket, then the + * name of the node and then a closing bracket. Right after the closing + * bracket a newline is placed. This newline is not to be displayed by + * the help viewer and must be skipped - its sole purpose is to faciliate + * the work of the people managing the help file template (xnc.hlp) . + * + * Links in the hypertext file are specified like this: the text that + * will be highlighted should have a leading ^A, then it comes the + * text, then a ^B indicating that highlighting is done, then the name + * of the node you want to link to and then a ^C. + * + * The file must contain a ^D at the beginning and at the end of the + * file or the program will not be able to detect the end of file. + * + * Lazyness/widgeting attack: This file does use the dialog manager + * and uses mainly the dialog to achieve the help work. there is only + * one specialized widget and it's only used to forward the mouse messages + * to the appropriate routine. + */ + + +#include <config.h> + +#include <errno.h> +#include <limits.h> /* MB_LEN_MAX */ +#include <stdio.h> +#include <sys/types.h> +#include <sys/stat.h> + +#include "lib/global.h" + +#include "lib/tty/tty.h" +#include "lib/skin.h" +#include "lib/strutil.h" +#include "lib/fileloc.h" +#include "lib/util.h" +#include "lib/widget.h" +#include "lib/event-types.h" + +#include "keymap.h" +#include "help.h" + +/*** global variables ****************************************************************************/ + +/*** file scope macro definitions ****************************************************************/ + +#define MAXLINKNAME 80 +#define HISTORY_SIZE 20 +#define HELP_WINDOW_WIDTH MIN(80, COLS - 16) + +#define STRING_LINK_START "\01" +#define STRING_LINK_POINTER "\02" +#define STRING_LINK_END "\03" +#define STRING_NODE_END "\04" + +/*** file scope type declarations ****************************************************************/ + +/* Link areas for the mouse */ +typedef struct Link_Area +{ + int x1, y1, x2, y2; + const char *link_name; +} Link_Area; + +/*** file scope variables ************************************************************************/ + +static char *fdata = NULL; /* Pointer to the loaded data file */ +static int help_lines; /* Lines in help viewer */ +static int history_ptr; /* For the history queue */ +static const char *main_node; /* The main node */ +static const char *last_shown = NULL; /* Last byte shown in a screen */ +static gboolean end_of_node = FALSE; /* Flag: the last character of the node shown? */ +static const char *currentpoint; +static const char *selected_item; + +/* The widget variables */ +static WDialog *whelp; + +static struct +{ + const char *page; /* Pointer to the selected page */ + const char *link; /* Pointer to the selected link */ +} history[HISTORY_SIZE]; + +static GSList *link_area = NULL; +static gboolean inside_link_area = FALSE; + +static cb_ret_t help_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data); + +/*** file scope functions ************************************************************************/ +/* --------------------------------------------------------------------------------------------- */ + +/** returns the position where text was found in the start buffer + * or 0 if not found + */ +static const char * +search_string (const char *start, const char *text) +{ + const char *result = NULL; + char *local_text = g_strdup (text); + char *d = local_text; + const char *e = start; + + /* fmt sometimes replaces a space with a newline in the help file */ + /* Replace the newlines in the link name with spaces to correct the situation */ + while (*d != '\0') + { + if (*d == '\n') + *d = ' '; + str_next_char (&d); + } + + /* Do search */ + for (d = local_text; *e; e++) + { + if (*d == *e) + d++; + else + d = local_text; + if (*d == '\0') + { + result = e + 1; + break; + } + } + + g_free (local_text); + return result; +} + +/* --------------------------------------------------------------------------------------------- */ +/** Searches text in the buffer pointed by start. Search ends + * if the CHAR_NODE_END is found in the text. + * @return NULL on failure + */ + +static const char * +search_string_node (const char *start, const char *text) +{ + const char *d = text; + const char *e = start; + + if (start != NULL) + for (; *e && *e != CHAR_NODE_END; e++) + { + if (*d == *e) + d++; + else + d = text; + if (*d == '\0') + return e + 1; + } + + return NULL; +} + +/* --------------------------------------------------------------------------------------------- */ +/** Searches the_char in the buffer pointer by start and searches + * it can search forward (direction = 1) or backward (direction = -1) + */ + +static const char * +search_char_node (const char *start, char the_char, int direction) +{ + const char *e; + + for (e = start; (*e != '\0') && (*e != CHAR_NODE_END); e += direction) + if (*e == the_char) + return e; + + return NULL; +} + +/* --------------------------------------------------------------------------------------------- */ +/** Returns the new current pointer when moved lines lines */ + +static const char * +move_forward2 (const char *c, int lines) +{ + const char *p; + int line; + + currentpoint = c; + for (line = 0, p = currentpoint; (*p != '\0') && (*p != CHAR_NODE_END); str_cnext_char (&p)) + { + if (line == lines) + return currentpoint = p; + + if (*p == '\n') + line++; + } + return currentpoint = c; +} + +/* --------------------------------------------------------------------------------------------- */ + +static const char * +move_backward2 (const char *c, int lines) +{ + const char *p; + int line; + + currentpoint = c; + for (line = 0, p = currentpoint; (*p != '\0') && ((int) (p - fdata) >= 0); str_cprev_char (&p)) + { + if (*p == CHAR_NODE_END) + { + /* We reached the beginning of the node */ + /* Skip the node headers */ + while (*p != ']') + str_cnext_char (&p); + return currentpoint = p + 2; /* Skip the newline following the start of the node */ + } + + if (*(p - 1) == '\n') + line++; + if (line == lines) + return currentpoint = p; + } + return currentpoint = c; +} + +/* --------------------------------------------------------------------------------------------- */ + +static void +move_forward (int i) +{ + if (!end_of_node) + currentpoint = move_forward2 (currentpoint, i); +} + +/* --------------------------------------------------------------------------------------------- */ + +static void +move_backward (int i) +{ + currentpoint = move_backward2 (currentpoint, ++i); +} + +/* --------------------------------------------------------------------------------------------- */ + +static void +move_to_top (void) +{ + while (((int) (currentpoint > fdata) > 0) && (*currentpoint != CHAR_NODE_END)) + currentpoint--; + + while (*currentpoint != ']') + currentpoint++; + currentpoint = currentpoint + 2; /* Skip the newline following the start of the node */ + selected_item = NULL; +} + +/* --------------------------------------------------------------------------------------------- */ + +static void +move_to_bottom (void) +{ + while ((*currentpoint != '\0') && (*currentpoint != CHAR_NODE_END)) + currentpoint++; + currentpoint--; + move_backward (1); +} + +/* --------------------------------------------------------------------------------------------- */ + +static const char * +help_follow_link (const char *start, const char *lc_selected_item) +{ + const char *p; + + if (lc_selected_item == NULL) + return start; + + for (p = lc_selected_item; *p && *p != CHAR_NODE_END && *p != CHAR_LINK_POINTER; p++) + ; + if (*p == CHAR_LINK_POINTER) + { + int i; + char link_name[MAXLINKNAME]; + + link_name[0] = '['; + for (i = 1; *p != CHAR_LINK_END && *p && *p != CHAR_NODE_END && i < MAXLINKNAME - 3;) + link_name[i++] = *++p; + link_name[i - 1] = ']'; + link_name[i] = '\0'; + p = search_string (fdata, link_name); + if (p != NULL) + { + p += 1; /* Skip the newline following the start of the node */ + return p; + } + } + + /* Create a replacement page with the error message */ + return _("Help file format error\n"); +} + +/* --------------------------------------------------------------------------------------------- */ + +static const char * +select_next_link (const char *current_link) +{ + const char *p; + + if (current_link == NULL) + return NULL; + + p = search_string_node (current_link, STRING_LINK_END); + if (p == NULL) + return NULL; + p = search_string_node (p, STRING_LINK_START); + if (p == NULL) + return NULL; + return p - 1; +} + +/* --------------------------------------------------------------------------------------------- */ + +static const char * +select_prev_link (const char *current_link) +{ + return current_link == NULL ? NULL : search_char_node (current_link - 1, CHAR_LINK_START, -1); +} + +/* --------------------------------------------------------------------------------------------- */ + +static void +start_link_area (int x, int y, const char *link_name) +{ + Link_Area *la; + + if (inside_link_area) + message (D_NORMAL, _("Warning"), "%s", _("Internal bug: Double start of link area")); + + /* Allocate memory for a new link area */ + la = g_new (Link_Area, 1); + /* Save the beginning coordinates of the link area */ + la->x1 = x; + la->y1 = y; + /* Save the name of the destination anchor */ + la->link_name = link_name; + link_area = g_slist_prepend (link_area, la); + + inside_link_area = TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ + +static void +end_link_area (int x, int y) +{ + if (inside_link_area) + { + Link_Area *la = (Link_Area *) link_area->data; + /* Save the end coordinates of the link area */ + la->x2 = x; + la->y2 = y; + inside_link_area = FALSE; + } +} + +/* --------------------------------------------------------------------------------------------- */ + +static void +clear_link_areas (void) +{ + g_clear_slist (&link_area, g_free); + inside_link_area = FALSE; +} + +/* --------------------------------------------------------------------------------------------- */ + +static void +help_print_word (WDialog * h, GString * word, int *col, int *line, gboolean add_space) +{ + if (*line >= help_lines) + g_string_set_size (word, 0); + else + { + int w; + + w = str_term_width1 (word->str); + if (*col + w >= HELP_WINDOW_WIDTH) + { + *col = 0; + (*line)++; + } + + if (*line >= help_lines) + g_string_set_size (word, 0); + else + { + widget_gotoyx (h, *line + 2, *col + 2); + tty_print_string (word->str); + g_string_set_size (word, 0); + *col += w; + } + } + + if (add_space) + { + if (*col < HELP_WINDOW_WIDTH - 1) + { + tty_print_char (' '); + (*col)++; + } + else + { + *col = 0; + (*line)++; + } + } +} + +/* --------------------------------------------------------------------------------------------- */ + +static void +help_show (WDialog * h, const char *paint_start) +{ + const char *p, *n; + int col, line, c; + gboolean painting = TRUE; + gboolean acs; /* Flag: Alternate character set active? */ + gboolean repeat_paint; + int active_col, active_line; /* Active link position */ + char buff[MB_LEN_MAX + 1]; + GString *word; + + word = g_string_sized_new (32); + + tty_setcolor (HELP_NORMAL_COLOR); + do + { + line = col = active_col = active_line = 0; + repeat_paint = FALSE; + acs = FALSE; + + clear_link_areas (); + if ((int) (selected_item - paint_start) < 0) + selected_item = NULL; + + p = paint_start; + n = paint_start; + while ((n[0] != '\0') && (n[0] != CHAR_NODE_END) && (line < help_lines)) + { + p = n; + n = str_cget_next_char (p); + memcpy (buff, p, n - p); + buff[n - p] = '\0'; + + c = (unsigned char) buff[0]; + switch (c) + { + case CHAR_LINK_START: + if (selected_item == NULL) + selected_item = p; + if (p != selected_item) + tty_setcolor (HELP_LINK_COLOR); + else + { + tty_setcolor (HELP_SLINK_COLOR); + + /* Store the coordinates of the link */ + active_col = col + 2; + active_line = line + 2; + } + start_link_area (col, line, p); + break; + case CHAR_LINK_POINTER: + painting = FALSE; + break; + case CHAR_LINK_END: + painting = TRUE; + help_print_word (h, word, &col, &line, FALSE); + end_link_area (col - 1, line); + tty_setcolor (HELP_NORMAL_COLOR); + break; + case CHAR_ALTERNATE: + acs = TRUE; + break; + case CHAR_NORMAL: + acs = FALSE; + break; + case CHAR_VERSION: + widget_gotoyx (h, line + 2, col + 2); + tty_print_string (mc_global.mc_version); + col += str_term_width1 (mc_global.mc_version); + break; + case CHAR_FONT_BOLD: + tty_setcolor (HELP_BOLD_COLOR); + break; + case CHAR_FONT_ITALIC: + tty_setcolor (HELP_ITALIC_COLOR); + break; + case CHAR_FONT_NORMAL: + help_print_word (h, word, &col, &line, FALSE); + tty_setcolor (HELP_NORMAL_COLOR); + break; + case '\n': + if (painting) + help_print_word (h, word, &col, &line, FALSE); + line++; + col = 0; + break; + case ' ': + case '\t': + /* word delimiter */ + if (painting) + { + help_print_word (h, word, &col, &line, c == ' '); + if (c == '\t') + { + col = (col / 8 + 1) * 8; + if (col >= HELP_WINDOW_WIDTH) + { + line++; + col = 8; + } + } + } + break; + default: + if (painting && (line < help_lines)) + { + if (!acs) + /* accumulate symbols in a word */ + g_string_append (word, buff); + else if (col < HELP_WINDOW_WIDTH) + { + widget_gotoyx (h, line + 2, col + 2); + + if ((c == ' ') || (c == '.')) + tty_print_char (c); + else +#ifndef HAVE_SLANG + tty_print_char (acs_map[c]); +#else + SLsmg_draw_object (WIDGET (h)->rect.y + line + 2, + WIDGET (h)->rect.x + col + 2, c); +#endif + col++; + } + } + } + } + + /* print last word */ + if (n[0] == CHAR_NODE_END) + help_print_word (h, word, &col, &line, FALSE); + + last_shown = p; + end_of_node = line < help_lines; + tty_setcolor (HELP_NORMAL_COLOR); + if ((int) (selected_item - last_shown) >= 0) + { + if ((link_area == NULL) || (link_area->data == NULL)) + selected_item = NULL; + else + { + selected_item = ((Link_Area *) link_area->data)->link_name; + repeat_paint = TRUE; + } + } + } + while (repeat_paint); + + g_string_free (word, TRUE); + + /* Position the cursor over a nice link */ + if (active_col) + widget_gotoyx (h, active_line, active_col); +} + +/* --------------------------------------------------------------------------------------------- */ +/** show help */ + +static void +help_help (WDialog * h) +{ + const char *p; + + history_ptr = (history_ptr + 1) % HISTORY_SIZE; + history[history_ptr].page = currentpoint; + history[history_ptr].link = selected_item; + + p = search_string (fdata, "[How to use help]"); + if (p != NULL) + { + currentpoint = p + 1; /* Skip the newline following the start of the node */ + selected_item = NULL; + widget_draw (WIDGET (h)); + } +} + +/* --------------------------------------------------------------------------------------------- */ + +static void +help_index (WDialog * h) +{ + const char *new_item; + + new_item = search_string (fdata, "[Contents]"); + + if (new_item == NULL) + message (D_ERROR, MSG_ERROR, _("Cannot find node %s in help file"), "[Contents]"); + else + { + history_ptr = (history_ptr + 1) % HISTORY_SIZE; + history[history_ptr].page = currentpoint; + history[history_ptr].link = selected_item; + + currentpoint = new_item + 1; /* Skip the newline following the start of the node */ + selected_item = NULL; + widget_draw (WIDGET (h)); + } +} + +/* --------------------------------------------------------------------------------------------- */ + +static void +help_back (WDialog * h) +{ + currentpoint = history[history_ptr].page; + selected_item = history[history_ptr].link; + history_ptr--; + if (history_ptr < 0) + history_ptr = HISTORY_SIZE - 1; + + widget_draw (WIDGET (h)); /* FIXME: unneeded? */ +} + +/* --------------------------------------------------------------------------------------------- */ + +static void +help_next_link (gboolean move_down) +{ + const char *new_item; + + new_item = select_next_link (selected_item); + if (new_item != NULL) + { + selected_item = new_item; + if ((int) (selected_item - last_shown) >= 0) + { + if (move_down) + move_forward (1); + else + selected_item = NULL; + } + } + else if (move_down) + move_forward (1); + else + selected_item = NULL; +} + +/* --------------------------------------------------------------------------------------------- */ + +static void +help_prev_link (gboolean move_up) +{ + const char *new_item; + + new_item = select_prev_link (selected_item); + selected_item = new_item; + if ((selected_item == NULL) || (selected_item < currentpoint)) + { + if (move_up) + move_backward (1); + else if ((link_area != NULL) && (link_area->data != NULL)) + selected_item = ((Link_Area *) link_area->data)->link_name; + else + selected_item = NULL; + } +} + +/* --------------------------------------------------------------------------------------------- */ + +static void +help_next_node (void) +{ + const char *new_item; + + new_item = currentpoint; + while ((*new_item != '\0') && (*new_item != CHAR_NODE_END)) + new_item++; + + if (*++new_item == '[') + while (*++new_item != '\0') + if ((*new_item == ']') && (*++new_item != '\0') && (*++new_item != '\0')) + { + currentpoint = new_item; + selected_item = NULL; + break; + } +} + +/* --------------------------------------------------------------------------------------------- */ + +static void +help_prev_node (void) +{ + const char *new_item; + + new_item = currentpoint; + while (((int) (new_item - fdata) > 1) && (*new_item != CHAR_NODE_END)) + new_item--; + new_item--; + while (((int) (new_item - fdata) > 0) && (*new_item != CHAR_NODE_END)) + new_item--; + while (*new_item != ']') + new_item++; + currentpoint = new_item + 2; + selected_item = NULL; +} + +/* --------------------------------------------------------------------------------------------- */ + +static void +help_select_link (void) +{ + /* follow link */ + if (selected_item == NULL) + { +#ifdef WE_WANT_TO_GO_BACKWARD_ON_KEY_RIGHT + /* Is there any reason why the right key would take us + * backward if there are no links selected?, I agree + * with Torben than doing nothing in this case is better + */ + /* If there are no links, go backward in history */ + history_ptr--; + if (history_ptr < 0) + history_ptr = HISTORY_SIZE - 1; + + currentpoint = history[history_ptr].page; + selected_item = history[history_ptr].link; +#endif + } + else + { + history_ptr = (history_ptr + 1) % HISTORY_SIZE; + history[history_ptr].page = currentpoint; + history[history_ptr].link = selected_item; + currentpoint = help_follow_link (currentpoint, selected_item); + } + + selected_item = NULL; +} + +/* --------------------------------------------------------------------------------------------- */ + +static cb_ret_t +help_execute_cmd (long command) +{ + cb_ret_t ret = MSG_HANDLED; + + switch (command) + { + case CK_Help: + help_help (whelp); + break; + case CK_Index: + help_index (whelp); + break; + case CK_Back: + help_back (whelp); + break; + case CK_Up: + help_prev_link (TRUE); + break; + case CK_Down: + help_next_link (TRUE); + break; + case CK_PageDown: + move_forward (help_lines - 1); + break; + case CK_PageUp: + move_backward (help_lines - 1); + break; + case CK_HalfPageDown: + move_forward (help_lines / 2); + break; + case CK_HalfPageUp: + move_backward (help_lines / 2); + break; + case CK_Top: + move_to_top (); + break; + case CK_Bottom: + move_to_bottom (); + break; + case CK_Enter: + help_select_link (); + break; + case CK_LinkNext: + help_next_link (FALSE); + break; + case CK_LinkPrev: + help_prev_link (FALSE); + break; + case CK_NodeNext: + help_next_node (); + break; + case CK_NodePrev: + help_prev_node (); + break; + case CK_Quit: + dlg_stop (whelp); + break; + default: + ret = MSG_NOT_HANDLED; + } + + return ret; +} + +/* --------------------------------------------------------------------------------------------- */ + +static cb_ret_t +help_handle_key (WDialog * h, int key) +{ + Widget *w = WIDGET (h); + long command; + + command = widget_lookup_key (w, key); + if (command == CK_IgnoreKey) + return MSG_NOT_HANDLED; + + return help_execute_cmd (command); +} + +/* --------------------------------------------------------------------------------------------- */ + +static cb_ret_t +help_bg_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data) +{ + switch (msg) + { + case MSG_DRAW: + frame_callback (w, NULL, MSG_DRAW, 0, NULL); + help_show (DIALOG (w->owner), currentpoint); + return MSG_HANDLED; + + default: + return frame_callback (w, sender, msg, parm, data); + } +} + +/* --------------------------------------------------------------------------------------------- */ + +static cb_ret_t +help_resize (WDialog * h) +{ + Widget *w = WIDGET (h); + WButtonBar *bb; + WRect r = w->rect; + + help_lines = MIN (LINES - 4, MAX (2 * LINES / 3, 18)); + r.lines = help_lines + 4; + r.cols = HELP_WINDOW_WIDTH + 4; + dlg_default_callback (w, NULL, MSG_RESIZE, 0, &r); + bb = find_buttonbar (h); + widget_set_size (WIDGET (bb), LINES - 1, 0, 1, COLS); + + return MSG_HANDLED; +} + +/* --------------------------------------------------------------------------------------------- */ + +static cb_ret_t +help_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data) +{ + WDialog *h = DIALOG (w); + + switch (msg) + { + case MSG_RESIZE: + return help_resize (h); + + case MSG_KEY: + { + cb_ret_t ret; + + ret = help_handle_key (h, parm); + if (ret == MSG_HANDLED) + widget_draw (w); + + return ret; + } + + case MSG_ACTION: + /* Handle shortcuts and buttonbar. */ + return help_execute_cmd (parm); + + default: + return dlg_default_callback (w, sender, msg, parm, data); + } +} + +/* --------------------------------------------------------------------------------------------- */ + +static void +interactive_display_finish (void) +{ + clear_link_areas (); +} + +/* --------------------------------------------------------------------------------------------- */ +/** translate help file into terminal encoding */ + +static void +translate_file (char *filedata) +{ + GIConv conv; + + conv = str_crt_conv_from ("UTF-8"); + if (conv != INVALID_CONV) + { + GString *translated_data; + gboolean nok; + + g_free (fdata); + + /* initial allocation for largest whole help file */ + translated_data = g_string_sized_new (32 * 1024); + nok = (str_convert (conv, filedata, translated_data) == ESTR_FAILURE); + fdata = g_string_free (translated_data, nok); + + str_close_conv (conv); + } +} + +/* --------------------------------------------------------------------------------------------- */ + +static cb_ret_t +md_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data) +{ + switch (msg) + { + case MSG_RESIZE: + widget_default_callback (w, NULL, MSG_RESIZE, 0, data); + w->rect.lines = help_lines; + return MSG_HANDLED; + + default: + return widget_default_callback (w, sender, msg, parm, data); + } +} + +/* --------------------------------------------------------------------------------------------- */ + +static void +help_mouse_callback (Widget * w, mouse_msg_t msg, mouse_event_t * event) +{ + int x, y; + GSList *current_area; + + if (msg != MSG_MOUSE_CLICK) + return; + + if ((event->buttons & GPM_B_RIGHT) != 0) + { + /* Right button click */ + help_back (whelp); + return; + } + + /* Left bytton click */ + + /* The event is relative to the dialog window, adjust it: */ + x = event->x - 1; + y = event->y - 1; + + /* Test whether the mouse click is inside one of the link areas */ + for (current_area = link_area; current_area != NULL; current_area = g_slist_next (current_area)) + { + Link_Area *la = (Link_Area *) current_area->data; + + /* Test one line link area */ + if (y == la->y1 && x >= la->x1 && y == la->y2 && x <= la->x2) + break; + + /* Test two line link area */ + if (la->y1 + 1 == la->y2) + { + /* The first line || The second line */ + if ((y == la->y1 && x >= la->x1) || (y == la->y2 && x <= la->x2)) + break; + } + /* Mouse will not work with link areas of more than two lines */ + } + + /* Test whether a link area was found */ + if (current_area != NULL) + { + Link_Area *la = (Link_Area *) current_area->data; + + /* The click was inside a link area -> follow the link */ + history_ptr = (history_ptr + 1) % HISTORY_SIZE; + history[history_ptr].page = currentpoint; + history[history_ptr].link = la->link_name; + currentpoint = help_follow_link (currentpoint, la->link_name); + selected_item = NULL; + } + else if (y < 0) + move_backward (help_lines - 1); + else if (y >= help_lines) + move_forward (help_lines - 1); + else if (y < help_lines / 2) + move_backward (1); + else + move_forward (1); + + /* Show the new node */ + widget_draw (WIDGET (w->owner)); +} + +/* --------------------------------------------------------------------------------------------- */ + +static Widget * +mousedispatch_new (const WRect * r) +{ + Widget *w; + + w = g_new0 (Widget, 1); + widget_init (w, r, md_callback, help_mouse_callback); + w->options |= WOP_SELECTABLE | WOP_WANT_CURSOR; + + return w; +} + +/* --------------------------------------------------------------------------------------------- */ +/*** public functions ****************************************************************************/ +/* --------------------------------------------------------------------------------------------- */ + +/* event callback */ +gboolean +help_interactive_display (const gchar * event_group_name, const gchar * event_name, + gpointer init_data, gpointer data) +{ + const dlg_colors_t help_colors = { + HELP_NORMAL_COLOR, /* common text color */ + 0, /* unused in help */ + HELP_BOLD_COLOR, /* bold text color */ + 0, /* unused in help */ + HELP_TITLE_COLOR /* title color */ + }; + + Widget *wh; + WGroup *g; + WButtonBar *help_bar; + Widget *md; + char *hlpfile = NULL; + char *filedata; + ev_help_t *event_data = (ev_help_t *) data; + WRect r = { 1, 1, 1, 1 }; + + (void) event_group_name; + (void) event_name; + (void) init_data; + + if (event_data->filename != NULL) + g_file_get_contents (event_data->filename, &filedata, NULL, NULL); + else + filedata = load_mc_home_file (mc_global.share_data_dir, MC_HELP, &hlpfile, NULL); + + if (filedata == NULL) + message (D_ERROR, MSG_ERROR, _("Cannot open file %s\n%s"), + event_data->filename ? event_data->filename : hlpfile, unix_error_string (errno)); + + g_free (hlpfile); + + if (filedata == NULL) + return TRUE; + + translate_file (filedata); + + g_free (filedata); + + if (fdata == NULL) + return TRUE; + + if ((event_data->node == NULL) || (*event_data->node == '\0')) + event_data->node = "[main]"; + + main_node = search_string (fdata, event_data->node); + + if (main_node == NULL) + { + message (D_ERROR, MSG_ERROR, _("Cannot find node %s in help file"), event_data->node); + + /* Fallback to [main], return if it also cannot be found */ + main_node = search_string (fdata, "[main]"); + if (main_node == NULL) + { + interactive_display_finish (); + return TRUE; + } + } + + help_lines = MIN (LINES - 4, MAX (2 * LINES / 3, 18)); + + whelp = + dlg_create (TRUE, 0, 0, help_lines + 4, HELP_WINDOW_WIDTH + 4, WPOS_CENTER | WPOS_TRYUP, + FALSE, help_colors, help_callback, NULL, "[Help]", _("Help")); + wh = WIDGET (whelp); + g = GROUP (whelp); + wh->keymap = help_map; + widget_want_tab (wh, TRUE); + /* draw background */ + whelp->bg->callback = help_bg_callback; + + selected_item = search_string_node (main_node, STRING_LINK_START) - 1; + currentpoint = main_node + 1; /* Skip the newline following the start of the node */ + + for (history_ptr = HISTORY_SIZE; history_ptr;) + { + history_ptr--; + history[history_ptr].page = currentpoint; + history[history_ptr].link = selected_item; + } + + help_bar = buttonbar_new (); + WIDGET (help_bar)->rect.y -= wh->rect.y; + WIDGET (help_bar)->rect.x -= wh->rect.x; + + r.lines = help_lines; + r.cols = HELP_WINDOW_WIDTH - 2; + md = mousedispatch_new (&r); + + group_add_widget (g, md); + group_add_widget (g, help_bar); /* FIXME */ + + buttonbar_set_label (help_bar, 1, Q_ ("ButtonBar|Help"), wh->keymap, NULL); + buttonbar_set_label (help_bar, 2, Q_ ("ButtonBar|Index"), wh->keymap, NULL); + buttonbar_set_label (help_bar, 3, Q_ ("ButtonBar|Prev"), wh->keymap, NULL); + buttonbar_set_label (help_bar, 4, "", wh->keymap, NULL); + buttonbar_set_label (help_bar, 5, "", wh->keymap, NULL); + buttonbar_set_label (help_bar, 6, "", wh->keymap, NULL); + buttonbar_set_label (help_bar, 7, "", wh->keymap, NULL); + buttonbar_set_label (help_bar, 8, "", wh->keymap, NULL); + buttonbar_set_label (help_bar, 9, "", wh->keymap, NULL); + buttonbar_set_label (help_bar, 10, Q_ ("ButtonBar|Quit"), wh->keymap, NULL); + + dlg_run (whelp); + interactive_display_finish (); + widget_destroy (wh); + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ |