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 |
|
24 #include "info.h" |
|
25 |
|
26 /* Return a line describing the format of a node information line. */ |
|
27 static char * |
|
28 nodemenu_format_info () |
|
29 { |
|
30 return ("\n\ |
|
31 * Menu:\n\ |
|
32 (File)Node Lines Size Containing File\n\ |
|
33 ---------- ----- ---- ---------------"); |
|
34 } |
|
35 |
|
36 /* Produce a formatted line of information about NODE. Here is what we want |
|
37 the output listing to look like: |
|
38 |
|
39 * Menu: |
|
40 (File)Node Lines Size Containing File |
|
41 ---------- ----- ---- --------------- |
|
42 * (emacs)Buffers:: 48 2230 /usr/gnu/info/emacs/emacs-1 |
|
43 * (autoconf)Writing configure.in:: 123 58789 /usr/gnu/info/autoconf/autoconf-1 |
|
44 * (dir)Top:: 40 589 /usr/gnu/info/dir |
|
45 */ |
|
46 static char * |
|
47 format_node_info (node) |
|
48 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, "%d", node->nodelen); |
|
102 |
|
103 if (node->filename && *(node->filename)) |
|
104 { |
|
105 len = pad_to (51, line_buffer); |
|
106 sprintf (line_buffer + len, node->filename); |
|
107 } |
|
108 |
|
109 return (savestring (line_buffer)); |
|
110 } |
|
111 |
|
112 /* Little string comparison routine for qsort (). */ |
|
113 static int |
|
114 compare_strings (string1, string2) |
|
115 char **string1, **string2; |
|
116 { |
|
117 return (stricmp (*string1, *string2)); |
|
118 } |
|
119 |
|
120 /* The name of the nodemenu node. */ |
|
121 static char *nodemenu_nodename = "*Node Menu*"; |
|
122 |
|
123 /* Produce an informative listing of all the visited nodes, and return it |
|
124 in a node. If FILTER_FUNC is non-null, it is a function which filters |
|
125 which nodes will appear in the listing. FILTER_FUNC takes an argument |
|
126 of NODE, and returns non-zero if the node should appear in the listing. */ |
|
127 NODE * |
|
128 get_visited_nodes (filter_func) |
|
129 Function *filter_func; |
|
130 { |
|
131 register int i, iw_index; |
|
132 INFO_WINDOW *info_win; |
|
133 NODE *node; |
|
134 char **lines = (char **)NULL; |
|
135 int lines_index = 0, lines_slots = 0; |
|
136 |
|
137 if (!info_windows) |
|
138 return ((NODE *)NULL); |
|
139 |
|
140 for (iw_index = 0; info_win = info_windows[iw_index]; iw_index++) |
|
141 { |
|
142 for (i = 0; i < info_win->nodes_index; i++) |
|
143 { |
|
144 node = info_win->nodes[i]; |
|
145 |
|
146 /* We skip mentioning "*Node Menu*" nodes. */ |
|
147 if (internal_info_node_p (node) && |
|
148 (strcmp (node->nodename, nodemenu_nodename) == 0)) |
|
149 continue; |
|
150 |
|
151 if (node && (!filter_func || (*filter_func) (node))) |
|
152 { |
|
153 char *line; |
|
154 |
|
155 line = format_node_info (node); |
|
156 add_pointer_to_array |
|
157 (line, lines_index, lines, lines_slots, 20, char *); |
|
158 } |
|
159 } |
|
160 } |
|
161 |
|
162 /* Sort the array of information lines. */ |
|
163 qsort (lines, lines_index, sizeof (char *), compare_strings); |
|
164 |
|
165 /* Delete duplicates. */ |
|
166 { |
|
167 register int j, newlen; |
|
168 char **temp; |
|
169 |
|
170 for (i = 0, newlen = 1; i < lines_index - 1; i++) |
|
171 { |
|
172 if (strcmp (lines[i], lines[i + 1]) == 0) |
|
173 { |
|
174 free (lines[i]); |
|
175 lines[i] = (char *)NULL; |
|
176 } |
|
177 else |
|
178 newlen++; |
|
179 } |
|
180 |
|
181 /* We have free ()'d and marked all of the duplicate slots. Copy the |
|
182 live slots rather than pruning the dead slots. */ |
|
183 temp = (char **)xmalloc ((1 + newlen) * sizeof (char *)); |
|
184 for (i = 0, j = 0; i < lines_index; i++) |
|
185 if (lines[i]) |
|
186 temp[j++] = lines[i]; |
|
187 |
|
188 temp[j] = (char *)NULL; |
|
189 free (lines); |
|
190 lines = temp; |
|
191 lines_index = newlen; |
|
192 } |
|
193 |
|
194 initialize_message_buffer (); |
|
195 printf_to_message_buffer |
|
196 ("Here is a menu of nodes you could select with info-history-node:\n"); |
|
197 printf_to_message_buffer ("%s\n", nodemenu_format_info ()); |
|
198 for (i = 0; i < lines_index; i++) |
|
199 { |
|
200 printf_to_message_buffer ("%s\n", lines[i]); |
|
201 free (lines[i]); |
|
202 } |
|
203 free (lines); |
|
204 |
|
205 node = message_buffer_to_node (); |
|
206 add_gcable_pointer (node->contents); |
|
207 return (node); |
|
208 } |
|
209 |
|
210 DECLARE_INFO_COMMAND (list_visited_nodes, |
|
211 "Make a window containing a menu of all of the currently visited nodes") |
|
212 { |
|
213 WINDOW *new; |
|
214 NODE *node; |
|
215 |
|
216 set_remembered_pagetop_and_point (window); |
|
217 |
|
218 /* If a window is visible and showing the buffer list already, re-use it. */ |
|
219 for (new = windows; new; new = new->next) |
|
220 { |
|
221 node = new->node; |
|
222 |
|
223 if (internal_info_node_p (node) && |
|
224 (strcmp (node->nodename, nodemenu_nodename) == 0)) |
|
225 break; |
|
226 } |
|
227 |
|
228 /* If we couldn't find an existing window, try to use the next window |
|
229 in the chain. */ |
|
230 if (!new && window->next) |
|
231 new = window->next; |
|
232 |
|
233 /* If we still don't have a window, make a new one to contain the list. */ |
|
234 if (!new) |
|
235 { |
|
236 WINDOW *old_active; |
|
237 |
|
238 old_active = active_window; |
|
239 active_window = window; |
|
240 new = window_make_window ((NODE *)NULL); |
|
241 active_window = old_active; |
|
242 } |
|
243 |
|
244 /* If we couldn't make a new window, use this one. */ |
|
245 if (!new) |
|
246 new = window; |
|
247 |
|
248 /* Lines do not wrap in this window. */ |
|
249 new->flags |= W_NoWrap; |
|
250 node = get_visited_nodes ((Function *)NULL); |
|
251 name_internal_node (node, nodemenu_nodename); |
|
252 |
|
253 /* Even if this is an internal node, we don't want the window |
|
254 system to treat it specially. So we turn off the internalness |
|
255 of it here. */ |
|
256 node->flags &= ~N_IsInternal; |
|
257 |
|
258 /* If this window is already showing a node menu, reuse the existing node |
|
259 slot. */ |
|
260 { |
|
261 int remember_me = 1; |
|
262 |
|
263 #if defined (NOTDEF) |
|
264 if (internal_info_node_p (new->node) && |
|
265 (strcmp (new->node->nodename, nodemenu_nodename) == 0)) |
|
266 remember_me = 0; |
|
267 #endif /* NOTDEF */ |
|
268 |
|
269 window_set_node_of_window (new, node); |
|
270 |
|
271 if (remember_me) |
|
272 remember_window_and_node (new, node); |
|
273 } |
|
274 |
|
275 active_window = new; |
|
276 } |
|
277 |
|
278 DECLARE_INFO_COMMAND (select_visited_node, |
|
279 "Select a node which has been previously visited in a visible window") |
|
280 { |
|
281 char *line; |
|
282 NODE *node; |
|
283 REFERENCE **menu; |
|
284 |
|
285 node = get_visited_nodes ((Function *)NULL); |
|
286 |
|
287 menu = info_menu_of_node (node); |
|
288 free (node); |
|
289 |
|
290 line = |
|
291 info_read_completing_in_echo_area (window, "Select visited node: ", menu); |
|
292 |
|
293 window = active_window; |
|
294 |
|
295 /* User aborts, just quit. */ |
|
296 if (!line) |
|
297 { |
|
298 info_abort_key (window, 0, 0); |
|
299 info_free_references (menu); |
|
300 return; |
|
301 } |
|
302 |
|
303 if (*line) |
|
304 { |
|
305 REFERENCE *entry; |
|
306 |
|
307 /* Find the selected label in the references. */ |
|
308 entry = info_get_labeled_reference (line, menu); |
|
309 |
|
310 if (!entry) |
|
311 info_error ("The reference disappeared! (%s).", line); |
|
312 else |
|
313 info_select_reference (window, entry); |
|
314 } |
|
315 |
|
316 free (line); |
|
317 info_free_references (menu); |
|
318 |
|
319 if (!info_error_was_printed) |
|
320 window_clear_echo_area (); |
|
321 } |