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