diff options
author | Norbert Preining <norbert@preining.info> | 2019-09-02 13:46:59 +0900 |
---|---|---|
committer | Norbert Preining <norbert@preining.info> | 2019-09-02 13:46:59 +0900 |
commit | e0c6872cf40896c7be36b11dcc744620f10adf1d (patch) | |
tree | 60335e10d2f4354b0674ec22d7b53f0f8abee672 /macros/texinfo/texinfo/info/nodemenu.c |
Initial commit
Diffstat (limited to 'macros/texinfo/texinfo/info/nodemenu.c')
-rw-r--r-- | macros/texinfo/texinfo/info/nodemenu.c | 283 |
1 files changed, 283 insertions, 0 deletions
diff --git a/macros/texinfo/texinfo/info/nodemenu.c b/macros/texinfo/texinfo/info/nodemenu.c new file mode 100644 index 0000000000..a870c07642 --- /dev/null +++ b/macros/texinfo/texinfo/info/nodemenu.c @@ -0,0 +1,283 @@ +/* nodemenu.c -- produce a menu of all visited nodes. + + Copyright 1993-2019 Free Software Foundation, Inc. + + This program 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. + + This program 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/>. + + Originally written by Brian Fox. */ + +#include "info.h" +#include "session.h" +#include "echo-area.h" +#include "variables.h" + +static NODE *get_visited_nodes (void); + +/* Return a line describing the format of a node information line. */ +static const char * +nodemenu_format_info (void) +{ + /* TRANSLATORS: The "\n* Menu:\n\n" part of this should not be translated, as + it is part of the Info syntax. */ + return _("\n* Menu:\n\n\ + (File)Node Lines Size Containing File\n\ + ---------- ----- ---- ---------------"); +} + +/* Produce a formatted line of information about NODE. Here is what we want + the output listing to look like: + +* Menu: + (File)Node Lines Size Containing File + ---------- ----- ---- --------------- +* (emacs)Buffers:: 48 2230 /usr/gnu/info/emacs/emacs-1 +* (autoconf)Writing configure.in:: 123 58789 /usr/gnu/info/autoconf/autoconf-1 +* (dir)Top:: 40 589 /usr/gnu/info/dir +*/ +static char * +format_node_info (NODE *node) +{ + register int i; + char *containing_file; + static struct text_buffer line_buffer = { 0 }; + + if (!text_buffer_base (&line_buffer)) + text_buffer_init (&line_buffer); + else + text_buffer_reset (&line_buffer); + + if (node->subfile) + containing_file = node->subfile; + else + containing_file = node->fullpath; + + if (!containing_file || !*containing_file) + text_buffer_printf (&line_buffer, "* %s::", node->nodename); + else + text_buffer_printf (&line_buffer, "* (%s)%s::", + filename_non_directory (node->fullpath), + node->nodename); + + for (i = text_buffer_off (&line_buffer); i < 36; i++) + text_buffer_add_char (&line_buffer, ' '); + + { + int lines = 1; + + for (i = 0; i < node->nodelen; i++) + if (node->contents[i] == '\n') + lines++; + + text_buffer_printf (&line_buffer, "%d", lines); + } + + text_buffer_add_char (&line_buffer, ' '); + for (i = text_buffer_off (&line_buffer); i < 44; i++) + text_buffer_add_char (&line_buffer, ' '); + text_buffer_printf (&line_buffer, "%ld", node->nodelen); + + if (containing_file) + { + for (i = text_buffer_off (&line_buffer); i < 51; i++) + text_buffer_add_char (&line_buffer, ' '); + text_buffer_printf (&line_buffer, containing_file); + } + + return xstrdup (text_buffer_base (&line_buffer)); +} + +/* Little string comparison routine for qsort (). */ +static int +compare_strings (const void *entry1, const void *entry2) +{ + char **e1 = (char **) entry1; + char **e2 = (char **) entry2; + + return mbscasecmp (*e1, *e2); +} + +/* The name of the nodemenu node. */ +static char *nodemenu_nodename = "*Node Menu*"; + +/* Produce an informative listing of all the visited nodes, and return it + in a newly allocated node. */ +static NODE * +get_visited_nodes (void) +{ + register int i; + WINDOW *info_win; + NODE *node; + char **lines = NULL; + size_t lines_index = 0, lines_slots = 0; + struct text_buffer message; + + for (info_win = windows; info_win; info_win = info_win->next) + { + for (i = 0; i < info_win->hist_index; i++) + { + NODE *history_node = info_win->hist[i]->node; + + /* We skip mentioning "*Node Menu*" nodes. */ + if (strcmp (history_node->nodename, nodemenu_nodename) == 0) + continue; + + if (history_node) + { + char *line; + + line = format_node_info (history_node); + add_pointer_to_array (line, lines_index, lines, lines_slots, 20); + } + } + } + + /* Sort the array of information lines, if there are any. */ + if (lines) + { + register int j, newlen; + char **temp; + + qsort (lines, lines_index, sizeof (char *), compare_strings); + + /* Delete duplicates. */ + for (i = 0, newlen = 1; i < lines_index - 1; i++) + { + /* Use FILENAME_CMP here, since the most important piece + of info in each line is the file name of the node. */ + if (FILENAME_CMP (lines[i], lines[i + 1]) == 0) + { + free (lines[i]); + lines[i] = NULL; + } + else + newlen++; + } + + /* We have free ()'d and marked all of the duplicate slots. + Copy the live slots rather than pruning the dead slots. */ + temp = xmalloc ((1 + newlen) * sizeof (char *)); + for (i = 0, j = 0; i < lines_index; i++) + if (lines[i]) + temp[j++] = lines[i]; + + temp[j] = NULL; + free (lines); + lines = temp; + lines_index = newlen; + } + + text_buffer_init (&message); + + text_buffer_printf (&message, "\n"); + text_buffer_printf (&message, + "%s", replace_in_documentation + (_("Here is the menu of nodes you have recently visited.\n\ +Select one from this menu, or use '\\[history-node]' in another window.\n"), 0)); + + text_buffer_printf (&message, "%s\n", nodemenu_format_info ()); + + for (i = 0; (lines != NULL) && (i < lines_index); i++) + { + text_buffer_printf (&message, "%s\n", lines[i]); + free (lines[i]); + } + + if (lines) + free (lines); + + node = text_buffer_to_node (&message); + scan_node_contents (node, 0, 0); + + return node; +} + +DECLARE_INFO_COMMAND (list_visited_nodes, + _("Make a window containing a menu of all of the currently visited nodes")) +{ + WINDOW *new; + NODE *node; + + /* If a window is visible and showing the buffer list already, re-use it. */ + for (new = windows; new; new = new->next) + { + node = new->node; + + if (internal_info_node_p (node) && + (strcmp (node->nodename, nodemenu_nodename) == 0)) + break; + } + + /* If we couldn't find an existing window, try to use the next window + in the chain. */ + if (!new) + { + if (window->next) + new = window->next; + /* If there is more than one window, wrap around. */ + else if (window != windows) + new = windows; + } + + /* If we still don't have a window, make a new one to contain the list. */ + if (!new) + new = window_make_window (); + + /* If we couldn't make a new window, use this one. */ + if (!new) + new = window; + + /* Lines do not wrap in this window. */ + new->flags |= W_NoWrap; + node = get_visited_nodes (); + name_internal_node (node, xstrdup (nodemenu_nodename)); + node->flags |= N_WasRewritten; + + info_set_node_of_window (new, node); + active_window = new; +} + +DECLARE_INFO_COMMAND (select_visited_node, + _("Select a node which has been previously visited in a visible window")) +{ + char *line; + NODE *node; + + node = get_visited_nodes (); + + line = info_read_completing_in_echo_area (_("Select visited node: "), + node->references); + + window = active_window; + + if (!line) + /* User aborts, just quit. */ + info_abort_key (window, 0); + else if (*line) + { + REFERENCE *entry; + + /* Find the selected label in the references. */ + entry = info_get_menu_entry_by_label (node, line, 0); + + if (!entry) + /* This shouldn't happen, because LINE was in the completion list + built from the list of references. */ + info_error (_("The reference disappeared! (%s)."), line); + else + info_select_reference (window, entry); + } + + free (line); + free (node); +} |