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