1 | /* nodemenu.c -- produce a menu of all visited nodes.
|
---|
2 | $Id: nodemenu.c,v 1.5 2004/04/11 17:56:46 karl 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 |
|
---|
25 | NODE * get_visited_nodes (Function *filter_func);
|
---|
26 |
|
---|
27 | /* Return a line describing the format of a node information line. */
|
---|
28 | static const char *
|
---|
29 | nodemenu_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 | */
|
---|
47 | static char *
|
---|
48 | format_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 (). */
|
---|
113 | static int
|
---|
114 | compare_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. */
|
---|
123 | static 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. */
|
---|
129 | NODE *
|
---|
130 | get_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\
|
---|
203 | Select 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 |
|
---|
223 | DECLARE_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 |
|
---|
303 | DECLARE_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 | }
|
---|