1/* nodemenu.c -- Produce a menu of all visited nodes. 2 $Id: nodemenu.c,v 1.1 2004/10/28 18:14:09 zooey Exp $ 3 4 Copyright (C) 1993, 97 Free Software Foundation, Inc. 5 6 This program is free software; you can redistribute it and/or modify 7 it under the terms of the GNU General Public License as published by 8 the Free Software Foundation; either version 2, or (at your option) 9 any later version. 10 11 This program is distributed in the hope that it will be useful, 12 but WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 GNU General Public License for more details. 15 16 You should have received a copy of the GNU General Public License 17 along with this program; if not, write to the Free Software 18 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 19 20 Written by Brian Fox (bfox@ai.mit.edu). */ 21 22#include "info.h" 23 24/* Return a line describing the format of a node information line. */ 25static char * 26nodemenu_format_info () 27{ 28 return (_("\n\ 29* Menu:\n\ 30 (File)Node Lines Size Containing File\n\ 31 ---------- ----- ---- ---------------")); 32} 33 34/* Produce a formatted line of information about NODE. Here is what we want 35 the output listing to look like: 36 37* Menu: 38 (File)Node Lines Size Containing File 39 ---------- ----- ---- --------------- 40* (emacs)Buffers:: 48 2230 /usr/gnu/info/emacs/emacs-1 41* (autoconf)Writing configure.in:: 123 58789 /usr/gnu/info/autoconf/autoconf-1 42* (dir)Top:: 40 589 /usr/gnu/info/dir 43*/ 44static char * 45format_node_info (node) 46 NODE *node; 47{ 48 register int i, len; 49 char *parent, *containing_file; 50 static char *line_buffer = (char *)NULL; 51 52 if (!line_buffer) 53 line_buffer = (char *)xmalloc (1000); 54 55 if (node->parent) 56 { 57 parent = filename_non_directory (node->parent); 58 if (!parent) 59 parent = node->parent; 60 } 61 else 62 parent = (char *)NULL; 63 64 containing_file = node->filename; 65 66 if (!parent && !*containing_file) 67 sprintf (line_buffer, "* %s::", node->nodename); 68 else 69 { 70 char *file = (char *)NULL; 71 72 if (parent) 73 file = parent; 74 else 75 file = filename_non_directory (containing_file); 76 77 if (!file) 78 file = containing_file; 79 80 if (!*file) 81 file = "dir"; 82 83 sprintf (line_buffer, "* (%s)%s::", file, node->nodename); 84 } 85 86 len = pad_to (36, line_buffer); 87 88 { 89 int lines = 1; 90 91 for (i = 0; i < node->nodelen; i++) 92 if (node->contents[i] == '\n') 93 lines++; 94 95 sprintf (line_buffer + len, "%d", lines); 96 } 97 98 len = pad_to (44, line_buffer); 99 sprintf (line_buffer + len, "%ld", node->nodelen); 100 101 if (node->filename && *(node->filename)) 102 { 103 len = pad_to (51, line_buffer); 104 sprintf (line_buffer + len, node->filename); 105 } 106 107 return xstrdup (line_buffer); 108} 109 110/* Little string comparison routine for qsort (). */ 111static int 112compare_strings (string1, string2) 113 char **string1, **string2; 114{ 115 return (strcasecmp (*string1, *string2)); 116} 117 118/* The name of the nodemenu node. */ 119static char *nodemenu_nodename = "*Node Menu*"; 120 121/* Produce an informative listing of all the visited nodes, and return it 122 in a node. If FILTER_FUNC is non-null, it is a function which filters 123 which nodes will appear in the listing. FILTER_FUNC takes an argument 124 of NODE, and returns non-zero if the node should appear in the listing. */ 125NODE * 126get_visited_nodes (filter_func) 127 Function *filter_func; 128{ 129 register int i, iw_index; 130 INFO_WINDOW *info_win; 131 NODE *node; 132 char **lines = (char **)NULL; 133 int lines_index = 0, lines_slots = 0; 134 135 if (!info_windows) 136 return ((NODE *)NULL); 137 138 for (iw_index = 0; (info_win = info_windows[iw_index]); iw_index++) 139 { 140 for (i = 0; i < info_win->nodes_index; i++) 141 { 142 node = info_win->nodes[i]; 143 144 /* We skip mentioning "*Node Menu*" nodes. */ 145 if (internal_info_node_p (node) && 146 (strcmp (node->nodename, nodemenu_nodename) == 0)) 147 continue; 148 149 if (node && (!filter_func || (*filter_func) (node))) 150 { 151 char *line; 152 153 line = format_node_info (node); 154 add_pointer_to_array 155 (line, lines_index, lines, lines_slots, 20, char *); 156 } 157 } 158 } 159 160 /* Sort the array of information lines, if there are any. */ 161 if (lines) 162 { 163 register int j, newlen; 164 char **temp; 165 166 qsort (lines, lines_index, sizeof (char *), compare_strings); 167 168 /* Delete duplicates. */ 169 for (i = 0, newlen = 1; i < lines_index - 1; i++) 170 { 171 if (strcmp (lines[i], lines[i + 1]) == 0) 172 { 173 free (lines[i]); 174 lines[i] = (char *)NULL; 175 } 176 else 177 newlen++; 178 } 179 180 /* We have free ()'d and marked all of the duplicate slots. 181 Copy the live slots rather than pruning the dead slots. */ 182 temp = (char **)xmalloc ((1 + newlen) * sizeof (char *)); 183 for (i = 0, j = 0; i < lines_index; i++) 184 if (lines[i]) 185 temp[j++] = lines[i]; 186 187 temp[j] = (char *)NULL; 188 free (lines); 189 lines = temp; 190 lines_index = newlen; 191 } 192 193 initialize_message_buffer (); 194 195 printf_to_message_buffer 196 ("%s", replace_in_documentation 197 (_("Here is the menu of nodes you have recently visited.\n\ 198Select one from this menu, or use `\\[history-node]' in another window.\n"))); 199 200 printf_to_message_buffer ("%s\n", nodemenu_format_info ()); 201 202 for (i = 0; (lines != (char **)NULL) && (i < lines_index); i++) 203 { 204 printf_to_message_buffer ("%s\n", lines[i]); 205 free (lines[i]); 206 } 207 208 if (lines) 209 free (lines); 210 211 node = message_buffer_to_node (); 212 add_gcable_pointer (node->contents); 213 return (node); 214} 215 216DECLARE_INFO_COMMAND (list_visited_nodes, 217 _("Make a window containing a menu of all of the currently visited nodes")) 218{ 219 WINDOW *new; 220 NODE *node; 221 222 set_remembered_pagetop_and_point (window); 223 224 /* If a window is visible and showing the buffer list already, re-use it. */ 225 for (new = windows; new; new = new->next) 226 { 227 node = new->node; 228 229 if (internal_info_node_p (node) && 230 (strcmp (node->nodename, nodemenu_nodename) == 0)) 231 break; 232 } 233 234 /* If we couldn't find an existing window, try to use the next window 235 in the chain. */ 236 if (!new) 237 { 238 if (window->next) 239 new = window->next; 240 /* If there is more than one window, wrap around. */ 241 else if (window != windows) 242 new = windows; 243 } 244 245 /* If we still don't have a window, make a new one to contain the list. */ 246 if (!new) 247 { 248 WINDOW *old_active; 249 250 old_active = active_window; 251 active_window = window; 252 new = window_make_window ((NODE *)NULL); 253 active_window = old_active; 254 } 255 256 /* If we couldn't make a new window, use this one. */ 257 if (!new) 258 new = window; 259 260 /* Lines do not wrap in this window. */ 261 new->flags |= W_NoWrap; 262 node = get_visited_nodes ((Function *)NULL); 263 name_internal_node (node, nodemenu_nodename); 264 265#if 0 266 /* Even if this is an internal node, we don't want the window 267 system to treat it specially. So we turn off the internalness 268 of it here. */ 269 /* Why? We depend on internal_info_node_p returning true, so we must 270 not remove the flag. Otherwise, the *Node Menu* nodes themselves 271 appear in the node menu. --Andreas Schwab 272 <schwab@issan.informatik.uni-dortmund.de>. */ 273 node->flags &= ~N_IsInternal; 274#endif 275 276 /* If this window is already showing a node menu, reuse the existing node 277 slot. */ 278 { 279 int remember_me = 1; 280 281#if defined (NOTDEF) 282 if (internal_info_node_p (new->node) && 283 (strcmp (new->node->nodename, nodemenu_nodename) == 0)) 284 remember_me = 0; 285#endif /* NOTDEF */ 286 287 window_set_node_of_window (new, node); 288 289 if (remember_me) 290 remember_window_and_node (new, node); 291 } 292 293 active_window = new; 294} 295 296DECLARE_INFO_COMMAND (select_visited_node, 297 _("Select a node which has been previously visited in a visible window")) 298{ 299 char *line; 300 NODE *node; 301 REFERENCE **menu; 302 303 node = get_visited_nodes ((Function *)NULL); 304 305 menu = info_menu_of_node (node); 306 free (node); 307 308 line = 309 info_read_completing_in_echo_area (window, _("Select visited node: "), menu); 310 311 window = active_window; 312 313 /* User aborts, just quit. */ 314 if (!line) 315 { 316 info_abort_key (window, 0, 0); 317 info_free_references (menu); 318 return; 319 } 320 321 if (*line) 322 { 323 REFERENCE *entry; 324 325 /* Find the selected label in the references. */ 326 entry = info_get_labeled_reference (line, menu); 327 328 if (!entry) 329 info_error (_("The reference disappeared! (%s)."), line); 330 else 331 info_select_reference (window, entry); 332 } 333 334 free (line); 335 info_free_references (menu); 336 337 if (!info_error_was_printed) 338 window_clear_echo_area (); 339} 340