nodemenu.c revision 1.3
1/* nodemenu.c -- produce a menu of all visited nodes. 2 $Id: nodemenu.c,v 1.3 2000/02/09 02:18:40 espie Exp $ 3 4 Copyright (C) 1993, 97, 98 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 /* Use FILENAME_CMP here, since the most important piece 172 of info in each line is the file name of the node. */ 173 if (FILENAME_CMP (lines[i], lines[i + 1]) == 0) 174 { 175 free (lines[i]); 176 lines[i] = (char *)NULL; 177 } 178 else 179 newlen++; 180 } 181 182 /* We have free ()'d and marked all of the duplicate slots. 183 Copy the live slots rather than pruning the dead slots. */ 184 temp = (char **)xmalloc ((1 + newlen) * sizeof (char *)); 185 for (i = 0, j = 0; i < lines_index; i++) 186 if (lines[i]) 187 temp[j++] = lines[i]; 188 189 temp[j] = (char *)NULL; 190 free (lines); 191 lines = temp; 192 lines_index = newlen; 193 } 194 195 initialize_message_buffer (); 196 197 printf_to_message_buffer 198 ("%s", replace_in_documentation 199 (_("Here is the menu of nodes you have recently visited.\n\ 200Select one from this menu, or use `\\[history-node]' in another window.\n"))); 201 202 printf_to_message_buffer ("%s\n", nodemenu_format_info ()); 203 204 for (i = 0; (lines != (char **)NULL) && (i < lines_index); i++) 205 { 206 printf_to_message_buffer ("%s\n", lines[i]); 207 free (lines[i]); 208 } 209 210 if (lines) 211 free (lines); 212 213 node = message_buffer_to_node (); 214 add_gcable_pointer (node->contents); 215 return (node); 216} 217 218DECLARE_INFO_COMMAND (list_visited_nodes, 219 _("Make a window containing a menu of all of the currently visited nodes")) 220{ 221 WINDOW *new; 222 NODE *node; 223 224 set_remembered_pagetop_and_point (window); 225 226 /* If a window is visible and showing the buffer list already, re-use it. */ 227 for (new = windows; new; new = new->next) 228 { 229 node = new->node; 230 231 if (internal_info_node_p (node) && 232 (strcmp (node->nodename, nodemenu_nodename) == 0)) 233 break; 234 } 235 236 /* If we couldn't find an existing window, try to use the next window 237 in the chain. */ 238 if (!new) 239 { 240 if (window->next) 241 new = window->next; 242 /* If there is more than one window, wrap around. */ 243 else if (window != windows) 244 new = windows; 245 } 246 247 /* If we still don't have a window, make a new one to contain the list. */ 248 if (!new) 249 { 250 WINDOW *old_active; 251 252 old_active = active_window; 253 active_window = window; 254 new = window_make_window ((NODE *)NULL); 255 active_window = old_active; 256 } 257 258 /* If we couldn't make a new window, use this one. */ 259 if (!new) 260 new = window; 261 262 /* Lines do not wrap in this window. */ 263 new->flags |= W_NoWrap; 264 node = get_visited_nodes ((Function *)NULL); 265 name_internal_node (node, nodemenu_nodename); 266 267#if 0 268 /* Even if this is an internal node, we don't want the window 269 system to treat it specially. So we turn off the internalness 270 of it here. */ 271 /* Why? We depend on internal_info_node_p returning true, so we must 272 not remove the flag. Otherwise, the *Node Menu* nodes themselves 273 appear in the node menu. --Andreas Schwab 274 <schwab@issan.informatik.uni-dortmund.de>. */ 275 node->flags &= ~N_IsInternal; 276#endif 277 278 /* If this window is already showing a node menu, reuse the existing node 279 slot. */ 280 { 281 int remember_me = 1; 282 283#if defined (NOTDEF) 284 if (internal_info_node_p (new->node) && 285 (strcmp (new->node->nodename, nodemenu_nodename) == 0)) 286 remember_me = 0; 287#endif /* NOTDEF */ 288 289 window_set_node_of_window (new, node); 290 291 if (remember_me) 292 remember_window_and_node (new, node); 293 } 294 295 active_window = new; 296} 297 298DECLARE_INFO_COMMAND (select_visited_node, 299 _("Select a node which has been previously visited in a visible window")) 300{ 301 char *line; 302 NODE *node; 303 REFERENCE **menu; 304 305 node = get_visited_nodes ((Function *)NULL); 306 307 menu = info_menu_of_node (node); 308 free (node); 309 310 line = 311 info_read_completing_in_echo_area (window, _("Select visited node: "), menu); 312 313 window = active_window; 314 315 /* User aborts, just quit. */ 316 if (!line) 317 { 318 info_abort_key (window, 0, 0); 319 info_free_references (menu); 320 return; 321 } 322 323 if (*line) 324 { 325 REFERENCE *entry; 326 327 /* Find the selected label in the references. */ 328 entry = info_get_labeled_reference (line, menu); 329 330 if (!entry) 331 info_error (_("The reference disappeared! (%s)."), line); 332 else 333 info_select_reference (window, entry); 334 } 335 336 free (line); 337 info_free_references (menu); 338 339 if (!info_error_was_printed) 340 window_clear_echo_area (); 341} 342