File: | src/gnu/usr.bin/texinfo/info/window.c |
Warning: | line 145, column 13 Use of memory after it is freed |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* window.c -- windows in Info. | ||||
2 | $Id: window.c,v 1.5 2006/07/17 16:12:36 espie Exp $ | ||||
3 | |||||
4 | Copyright (C) 1993, 1997, 1998, 2001, 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 | #include "nodes.h" | ||||
25 | #include "window.h" | ||||
26 | #include "display.h" | ||||
27 | #include "info-utils.h" | ||||
28 | #include "infomap.h" | ||||
29 | |||||
30 | /* The window which describes the screen. */ | ||||
31 | WINDOW *the_screen = NULL((void *)0); | ||||
32 | |||||
33 | /* The window which describes the echo area. */ | ||||
34 | WINDOW *the_echo_area = NULL((void *)0); | ||||
35 | |||||
36 | /* The list of windows in Info. */ | ||||
37 | WINDOW *windows = NULL((void *)0); | ||||
38 | |||||
39 | /* Pointer to the active window in WINDOW_LIST. */ | ||||
40 | WINDOW *active_window = NULL((void *)0); | ||||
41 | |||||
42 | /* The size of the echo area in Info. It never changes, irregardless of the | ||||
43 | size of the screen. */ | ||||
44 | #define ECHO_AREA_HEIGHT1 1 | ||||
45 | |||||
46 | /* Macro returns the amount of space that the echo area truly requires relative | ||||
47 | to the entire screen. */ | ||||
48 | #define echo_area_required(1 + the_echo_area->height) (1 + the_echo_area->height) | ||||
49 | |||||
50 | /* Initalize the window system by creating THE_SCREEN and THE_ECHO_AREA. | ||||
51 | Create the first window ever. | ||||
52 | You pass the dimensions of the total screen size. */ | ||||
53 | void | ||||
54 | window_initialize_windows (int width, int height) | ||||
55 | { | ||||
56 | the_screen = xmalloc (sizeof (WINDOW)); | ||||
57 | the_echo_area = xmalloc (sizeof (WINDOW)); | ||||
58 | windows = xmalloc (sizeof (WINDOW)); | ||||
59 | active_window = windows; | ||||
60 | |||||
61 | zero_mem (the_screen, sizeof (WINDOW))memset (the_screen, 0, sizeof (WINDOW)); | ||||
62 | zero_mem (the_echo_area, sizeof (WINDOW))memset (the_echo_area, 0, sizeof (WINDOW)); | ||||
63 | zero_mem (active_window, sizeof (WINDOW))memset (active_window, 0, sizeof (WINDOW)); | ||||
64 | |||||
65 | /* None of these windows has a goal column yet. */ | ||||
66 | the_echo_area->goal_column = -1; | ||||
67 | active_window->goal_column = -1; | ||||
68 | the_screen->goal_column = -1; | ||||
69 | |||||
70 | /* The active and echo_area windows are visible. | ||||
71 | The echo_area is permanent. | ||||
72 | The screen is permanent. */ | ||||
73 | active_window->flags = W_WindowVisible0x04; | ||||
74 | the_echo_area->flags = W_WindowIsPerm0x02 | W_InhibitMode0x08 | W_WindowVisible0x04; | ||||
75 | the_screen->flags = W_WindowIsPerm0x02; | ||||
76 | |||||
77 | /* The height of the echo area never changes. It is statically set right | ||||
78 | here, and it must be at least 1 line for display. The size of the | ||||
79 | initial window cannot be the same size as the screen, since the screen | ||||
80 | includes the echo area. So, we make the height of the initial window | ||||
81 | equal to the screen's displayable region minus the height of the echo | ||||
82 | area. */ | ||||
83 | the_echo_area->height = ECHO_AREA_HEIGHT1; | ||||
84 | active_window->height = the_screen->height - 1 - the_echo_area->height; | ||||
85 | window_new_screen_size (width, height); | ||||
| |||||
86 | |||||
87 | /* The echo area uses a different keymap than normal info windows. */ | ||||
88 | the_echo_area->keymap = echo_area_keymap; | ||||
89 | active_window->keymap = info_keymap; | ||||
90 | } | ||||
91 | |||||
92 | /* Given that the size of the screen has changed to WIDTH and HEIGHT | ||||
93 | from whatever it was before (found in the_screen->height, ->width), | ||||
94 | change the size (and possibly location) of each window in the screen. | ||||
95 | If a window would become too small, call the function DELETER on it, | ||||
96 | after deleting the window from our chain of windows. If DELETER is NULL, | ||||
97 | nothing extra is done. The last window can never be deleted, but it can | ||||
98 | become invisible. */ | ||||
99 | |||||
100 | /* If non-null, a function to call with WINDOW as argument when the function | ||||
101 | window_new_screen_size () has deleted WINDOW. */ | ||||
102 | VFunction *window_deletion_notifier = NULL((void *)0); | ||||
103 | |||||
104 | void | ||||
105 | window_new_screen_size (int width, int height) | ||||
106 | { | ||||
107 | register WINDOW *win; | ||||
108 | int delta_height, delta_each, delta_leftover; | ||||
109 | int numwins; | ||||
110 | |||||
111 | /* If no change, do nothing. */ | ||||
112 | if (width == the_screen->width && height == the_screen->height) | ||||
113 | return; | ||||
114 | |||||
115 | /* If the new window height is too small, make it be zero. */ | ||||
116 | if (height < (WINDOW_MIN_SIZE(2 + 1) + the_echo_area->height)) | ||||
117 | height = 0; | ||||
118 | if (width < 0) | ||||
119 | width = 0; | ||||
120 | |||||
121 | /* Find out how many windows will change. */ | ||||
122 | for (numwins = 0, win = windows; win; win = win->next, numwins++); | ||||
123 | |||||
124 | /* See if some windows will need to be deleted. This is the case if | ||||
125 | the screen is getting smaller, and the available space divided by | ||||
126 | the number of windows is less than WINDOW_MIN_SIZE. In that case, | ||||
127 | delete some windows and try again until there is either enough | ||||
128 | space to divy up among the windows, or until there is only one | ||||
129 | window left. */ | ||||
130 | while ((height - echo_area_required(1 + the_echo_area->height)) / numwins <= WINDOW_MIN_SIZE(2 + 1)) | ||||
131 | { | ||||
132 | /* If only one window, make the size of it be zero, and return | ||||
133 | immediately. */ | ||||
134 | if (!windows->next
| ||||
135 | { | ||||
136 | windows->height = 0; | ||||
137 | maybe_free (windows->line_starts)do { if (windows->line_starts) free (windows->line_starts ); } while (0); | ||||
138 | windows->line_starts = NULL((void *)0); | ||||
139 | windows->line_count = 0; | ||||
140 | break; | ||||
141 | } | ||||
142 | |||||
143 | /* If we have some temporary windows, delete one of them. */ | ||||
144 | for (win = windows; win; win = win->next) | ||||
145 | if (win->flags & W_TempWindow0x40) | ||||
| |||||
146 | break; | ||||
147 | |||||
148 | /* Otherwise, delete the first window, and try again. */ | ||||
149 | if (!win
| ||||
150 | win = windows; | ||||
151 | |||||
152 | if (window_deletion_notifier) | ||||
153 | (*window_deletion_notifier) (win); | ||||
154 | |||||
155 | window_delete_window (win); | ||||
156 | numwins--; | ||||
157 | } | ||||
158 | |||||
159 | /* The screen has changed height and width. */ | ||||
160 | delta_height = height - the_screen->height; /* This is how much. */ | ||||
161 | the_screen->height = height; /* This is the new height. */ | ||||
162 | the_screen->width = width; /* This is the new width. */ | ||||
163 | |||||
164 | /* Set the start of the echo area. */ | ||||
165 | the_echo_area->first_row = height - the_echo_area->height; | ||||
166 | the_echo_area->width = width; | ||||
167 | |||||
168 | /* Check to see if the screen can really be changed this way. */ | ||||
169 | if ((!windows->next) && ((windows->height == 0) && (delta_height < 0))) | ||||
170 | return; | ||||
171 | |||||
172 | /* Divide the change in height among the available windows. */ | ||||
173 | delta_each = delta_height / numwins; | ||||
174 | delta_leftover = delta_height - (delta_each * numwins); | ||||
175 | |||||
176 | /* Change the height of each window in the chain by delta_each. Change | ||||
177 | the height of the last window in the chain by delta_each and by the | ||||
178 | leftover amount of change. Change the width of each window to be | ||||
179 | WIDTH. */ | ||||
180 | for (win = windows; win; win = win->next) | ||||
181 | { | ||||
182 | if ((win->width != width) && ((win->flags & W_InhibitMode0x08) == 0)) | ||||
183 | { | ||||
184 | win->width = width; | ||||
185 | maybe_free (win->modeline)do { if (win->modeline) free (win->modeline); } while ( 0); | ||||
186 | win->modeline = xmalloc (1 + width); | ||||
187 | } | ||||
188 | |||||
189 | win->height += delta_each; | ||||
190 | |||||
191 | /* If the previous height of this window was zero, it was the only | ||||
192 | window, and it was not visible. Thus we need to compensate for | ||||
193 | the echo_area. */ | ||||
194 | if (win->height == delta_each) | ||||
195 | win->height -= (1 + the_echo_area->height); | ||||
196 | |||||
197 | /* If this is not the first window in the chain, then change the | ||||
198 | first row of it. We cannot just add delta_each to the first row, | ||||
199 | since this window's first row is the sum of the collective increases | ||||
200 | that have gone before it. So we just add one to the location of the | ||||
201 | previous window's modeline. */ | ||||
202 | if (win->prev) | ||||
203 | win->first_row = (win->prev->first_row + win->prev->height) + 1; | ||||
204 | |||||
205 | /* The last window in the chain gets the extra space (or shrinkage). */ | ||||
206 | if (!win->next) | ||||
207 | win->height += delta_leftover; | ||||
208 | |||||
209 | if (win->node) | ||||
210 | recalculate_line_starts (win); | ||||
211 | |||||
212 | win->flags |= W_UpdateWindow0x01; | ||||
213 | } | ||||
214 | |||||
215 | /* If the screen got smaller, check over the windows just shrunk to | ||||
216 | keep them within bounds. Some of the windows may have gotten smaller | ||||
217 | than WINDOW_MIN_HEIGHT in which case some of the other windows are | ||||
218 | larger than the available display space in the screen. Because of our | ||||
219 | intial test above, we know that there is enough space for all of the | ||||
220 | windows. */ | ||||
221 | if ((delta_each < 0) && ((windows->height != 0) && windows->next)) | ||||
222 | { | ||||
223 | int avail; | ||||
224 | |||||
225 | avail = the_screen->height - (numwins + the_echo_area->height); | ||||
226 | win = windows; | ||||
227 | |||||
228 | while (win) | ||||
229 | { | ||||
230 | if ((win->height < WINDOW_MIN_HEIGHT2) || | ||||
231 | (win->height > avail)) | ||||
232 | { | ||||
233 | WINDOW *lastwin = NULL((void *)0); | ||||
234 | |||||
235 | /* Split the space among the available windows. */ | ||||
236 | delta_each = avail / numwins; | ||||
237 | delta_leftover = avail - (delta_each * numwins); | ||||
238 | |||||
239 | for (win = windows; win; win = win->next) | ||||
240 | { | ||||
241 | lastwin = win; | ||||
242 | if (win->prev) | ||||
243 | win->first_row = | ||||
244 | (win->prev->first_row + win->prev->height) + 1; | ||||
245 | win->height = delta_each; | ||||
246 | } | ||||
247 | |||||
248 | /* Give the leftover space (if any) to the last window. */ | ||||
249 | lastwin->height += delta_leftover; | ||||
250 | break; | ||||
251 | } | ||||
252 | else | ||||
253 | win= win->next; | ||||
254 | } | ||||
255 | } | ||||
256 | } | ||||
257 | |||||
258 | /* Make a new window showing NODE, and return that window structure. | ||||
259 | If NODE is passed as NULL, then show the node showing in the active | ||||
260 | window. If the window could not be made return a NULL pointer. The | ||||
261 | active window is not changed.*/ | ||||
262 | WINDOW * | ||||
263 | window_make_window (NODE *node) | ||||
264 | { | ||||
265 | WINDOW *window; | ||||
266 | |||||
267 | if (!node) | ||||
268 | node = active_window->node; | ||||
269 | |||||
270 | /* If there isn't enough room to make another window, return now. */ | ||||
271 | if ((active_window->height / 2) < WINDOW_MIN_SIZE(2 + 1)) | ||||
272 | return (NULL((void *)0)); | ||||
273 | |||||
274 | /* Make and initialize the new window. | ||||
275 | The fudging about with -1 and +1 is because the following window in the | ||||
276 | chain cannot start at window->height, since that is where the modeline | ||||
277 | for the previous window is displayed. The inverse adjustment is made | ||||
278 | in window_delete_window (). */ | ||||
279 | window = xmalloc (sizeof (WINDOW)); | ||||
280 | window->width = the_screen->width; | ||||
281 | window->height = (active_window->height / 2) - 1; | ||||
282 | #if defined (SPLIT_BEFORE_ACTIVE) | ||||
283 | window->first_row = active_window->first_row; | ||||
284 | #else | ||||
285 | window->first_row = active_window->first_row + | ||||
286 | (active_window->height - window->height); | ||||
287 | #endif | ||||
288 | window->keymap = info_keymap; | ||||
289 | window->goal_column = -1; | ||||
290 | window->modeline = xmalloc (1 + window->width); | ||||
291 | window->line_starts = NULL((void *)0); | ||||
292 | window->flags = W_UpdateWindow0x01 | W_WindowVisible0x04; | ||||
293 | window_set_node_of_window (window, node); | ||||
294 | |||||
295 | /* Adjust the height of the old active window. */ | ||||
296 | active_window->height -= (window->height + 1); | ||||
297 | #if defined (SPLIT_BEFORE_ACTIVE) | ||||
298 | active_window->first_row += (window->height + 1); | ||||
299 | #endif | ||||
300 | active_window->flags |= W_UpdateWindow0x01; | ||||
301 | |||||
302 | /* Readjust the new and old windows so that their modelines and contents | ||||
303 | will be displayed correctly. */ | ||||
304 | #if defined (NOTDEF) | ||||
305 | /* We don't have to do this for WINDOW since window_set_node_of_window () | ||||
306 | already did. */ | ||||
307 | window_adjust_pagetop (window); | ||||
308 | window_make_modeline (window); | ||||
309 | #endif /* NOTDEF */ | ||||
310 | |||||
311 | /* We do have to readjust the existing active window. */ | ||||
312 | window_adjust_pagetop (active_window); | ||||
313 | window_make_modeline (active_window); | ||||
314 | |||||
315 | #if defined (SPLIT_BEFORE_ACTIVE) | ||||
316 | /* This window is just before the active one. The active window gets | ||||
317 | bumped down one. The active window is not changed. */ | ||||
318 | window->next = active_window; | ||||
319 | |||||
320 | window->prev = active_window->prev; | ||||
321 | active_window->prev = window; | ||||
322 | |||||
323 | if (window->prev) | ||||
324 | window->prev->next = window; | ||||
325 | else | ||||
326 | windows = window; | ||||
327 | #else | ||||
328 | /* This window is just after the active one. Which window is active is | ||||
329 | not changed. */ | ||||
330 | window->prev = active_window; | ||||
331 | window->next = active_window->next; | ||||
332 | active_window->next = window; | ||||
333 | if (window->next) | ||||
334 | window->next->prev = window; | ||||
335 | #endif /* !SPLIT_BEFORE_ACTIVE */ | ||||
336 | return (window); | ||||
337 | } | ||||
338 | |||||
339 | /* These useful macros make it possible to read the code in | ||||
340 | window_change_window_height (). */ | ||||
341 | #define grow_me_shrinking_next(me, next, diff)do { me->height += diff; next->height -= diff; next-> first_row += diff; window_adjust_pagetop (next); } while (0) \ | ||||
342 | do { \ | ||||
343 | me->height += diff; \ | ||||
344 | next->height -= diff; \ | ||||
345 | next->first_row += diff; \ | ||||
346 | window_adjust_pagetop (next); \ | ||||
347 | } while (0) | ||||
348 | |||||
349 | #define grow_me_shrinking_prev(me, prev, diff)do { me->height += diff; prev->height -= diff; me->first_row -=diff; window_adjust_pagetop (prev); } while (0) \ | ||||
350 | do { \ | ||||
351 | me->height += diff; \ | ||||
352 | prev->height -= diff; \ | ||||
353 | me->first_row -=diff; \ | ||||
354 | window_adjust_pagetop (prev); \ | ||||
355 | } while (0) | ||||
356 | |||||
357 | #define shrink_me_growing_next(me, next, diff)do { me->height -= diff; next->height += diff; next-> first_row -= diff; window_adjust_pagetop (next); } while (0) \ | ||||
358 | do { \ | ||||
359 | me->height -= diff; \ | ||||
360 | next->height += diff; \ | ||||
361 | next->first_row -= diff; \ | ||||
362 | window_adjust_pagetop (next); \ | ||||
363 | } while (0) | ||||
364 | |||||
365 | #define shrink_me_growing_prev(me, prev, diff)do { me->height -= diff; prev->height += diff; me->first_row += diff; window_adjust_pagetop (prev); } while (0) \ | ||||
366 | do { \ | ||||
367 | me->height -= diff; \ | ||||
368 | prev->height += diff; \ | ||||
369 | me->first_row += diff; \ | ||||
370 | window_adjust_pagetop (prev); \ | ||||
371 | } while (0) | ||||
372 | |||||
373 | /* Change the height of WINDOW by AMOUNT. This also automagically adjusts | ||||
374 | the previous and next windows in the chain. If there is only one user | ||||
375 | window, then no change takes place. */ | ||||
376 | void | ||||
377 | window_change_window_height (WINDOW *window, int amount) | ||||
378 | { | ||||
379 | register WINDOW *win, *prev, *next; | ||||
380 | |||||
381 | /* If there is only one window, or if the amount of change is zero, | ||||
382 | return immediately. */ | ||||
383 | if (!windows->next || amount == 0) | ||||
384 | return; | ||||
385 | |||||
386 | /* Find this window in our chain. */ | ||||
387 | for (win = windows; win; win = win->next) | ||||
388 | if (win == window) | ||||
389 | break; | ||||
390 | |||||
391 | /* If the window is isolated (i.e., doesn't appear in our window list, | ||||
392 | then quit now. */ | ||||
393 | if (!win) | ||||
394 | return; | ||||
395 | |||||
396 | /* Change the height of this window by AMOUNT, if that is possible. | ||||
397 | It can be impossible if there isn't enough available room on the | ||||
398 | screen, or if the resultant window would be too small. */ | ||||
399 | |||||
400 | prev = window->prev; | ||||
401 | next = window->next; | ||||
402 | |||||
403 | /* WINDOW decreasing in size? */ | ||||
404 | if (amount < 0) | ||||
405 | { | ||||
406 | int abs_amount = -amount; /* It is easier to deal with this way. */ | ||||
407 | |||||
408 | /* If the resultant window would be too small, stop here. */ | ||||
409 | if ((window->height - abs_amount) < WINDOW_MIN_HEIGHT2) | ||||
410 | return; | ||||
411 | |||||
412 | /* If we have two neighboring windows, choose the smaller one to get | ||||
413 | larger. */ | ||||
414 | if (next && prev) | ||||
415 | { | ||||
416 | if (prev->height < next->height) | ||||
417 | shrink_me_growing_prev (window, prev, abs_amount)do { window->height -= abs_amount; prev->height += abs_amount ; window->first_row += abs_amount; window_adjust_pagetop ( prev); } while (0); | ||||
418 | else | ||||
419 | shrink_me_growing_next (window, next, abs_amount)do { window->height -= abs_amount; next->height += abs_amount ; next->first_row -= abs_amount; window_adjust_pagetop (next ); } while (0); | ||||
420 | } | ||||
421 | else if (next) | ||||
422 | shrink_me_growing_next (window, next, abs_amount)do { window->height -= abs_amount; next->height += abs_amount ; next->first_row -= abs_amount; window_adjust_pagetop (next ); } while (0); | ||||
423 | else | ||||
424 | shrink_me_growing_prev (window, prev, abs_amount)do { window->height -= abs_amount; prev->height += abs_amount ; window->first_row += abs_amount; window_adjust_pagetop ( prev); } while (0); | ||||
425 | } | ||||
426 | |||||
427 | /* WINDOW increasing in size? */ | ||||
428 | if (amount > 0) | ||||
429 | { | ||||
430 | int total_avail, next_avail = 0, prev_avail = 0; | ||||
431 | |||||
432 | if (next) | ||||
433 | next_avail = next->height - WINDOW_MIN_SIZE(2 + 1); | ||||
434 | |||||
435 | if (prev) | ||||
436 | prev_avail = prev->height - WINDOW_MIN_SIZE(2 + 1); | ||||
437 | |||||
438 | total_avail = next_avail + prev_avail; | ||||
439 | |||||
440 | /* If there isn't enough space available to grow this window, give up. */ | ||||
441 | if (amount > total_avail) | ||||
442 | return; | ||||
443 | |||||
444 | /* If there aren't two neighboring windows, or if one of the neighbors | ||||
445 | is larger than the other one by at least AMOUNT, grow that one. */ | ||||
446 | if ((next && !prev) || ((next_avail - amount) >= prev_avail)) | ||||
447 | grow_me_shrinking_next (window, next, amount)do { window->height += amount; next->height -= amount; next ->first_row += amount; window_adjust_pagetop (next); } while (0); | ||||
448 | else if ((prev && !next) || ((prev_avail - amount) >= next_avail)) | ||||
449 | grow_me_shrinking_prev (window, prev, amount)do { window->height += amount; prev->height -= amount; window ->first_row -=amount; window_adjust_pagetop (prev); } while (0); | ||||
450 | else | ||||
451 | { | ||||
452 | int change; | ||||
453 | |||||
454 | /* This window has two neighbors. They both must be shrunk in to | ||||
455 | make enough space for WINDOW to grow. Make them both the same | ||||
456 | size. */ | ||||
457 | if (prev_avail > next_avail) | ||||
458 | { | ||||
459 | change = prev_avail - next_avail; | ||||
460 | grow_me_shrinking_prev (window, prev, change)do { window->height += change; prev->height -= change; window ->first_row -=change; window_adjust_pagetop (prev); } while (0); | ||||
461 | amount -= change; | ||||
462 | } | ||||
463 | else | ||||
464 | { | ||||
465 | change = next_avail - prev_avail; | ||||
466 | grow_me_shrinking_next (window, next, change)do { window->height += change; next->height -= change; next ->first_row += change; window_adjust_pagetop (next); } while (0); | ||||
467 | amount -= change; | ||||
468 | } | ||||
469 | |||||
470 | /* Both neighbors are the same size. Split the difference in | ||||
471 | AMOUNT between them. */ | ||||
472 | while (amount) | ||||
473 | { | ||||
474 | window->height++; | ||||
475 | amount--; | ||||
476 | |||||
477 | /* Odd numbers grow next, even grow prev. */ | ||||
478 | if (amount & 1) | ||||
479 | { | ||||
480 | prev->height--; | ||||
481 | window->first_row--; | ||||
482 | } | ||||
483 | else | ||||
484 | { | ||||
485 | next->height--; | ||||
486 | next->first_row++; | ||||
487 | } | ||||
488 | } | ||||
489 | window_adjust_pagetop (prev); | ||||
490 | window_adjust_pagetop (next); | ||||
491 | } | ||||
492 | } | ||||
493 | if (prev) | ||||
494 | prev->flags |= W_UpdateWindow0x01; | ||||
495 | |||||
496 | if (next) | ||||
497 | next->flags |= W_UpdateWindow0x01; | ||||
498 | |||||
499 | window->flags |= W_UpdateWindow0x01; | ||||
500 | window_adjust_pagetop (window); | ||||
501 | } | ||||
502 | |||||
503 | /* Tile all of the windows currently displayed in the global variable | ||||
504 | WINDOWS. If argument STYLE is TILE_INTERNALS, tile windows displaying | ||||
505 | internal nodes as well, otherwise do not change the height of such | ||||
506 | windows. */ | ||||
507 | void | ||||
508 | window_tile_windows (int style) | ||||
509 | { | ||||
510 | WINDOW *win, *last_adjusted; | ||||
511 | int numwins, avail, per_win_height, leftover; | ||||
512 | int do_internals; | ||||
513 | |||||
514 | numwins = avail = 0; | ||||
515 | do_internals = (style == TILE_INTERNALS1); | ||||
516 | |||||
517 | for (win = windows; win; win = win->next) | ||||
518 | if (do_internals || !win->node || | ||||
519 | (win->node->flags & N_IsInternal0x10) == 0) | ||||
520 | { | ||||
521 | avail += win->height; | ||||
522 | numwins++; | ||||
523 | } | ||||
524 | |||||
525 | if (numwins <= 1 || !the_screen->height) | ||||
526 | return; | ||||
527 | |||||
528 | /* Find the size for each window. Divide the size of the usable portion | ||||
529 | of the screen by the number of windows. */ | ||||
530 | per_win_height = avail / numwins; | ||||
531 | leftover = avail - (per_win_height * numwins); | ||||
532 | |||||
533 | last_adjusted = NULL((void *)0); | ||||
534 | for (win = windows; win; win = win->next) | ||||
535 | { | ||||
536 | if (do_internals || !win->node || | ||||
537 | (win->node->flags & N_IsInternal0x10) == 0) | ||||
538 | { | ||||
539 | last_adjusted = win; | ||||
540 | win->height = per_win_height; | ||||
541 | } | ||||
542 | } | ||||
543 | |||||
544 | if (last_adjusted) | ||||
545 | last_adjusted->height += leftover; | ||||
546 | |||||
547 | /* Readjust the first_row of every window in the chain. */ | ||||
548 | for (win = windows; win; win = win->next) | ||||
549 | { | ||||
550 | if (win->prev) | ||||
551 | win->first_row = win->prev->first_row + win->prev->height + 1; | ||||
552 | |||||
553 | window_adjust_pagetop (win); | ||||
554 | win->flags |= W_UpdateWindow0x01; | ||||
555 | } | ||||
556 | } | ||||
557 | |||||
558 | /* Toggle the state of line wrapping in WINDOW. This can do a bit of fancy | ||||
559 | redisplay. */ | ||||
560 | void | ||||
561 | window_toggle_wrap (WINDOW *window) | ||||
562 | { | ||||
563 | if (window->flags & W_NoWrap0x10) | ||||
564 | window->flags &= ~W_NoWrap0x10; | ||||
565 | else | ||||
566 | window->flags |= W_NoWrap0x10; | ||||
567 | |||||
568 | if (window != the_echo_area) | ||||
569 | { | ||||
570 | char **old_starts; | ||||
571 | int old_lines, old_pagetop; | ||||
572 | |||||
573 | old_starts = window->line_starts; | ||||
574 | old_lines = window->line_count; | ||||
575 | old_pagetop = window->pagetop; | ||||
576 | |||||
577 | calculate_line_starts (window); | ||||
578 | |||||
579 | /* Make sure that point appears within this window. */ | ||||
580 | window_adjust_pagetop (window); | ||||
581 | |||||
582 | /* If the pagetop hasn't changed maybe we can do some scrolling now | ||||
583 | to speed up the display. Many of the line starts will be the same, | ||||
584 | so scrolling here is a very good optimization.*/ | ||||
585 | if (old_pagetop == window->pagetop) | ||||
586 | display_scroll_line_starts | ||||
587 | (window, old_pagetop, old_starts, old_lines); | ||||
588 | maybe_free (old_starts)do { if (old_starts) free (old_starts); } while (0); | ||||
589 | } | ||||
590 | window->flags |= W_UpdateWindow0x01; | ||||
591 | } | ||||
592 | |||||
593 | /* Set WINDOW to display NODE. */ | ||||
594 | void | ||||
595 | window_set_node_of_window (WINDOW *window, NODE *node) | ||||
596 | { | ||||
597 | window->node = node; | ||||
598 | window->pagetop = 0; | ||||
599 | window->point = 0; | ||||
600 | recalculate_line_starts (window); | ||||
601 | window->flags |= W_UpdateWindow0x01; | ||||
602 | /* The display_pos member is nonzero if we're displaying an anchor. */ | ||||
603 | window->point = node ? node->display_pos : 0; | ||||
604 | window_adjust_pagetop (window); | ||||
605 | window_make_modeline (window); | ||||
606 | } | ||||
607 | |||||
608 | /* Delete WINDOW from the list of known windows. If this window was the | ||||
609 | active window, make the next window in the chain be the active window. | ||||
610 | If the active window is the next or previous window, choose that window | ||||
611 | as the recipient of the extra space. Otherwise, prefer the next window. */ | ||||
612 | void | ||||
613 | window_delete_window (WINDOW *window) | ||||
614 | { | ||||
615 | WINDOW *next, *prev, *window_to_fix; | ||||
616 | |||||
617 | next = window->next; | ||||
618 | prev = window->prev; | ||||
619 | |||||
620 | /* You cannot delete the only window or a permanent window. */ | ||||
621 | if ((!next
| ||||
622 | return; | ||||
623 | |||||
624 | if (next
| ||||
625 | next->prev = prev; | ||||
626 | |||||
627 | if (!prev
| ||||
628 | windows = next; | ||||
629 | else | ||||
630 | prev->next = next; | ||||
631 | |||||
632 | if (window->line_starts) | ||||
633 | free (window->line_starts); | ||||
634 | |||||
635 | if (window->modeline) | ||||
636 | free (window->modeline); | ||||
637 | |||||
638 | if (window == active_window) | ||||
639 | { | ||||
640 | /* If there isn't a next window, then there must be a previous one, | ||||
641 | since we cannot delete the last window. If there is a next window, | ||||
642 | prefer to use that as the active window. */ | ||||
643 | if (next) | ||||
644 | active_window = next; | ||||
645 | else | ||||
646 | active_window = prev; | ||||
647 | } | ||||
648 | |||||
649 | if (next
| ||||
650 | window_to_fix = next; | ||||
651 | else if (prev
| ||||
652 | window_to_fix = prev; | ||||
653 | else if (next) | ||||
654 | window_to_fix = next; | ||||
655 | else if (prev) | ||||
656 | window_to_fix = prev; | ||||
657 | else | ||||
658 | window_to_fix = windows; | ||||
659 | |||||
660 | if (window_to_fix->first_row > window->first_row) | ||||
661 | { | ||||
662 | int diff; | ||||
663 | |||||
664 | /* Try to adjust the visible part of the node so that as little | ||||
665 | text as possible has to move. */ | ||||
666 | diff = window_to_fix->first_row - window->first_row; | ||||
667 | window_to_fix->first_row = window->first_row; | ||||
668 | |||||
669 | window_to_fix->pagetop -= diff; | ||||
670 | if (window_to_fix->pagetop < 0) | ||||
671 | window_to_fix->pagetop = 0; | ||||
672 | } | ||||
673 | |||||
674 | /* The `+ 1' is to offset the difference between the first_row locations. | ||||
675 | See the code in window_make_window (). */ | ||||
676 | window_to_fix->height += window->height + 1; | ||||
677 | window_to_fix->flags |= W_UpdateWindow0x01; | ||||
678 | |||||
679 | free (window); | ||||
680 | } | ||||
681 | |||||
682 | /* For every window in CHAIN, set the flags member to have FLAG set. */ | ||||
683 | void | ||||
684 | window_mark_chain (WINDOW *chain, int flag) | ||||
685 | { | ||||
686 | register WINDOW *win; | ||||
687 | |||||
688 | for (win = chain; win; win = win->next) | ||||
689 | win->flags |= flag; | ||||
690 | } | ||||
691 | |||||
692 | /* For every window in CHAIN, clear the flags member of FLAG. */ | ||||
693 | void | ||||
694 | window_unmark_chain (WINDOW *chain, int flag) | ||||
695 | { | ||||
696 | register WINDOW *win; | ||||
697 | |||||
698 | for (win = chain; win; win = win->next) | ||||
699 | win->flags &= ~flag; | ||||
700 | } | ||||
701 | |||||
702 | /* Return the number of characters it takes to display CHARACTER on the | ||||
703 | screen at HPOS. */ | ||||
704 | int | ||||
705 | character_width (int character, int hpos) | ||||
706 | { | ||||
707 | int printable_limit = 127; | ||||
708 | int width = 1; | ||||
709 | |||||
710 | if (ISO_Latin_p) | ||||
711 | printable_limit = 255; | ||||
712 | |||||
713 | if (character > printable_limit) | ||||
714 | width = 3; | ||||
715 | else if (iscntrl (character)) | ||||
716 | { | ||||
717 | switch (character) | ||||
718 | { | ||||
719 | case '\r': | ||||
720 | case '\n': | ||||
721 | width = the_screen->width - hpos; | ||||
722 | break; | ||||
723 | case '\t': | ||||
724 | width = ((hpos + 8) & 0xf8) - hpos; | ||||
725 | break; | ||||
726 | default: | ||||
727 | width = 2; | ||||
728 | } | ||||
729 | } | ||||
730 | else if (character == DEL'\177') | ||||
731 | width = 2; | ||||
732 | |||||
733 | return (width); | ||||
734 | } | ||||
735 | |||||
736 | /* Return the number of characters it takes to display STRING on the screen | ||||
737 | at HPOS. */ | ||||
738 | int | ||||
739 | string_width (char *string, int hpos) | ||||
740 | { | ||||
741 | register int i, width, this_char_width; | ||||
742 | |||||
743 | for (width = 0, i = 0; string[i]; i++) | ||||
744 | { | ||||
745 | /* Support ANSI escape sequences for -R. */ | ||||
746 | if (raw_escapes_p | ||||
747 | && string[i] == '\033' | ||||
748 | && string[i+1] == '[' | ||||
749 | && isdigit (string[i+2]) | ||||
750 | && (string[i+3] == 'm' | ||||
751 | || (isdigit (string[i+3]) && string[i+4] == 'm'))) | ||||
752 | { | ||||
753 | while (string[i] != 'm') | ||||
754 | i++; | ||||
755 | this_char_width = 0; | ||||
756 | } | ||||
757 | else | ||||
758 | this_char_width = character_width (string[i], hpos); | ||||
759 | width += this_char_width; | ||||
760 | hpos += this_char_width; | ||||
761 | } | ||||
762 | return (width); | ||||
763 | } | ||||
764 | |||||
765 | /* Quickly guess the approximate number of lines that NODE would | ||||
766 | take to display. This really only counts carriage returns. */ | ||||
767 | int | ||||
768 | window_physical_lines (NODE *node) | ||||
769 | { | ||||
770 | register int i, lines; | ||||
771 | char *contents; | ||||
772 | |||||
773 | if (!node) | ||||
774 | return (0); | ||||
775 | |||||
776 | contents = node->contents; | ||||
777 | for (i = 0, lines = 1; i < node->nodelen; i++) | ||||
778 | if (contents[i] == '\n') | ||||
779 | lines++; | ||||
780 | |||||
781 | return (lines); | ||||
782 | } | ||||
783 | |||||
784 | /* Calculate a list of line starts for the node belonging to WINDOW. The line | ||||
785 | starts are pointers to the actual text within WINDOW->NODE. */ | ||||
786 | void | ||||
787 | calculate_line_starts (WINDOW *window) | ||||
788 | { | ||||
789 | register int i, hpos; | ||||
790 | char **line_starts = NULL((void *)0); | ||||
791 | int line_starts_index = 0, line_starts_slots = 0; | ||||
792 | int bump_index; | ||||
793 | NODE *node; | ||||
794 | |||||
795 | window->line_starts = NULL((void *)0); | ||||
796 | window->line_count = 0; | ||||
797 | node = window->node; | ||||
798 | |||||
799 | if (!node) | ||||
800 | return; | ||||
801 | |||||
802 | /* Grovel the node starting at the top, and for each line calculate the | ||||
803 | width of the characters appearing in that line. Add each line start | ||||
804 | to our array. */ | ||||
805 | i = 0; | ||||
806 | hpos = 0; | ||||
807 | bump_index = 0; | ||||
808 | |||||
809 | while (i < node->nodelen) | ||||
810 | { | ||||
811 | char *line = node->contents + i; | ||||
812 | unsigned int cwidth, c; | ||||
813 | |||||
814 | add_pointer_to_array (line, line_starts_index, line_starts,do { if (line_starts_index + 2 >= line_starts_slots) line_starts = (char * *)(xrealloc (line_starts, (line_starts_slots += 100 ) * sizeof (char *))); line_starts[line_starts_index++] = (char *)line; line_starts[line_starts_index] = (char *)((void *)0) ; } while (0) | ||||
815 | line_starts_slots, 100, char *)do { if (line_starts_index + 2 >= line_starts_slots) line_starts = (char * *)(xrealloc (line_starts, (line_starts_slots += 100 ) * sizeof (char *))); line_starts[line_starts_index++] = (char *)line; line_starts[line_starts_index] = (char *)((void *)0) ; } while (0); | ||||
816 | if (bump_index) | ||||
817 | { | ||||
818 | i++; | ||||
819 | bump_index = 0; | ||||
820 | } | ||||
821 | |||||
822 | while (1) | ||||
823 | { | ||||
824 | /* The cast to unsigned char is for 8-bit characters, which | ||||
825 | could be passed as negative integers to character_width | ||||
826 | and wreak havoc on some naive implementations of iscntrl. */ | ||||
827 | c = (unsigned char) node->contents[i]; | ||||
828 | |||||
829 | /* Support ANSI escape sequences for -R. */ | ||||
830 | if (raw_escapes_p | ||||
831 | && c == '\033' | ||||
832 | && node->contents[i+1] == '[' | ||||
833 | && isdigit (node->contents[i+2])) | ||||
834 | { | ||||
835 | if (node->contents[i+3] == 'm') | ||||
836 | { | ||||
837 | i += 3; | ||||
838 | cwidth = 0; | ||||
839 | } | ||||
840 | else if (isdigit (node->contents[i+3]) | ||||
841 | && node->contents[i+4] == 'm') | ||||
842 | { | ||||
843 | i += 4; | ||||
844 | cwidth = 0; | ||||
845 | } | ||||
846 | else | ||||
847 | cwidth = character_width (c, hpos); | ||||
848 | } | ||||
849 | else | ||||
850 | cwidth = character_width (c, hpos); | ||||
851 | |||||
852 | /* If this character fits within this line, just do the next one. */ | ||||
853 | if ((hpos + cwidth) < (unsigned int) window->width) | ||||
854 | { | ||||
855 | i++; | ||||
856 | hpos += cwidth; | ||||
857 | continue; | ||||
858 | } | ||||
859 | else | ||||
860 | { | ||||
861 | /* If this character would position the cursor at the start of | ||||
862 | the next printed screen line, then do the next line. */ | ||||
863 | if (c == '\n' || c == '\r' || c == '\t') | ||||
864 | { | ||||
865 | i++; | ||||
866 | hpos = 0; | ||||
867 | break; | ||||
868 | } | ||||
869 | else | ||||
870 | { | ||||
871 | /* This character passes the window width border. Postion | ||||
872 | the cursor after the printed character, but remember this | ||||
873 | line start as where this character is. A bit tricky. */ | ||||
874 | |||||
875 | /* If this window doesn't wrap lines, proceed to the next | ||||
876 | physical line here. */ | ||||
877 | if (window->flags & W_NoWrap0x10) | ||||
878 | { | ||||
879 | hpos = 0; | ||||
880 | while (i < node->nodelen && node->contents[i] != '\n') | ||||
881 | i++; | ||||
882 | |||||
883 | if (node->contents[i] == '\n') | ||||
884 | i++; | ||||
885 | } | ||||
886 | else | ||||
887 | { | ||||
888 | hpos = the_screen->width - hpos; | ||||
889 | bump_index++; | ||||
890 | } | ||||
891 | break; | ||||
892 | } | ||||
893 | } | ||||
894 | } | ||||
895 | } | ||||
896 | window->line_starts = line_starts; | ||||
897 | window->line_count = line_starts_index; | ||||
898 | } | ||||
899 | |||||
900 | /* Given WINDOW, recalculate the line starts for the node it displays. */ | ||||
901 | void | ||||
902 | recalculate_line_starts (WINDOW *window) | ||||
903 | { | ||||
904 | maybe_free (window->line_starts)do { if (window->line_starts) free (window->line_starts ); } while (0); | ||||
905 | calculate_line_starts (window); | ||||
906 | } | ||||
907 | |||||
908 | /* Global variable control redisplay of scrolled windows. If non-zero, it | ||||
909 | is the desired number of lines to scroll the window in order to make | ||||
910 | point visible. A user might set this to 1 for smooth scrolling. If | ||||
911 | set to zero, the line containing point is centered within the window. */ | ||||
912 | int window_scroll_step = 0; | ||||
913 | |||||
914 | /* Adjust the pagetop of WINDOW such that the cursor point will be visible. */ | ||||
915 | void | ||||
916 | window_adjust_pagetop (WINDOW *window) | ||||
917 | { | ||||
918 | register int line = 0; | ||||
919 | char *contents; | ||||
920 | |||||
921 | if (!window->node) | ||||
922 | return; | ||||
923 | |||||
924 | contents = window->node->contents; | ||||
925 | |||||
926 | /* Find the first printed line start which is after WINDOW->point. */ | ||||
927 | for (line = 0; line < window->line_count; line++) | ||||
928 | { | ||||
929 | char *line_start; | ||||
930 | |||||
931 | line_start = window->line_starts[line]; | ||||
932 | |||||
933 | if ((line_start - contents) > window->point) | ||||
934 | break; | ||||
935 | } | ||||
936 | |||||
937 | /* The line index preceding the line start which is past point is the | ||||
938 | one containing point. */ | ||||
939 | line--; | ||||
940 | |||||
941 | /* If this line appears in the current displayable page, do nothing. | ||||
942 | Otherwise, adjust the top of the page to make this line visible. */ | ||||
943 | if ((line < window->pagetop) || | ||||
944 | (line - window->pagetop > (window->height - 1))) | ||||
945 | { | ||||
946 | /* The user-settable variable "scroll-step" is used to attempt | ||||
947 | to make point visible, iff it is non-zero. If that variable | ||||
948 | is zero, then the line containing point is centered within | ||||
949 | the window. */ | ||||
950 | if (window_scroll_step < window->height) | ||||
951 | { | ||||
952 | if ((line < window->pagetop) && | ||||
953 | ((window->pagetop - window_scroll_step) <= line)) | ||||
954 | window->pagetop -= window_scroll_step; | ||||
955 | else if ((line - window->pagetop > (window->height - 1)) && | ||||
956 | ((line - (window->pagetop + window_scroll_step) | ||||
957 | < window->height))) | ||||
958 | window->pagetop += window_scroll_step; | ||||
959 | else | ||||
960 | window->pagetop = line - ((window->height - 1) / 2); | ||||
961 | } | ||||
962 | else | ||||
963 | window->pagetop = line - ((window->height - 1) / 2); | ||||
964 | |||||
965 | if (window->pagetop < 0) | ||||
966 | window->pagetop = 0; | ||||
967 | window->flags |= W_UpdateWindow0x01; | ||||
968 | } | ||||
969 | } | ||||
970 | |||||
971 | /* Return the index of the line containing point. */ | ||||
972 | int | ||||
973 | window_line_of_point (WINDOW *window) | ||||
974 | { | ||||
975 | register int i, start = 0; | ||||
976 | |||||
977 | /* Try to optimize. Check to see if point is past the pagetop for | ||||
978 | this window, and if so, start searching forward from there. */ | ||||
979 | if ((window->pagetop > -1 && window->pagetop < window->line_count) && | ||||
980 | (window->line_starts[window->pagetop] - window->node->contents) | ||||
981 | <= window->point) | ||||
982 | start = window->pagetop; | ||||
983 | |||||
984 | for (i = start; i < window->line_count; i++) | ||||
985 | { | ||||
986 | if ((window->line_starts[i] - window->node->contents) > window->point) | ||||
987 | break; | ||||
988 | } | ||||
989 | |||||
990 | return (i - 1); | ||||
991 | } | ||||
992 | |||||
993 | /* Get and return the goal column for this window. */ | ||||
994 | int | ||||
995 | window_get_goal_column (WINDOW *window) | ||||
996 | { | ||||
997 | if (!window->node) | ||||
998 | return (-1); | ||||
999 | |||||
1000 | if (window->goal_column != -1) | ||||
1001 | return (window->goal_column); | ||||
1002 | |||||
1003 | /* Okay, do the work. Find the printed offset of the cursor | ||||
1004 | in this window. */ | ||||
1005 | return (window_get_cursor_column (window)); | ||||
1006 | } | ||||
1007 | |||||
1008 | /* Get and return the printed column offset of the cursor in this window. */ | ||||
1009 | int | ||||
1010 | window_get_cursor_column (WINDOW *window) | ||||
1011 | { | ||||
1012 | int i, hpos, end; | ||||
1013 | char *line; | ||||
1014 | |||||
1015 | i = window_line_of_point (window); | ||||
1016 | |||||
1017 | if (i < 0) | ||||
1018 | return (-1); | ||||
1019 | |||||
1020 | line = window->line_starts[i]; | ||||
1021 | end = window->point - (line - window->node->contents); | ||||
1022 | |||||
1023 | for (hpos = 0, i = 0; i < end; i++) | ||||
1024 | { | ||||
1025 | /* Support ANSI escape sequences for -R. */ | ||||
1026 | if (raw_escapes_p | ||||
1027 | && line[i] == '\033' | ||||
1028 | && line[i+1] == '[' | ||||
1029 | && isdigit (line[i+2])) | ||||
1030 | { | ||||
1031 | if (line[i+3] == 'm') | ||||
1032 | i += 3; | ||||
1033 | else if (isdigit (line[i+3]) && line[i+4] == 'm') | ||||
1034 | i += 4; | ||||
1035 | else | ||||
1036 | hpos += character_width (line[i], hpos); | ||||
1037 | } | ||||
1038 | else | ||||
1039 | hpos += character_width (line[i], hpos); | ||||
1040 | } | ||||
1041 | |||||
1042 | return (hpos); | ||||
1043 | } | ||||
1044 | |||||
1045 | /* Count the number of characters in LINE that precede the printed column | ||||
1046 | offset of GOAL. */ | ||||
1047 | int | ||||
1048 | window_chars_to_goal (char *line, int goal) | ||||
1049 | { | ||||
1050 | register int i, check = 0, hpos; | ||||
1051 | |||||
1052 | for (hpos = 0, i = 0; line[i] != '\n'; i++) | ||||
1053 | { | ||||
1054 | /* Support ANSI escape sequences for -R. */ | ||||
1055 | if (raw_escapes_p | ||||
1056 | && line[i] == '\033' | ||||
1057 | && line[i+1] == '[' | ||||
1058 | && isdigit (line[i+2]) | ||||
1059 | && (line[i+3] == 'm' | ||||
1060 | || (isdigit (line[i+3]) && line[i+4] == 'm'))) | ||||
1061 | while (line[i] != 'm') | ||||
1062 | i++; | ||||
1063 | else | ||||
1064 | check = hpos + character_width (line[i], hpos); | ||||
1065 | |||||
1066 | if (check > goal) | ||||
1067 | break; | ||||
1068 | |||||
1069 | hpos = check; | ||||
1070 | } | ||||
1071 | return (i); | ||||
1072 | } | ||||
1073 | |||||
1074 | /* Create a modeline for WINDOW, and store it in window->modeline. */ | ||||
1075 | void | ||||
1076 | window_make_modeline (WINDOW *window) | ||||
1077 | { | ||||
1078 | register int i; | ||||
1079 | char *modeline; | ||||
1080 | char location_indicator[4]; | ||||
1081 | int lines_remaining; | ||||
1082 | |||||
1083 | /* Only make modelines for those windows which have one. */ | ||||
1084 | if (window->flags & W_InhibitMode0x08) | ||||
1085 | return; | ||||
1086 | |||||
1087 | /* Find the number of lines actually displayed in this window. */ | ||||
1088 | lines_remaining = window->line_count - window->pagetop; | ||||
1089 | |||||
1090 | if (window->pagetop == 0) | ||||
1091 | { | ||||
1092 | if (lines_remaining <= window->height) | ||||
1093 | strcpy (location_indicator, "All"); | ||||
1094 | else | ||||
1095 | strcpy (location_indicator, "Top"); | ||||
1096 | } | ||||
1097 | else | ||||
1098 | { | ||||
1099 | if (lines_remaining <= window->height) | ||||
1100 | strcpy (location_indicator, "Bot"); | ||||
1101 | else | ||||
1102 | { | ||||
1103 | float pt, lc; | ||||
1104 | int percentage; | ||||
1105 | |||||
1106 | pt = (float)window->pagetop; | ||||
1107 | lc = (float)window->line_count; | ||||
1108 | |||||
1109 | percentage = 100 * (pt / lc); | ||||
1110 | |||||
1111 | sprintf (location_indicator, "%2d%%", percentage); | ||||
1112 | } | ||||
1113 | } | ||||
1114 | |||||
1115 | /* Calculate the maximum size of the information to stick in MODELINE. */ | ||||
1116 | { | ||||
1117 | int modeline_len = 0; | ||||
1118 | char *parent = NULL((void *)0), *filename = "*no file*"; | ||||
1119 | char *nodename = "*no node*"; | ||||
1120 | const char *update_message = NULL((void *)0); | ||||
1121 | NODE *node = window->node; | ||||
1122 | |||||
1123 | if (node) | ||||
1124 | { | ||||
1125 | if (node->nodename) | ||||
1126 | nodename = node->nodename; | ||||
1127 | |||||
1128 | if (node->parent) | ||||
1129 | { | ||||
1130 | parent = filename_non_directory (node->parent); | ||||
1131 | modeline_len += strlen ("Subfile: ") + strlen (node->filename); | ||||
1132 | } | ||||
1133 | |||||
1134 | if (node->filename) | ||||
1135 | filename = filename_non_directory (node->filename); | ||||
1136 | |||||
1137 | if (node->flags & N_UpdateTags0x04) | ||||
1138 | update_message = _("--*** Tags out of Date ***")((const char *) ("--*** Tags out of Date ***")); | ||||
1139 | } | ||||
1140 | |||||
1141 | if (update_message) | ||||
1142 | modeline_len += strlen (update_message); | ||||
1143 | modeline_len += strlen (filename); | ||||
1144 | modeline_len += strlen (nodename); | ||||
1145 | modeline_len += 4; /* strlen (location_indicator). */ | ||||
1146 | |||||
1147 | /* 10 for the decimal representation of the number of lines in this | ||||
1148 | node, and the remainder of the text that can appear in the line. */ | ||||
1149 | modeline_len += 10 + strlen (_("-----Info: (), lines ----, ")((const char *) ("-----Info: (), lines ----, "))); | ||||
1150 | modeline_len += window->width; | ||||
1151 | |||||
1152 | modeline = xmalloc (1 + modeline_len); | ||||
1153 | |||||
1154 | /* Special internal windows have no filename. */ | ||||
1155 | if (!parent && !*filename) | ||||
1156 | sprintf (modeline, _("-%s---Info: %s, %d lines --%s--")((const char *) ("-%s---Info: %s, %d lines --%s--")), | ||||
1157 | (window->flags & W_NoWrap0x10) ? "$" : "-", | ||||
1158 | nodename, window->line_count, location_indicator); | ||||
1159 | else | ||||
1160 | sprintf (modeline, _("-%s%s-Info: (%s)%s, %d lines --%s--")((const char *) ("-%s%s-Info: (%s)%s, %d lines --%s--")), | ||||
1161 | (window->flags & W_NoWrap0x10) ? "$" : "-", | ||||
1162 | (node && (node->flags & N_IsCompressed0x08)) ? "zz" : "--", | ||||
1163 | parent ? parent : filename, | ||||
1164 | nodename, window->line_count, location_indicator); | ||||
1165 | |||||
1166 | if (parent) | ||||
1167 | sprintf (modeline + strlen (modeline), _(" Subfile: %s")((const char *) (" Subfile: %s")), filename); | ||||
1168 | |||||
1169 | if (update_message) | ||||
1170 | sprintf (modeline + strlen (modeline), "%s", update_message); | ||||
1171 | |||||
1172 | i = strlen (modeline); | ||||
1173 | |||||
1174 | if (i >= window->width) | ||||
1175 | modeline[window->width] = '\0'; | ||||
1176 | else | ||||
1177 | { | ||||
1178 | while (i < window->width) | ||||
1179 | modeline[i++] = '-'; | ||||
1180 | modeline[i] = '\0'; | ||||
1181 | } | ||||
1182 | |||||
1183 | strcpy (window->modeline, modeline); | ||||
1184 | free (modeline); | ||||
1185 | } | ||||
1186 | } | ||||
1187 | |||||
1188 | /* Make WINDOW start displaying at PERCENT percentage of its node. */ | ||||
1189 | void | ||||
1190 | window_goto_percentage (WINDOW *window, int percent) | ||||
1191 | { | ||||
1192 | int desired_line; | ||||
1193 | |||||
1194 | if (!percent) | ||||
1195 | desired_line = 0; | ||||
1196 | else | ||||
1197 | desired_line = | ||||
1198 | (int) ((float)window->line_count * ((float)percent / 100.0)); | ||||
1199 | |||||
1200 | window->pagetop = desired_line; | ||||
1201 | window->point = | ||||
1202 | window->line_starts[window->pagetop] - window->node->contents; | ||||
1203 | window->flags |= W_UpdateWindow0x01; | ||||
1204 | window_make_modeline (window); | ||||
1205 | } | ||||
1206 | |||||
1207 | /* Get the state of WINDOW, and save it in STATE. */ | ||||
1208 | void | ||||
1209 | window_get_state (WINDOW *window, SEARCH_STATE *state) | ||||
1210 | { | ||||
1211 | state->node = window->node; | ||||
1212 | state->pagetop = window->pagetop; | ||||
1213 | state->point = window->point; | ||||
1214 | } | ||||
1215 | |||||
1216 | /* Set the node, pagetop, and point of WINDOW. */ | ||||
1217 | void | ||||
1218 | window_set_state (WINDOW *window, SEARCH_STATE *state) | ||||
1219 | { | ||||
1220 | if (window->node != state->node) | ||||
1221 | window_set_node_of_window (window, state->node); | ||||
1222 | window->pagetop = state->pagetop; | ||||
1223 | window->point = state->point; | ||||
1224 | } | ||||
1225 | |||||
1226 | |||||
1227 | /* Manipulating home-made nodes. */ | ||||
1228 | |||||
1229 | /* A place to buffer echo area messages. */ | ||||
1230 | static NODE *echo_area_node = NULL((void *)0); | ||||
1231 | |||||
1232 | /* Make the node of the_echo_area be an empty one. */ | ||||
1233 | static void | ||||
1234 | free_echo_area (void) | ||||
1235 | { | ||||
1236 | if (echo_area_node) | ||||
1237 | { | ||||
1238 | maybe_free (echo_area_node->contents)do { if (echo_area_node->contents) free (echo_area_node-> contents); } while (0); | ||||
1239 | free (echo_area_node); | ||||
1240 | } | ||||
1241 | |||||
1242 | echo_area_node = NULL((void *)0); | ||||
1243 | window_set_node_of_window (the_echo_area, echo_area_node); | ||||
1244 | } | ||||
1245 | |||||
1246 | /* Clear the echo area, removing any message that is already present. | ||||
1247 | The echo area is cleared immediately. */ | ||||
1248 | void | ||||
1249 | window_clear_echo_area (void) | ||||
1250 | { | ||||
1251 | free_echo_area (); | ||||
1252 | display_update_one_window (the_echo_area); | ||||
1253 | } | ||||
1254 | |||||
1255 | /* Make a message appear in the echo area, built from FORMAT, ARG1 and ARG2. | ||||
1256 | The arguments are treated similar to printf () arguments, but not all of | ||||
1257 | printf () hair is present. The message appears immediately. If there was | ||||
1258 | already a message appearing in the echo area, it is removed. */ | ||||
1259 | void | ||||
1260 | window_message_in_echo_area (char *format, void *arg1, void *arg2) | ||||
1261 | { | ||||
1262 | free_echo_area (); | ||||
1263 | echo_area_node = build_message_node (format, arg1, arg2); | ||||
1264 | window_set_node_of_window (the_echo_area, echo_area_node); | ||||
1265 | display_update_one_window (the_echo_area); | ||||
1266 | } | ||||
1267 | |||||
1268 | /* Place a temporary message in the echo area built from FORMAT, ARG1 | ||||
1269 | and ARG2. The message appears immediately, but does not destroy | ||||
1270 | any existing message. A future call to unmessage_in_echo_area () | ||||
1271 | restores the old contents. */ | ||||
1272 | static NODE **old_echo_area_nodes = NULL((void *)0); | ||||
1273 | static int old_echo_area_nodes_index = 0; | ||||
1274 | static int old_echo_area_nodes_slots = 0; | ||||
1275 | |||||
1276 | void | ||||
1277 | message_in_echo_area (char *format, void *arg1, void *arg2) | ||||
1278 | { | ||||
1279 | if (echo_area_node) | ||||
1280 | { | ||||
1281 | add_pointer_to_array (echo_area_node, old_echo_area_nodes_index,do { if (old_echo_area_nodes_index + 2 >= old_echo_area_nodes_slots ) old_echo_area_nodes = (NODE * *)(xrealloc (old_echo_area_nodes , (old_echo_area_nodes_slots += 4) * sizeof (NODE *))); old_echo_area_nodes [old_echo_area_nodes_index++] = (NODE *)echo_area_node; old_echo_area_nodes [old_echo_area_nodes_index] = (NODE *)((void *)0); } while (0 ) | ||||
1282 | old_echo_area_nodes, old_echo_area_nodes_slots,do { if (old_echo_area_nodes_index + 2 >= old_echo_area_nodes_slots ) old_echo_area_nodes = (NODE * *)(xrealloc (old_echo_area_nodes , (old_echo_area_nodes_slots += 4) * sizeof (NODE *))); old_echo_area_nodes [old_echo_area_nodes_index++] = (NODE *)echo_area_node; old_echo_area_nodes [old_echo_area_nodes_index] = (NODE *)((void *)0); } while (0 ) | ||||
1283 | 4, NODE *)do { if (old_echo_area_nodes_index + 2 >= old_echo_area_nodes_slots ) old_echo_area_nodes = (NODE * *)(xrealloc (old_echo_area_nodes , (old_echo_area_nodes_slots += 4) * sizeof (NODE *))); old_echo_area_nodes [old_echo_area_nodes_index++] = (NODE *)echo_area_node; old_echo_area_nodes [old_echo_area_nodes_index] = (NODE *)((void *)0); } while (0 ); | ||||
1284 | } | ||||
1285 | echo_area_node = NULL((void *)0); | ||||
1286 | window_message_in_echo_area (format, arg1, arg2); | ||||
1287 | } | ||||
1288 | |||||
1289 | void | ||||
1290 | unmessage_in_echo_area (void) | ||||
1291 | { | ||||
1292 | free_echo_area (); | ||||
1293 | |||||
1294 | if (old_echo_area_nodes_index) | ||||
1295 | echo_area_node = old_echo_area_nodes[--old_echo_area_nodes_index]; | ||||
1296 | |||||
1297 | window_set_node_of_window (the_echo_area, echo_area_node); | ||||
1298 | display_update_one_window (the_echo_area); | ||||
1299 | } | ||||
1300 | |||||
1301 | /* A place to build a message. */ | ||||
1302 | static char *message_buffer = NULL((void *)0); | ||||
1303 | static int message_buffer_index = 0; | ||||
1304 | static int message_buffer_size = 0; | ||||
1305 | |||||
1306 | /* Ensure that there is enough space to stuff LENGTH characters into | ||||
1307 | MESSAGE_BUFFER. */ | ||||
1308 | static void | ||||
1309 | message_buffer_resize (int length) | ||||
1310 | { | ||||
1311 | if (!message_buffer) | ||||
1312 | { | ||||
1313 | message_buffer_size = length + 1; | ||||
1314 | message_buffer = xmalloc (message_buffer_size); | ||||
1315 | message_buffer_index = 0; | ||||
1316 | } | ||||
1317 | |||||
1318 | while (message_buffer_size <= message_buffer_index + length) | ||||
1319 | message_buffer = (char *) | ||||
1320 | xrealloc (message_buffer, | ||||
1321 | message_buffer_size += 100 + (2 * length)); | ||||
1322 | } | ||||
1323 | |||||
1324 | /* Format MESSAGE_BUFFER with the results of printing FORMAT with ARG1 and | ||||
1325 | ARG2. */ | ||||
1326 | static void | ||||
1327 | build_message_buffer (char *format, void *arg1, void *arg2, void *arg3) | ||||
1328 | { | ||||
1329 | register int i, len; | ||||
1330 | void *args[3]; | ||||
1331 | int arg_index = 0; | ||||
1332 | |||||
1333 | args[0] = arg1; | ||||
1334 | args[1] = arg2; | ||||
1335 | args[2] = arg3; | ||||
1336 | |||||
1337 | len = strlen (format); | ||||
1338 | |||||
1339 | message_buffer_resize (len); | ||||
1340 | |||||
1341 | for (i = 0; format[i]; i++) | ||||
1342 | { | ||||
1343 | if (format[i] != '%') | ||||
1344 | { | ||||
1345 | message_buffer[message_buffer_index++] = format[i]; | ||||
1346 | len--; | ||||
1347 | } | ||||
1348 | else | ||||
1349 | { | ||||
1350 | char c; | ||||
1351 | char *fmt_start = format + i; | ||||
1352 | char *fmt; | ||||
1353 | int fmt_len, formatted_len; | ||||
1354 | int paramed = 0; | ||||
1355 | |||||
1356 | format_again: | ||||
1357 | i++; | ||||
1358 | while (format[i] && strchr ("-. +0123456789", format[i])) | ||||
1359 | i++; | ||||
1360 | c = format[i]; | ||||
1361 | |||||
1362 | if (c == '\0') | ||||
1363 | abort (); | ||||
1364 | |||||
1365 | if (c == '$') { | ||||
1366 | /* position parameter parameter */ | ||||
1367 | /* better to use bprintf from bfox's metahtml? */ | ||||
1368 | arg_index = atoi(fmt_start + 1) - 1; | ||||
1369 | if (arg_index < 0) | ||||
1370 | arg_index = 0; | ||||
1371 | if (arg_index >= 2) | ||||
1372 | arg_index = 1; | ||||
1373 | paramed = 1; | ||||
1374 | goto format_again; | ||||
1375 | } | ||||
1376 | |||||
1377 | fmt_len = format + i - fmt_start + 1; | ||||
1378 | fmt = (char *) xmalloc (fmt_len + 1); | ||||
1379 | strncpy (fmt, fmt_start, fmt_len); | ||||
1380 | fmt[fmt_len] = '\0'; | ||||
1381 | |||||
1382 | if (paramed) { | ||||
1383 | /* removed positioned parameter */ | ||||
1384 | char *p; | ||||
1385 | for (p = fmt + 1; *p && *p != '$'; p++) { | ||||
1386 | ; | ||||
1387 | } | ||||
1388 | strcpy(fmt + 1, p + 1); | ||||
1389 | } | ||||
1390 | |||||
1391 | /* If we have "%-98s", maybe 98 calls for a longer string. */ | ||||
1392 | if (fmt_len > 2) | ||||
1393 | { | ||||
1394 | int j; | ||||
1395 | |||||
1396 | for (j = fmt_len - 2; j >= 0; j--) | ||||
1397 | if (isdigit (fmt[j]) || fmt[j] == '$') | ||||
1398 | break; | ||||
1399 | |||||
1400 | formatted_len = atoi (fmt + j); | ||||
1401 | } | ||||
1402 | else | ||||
1403 | formatted_len = c == 's' ? 0 : 1; /* %s can produce empty string */ | ||||
1404 | |||||
1405 | switch (c) | ||||
1406 | { | ||||
1407 | case '%': /* Insert a percent sign. */ | ||||
1408 | message_buffer_resize (len + formatted_len); | ||||
1409 | sprintf | ||||
1410 | (message_buffer + message_buffer_index, fmt, "%"); | ||||
1411 | message_buffer_index += formatted_len; | ||||
1412 | break; | ||||
1413 | |||||
1414 | case 's': /* Insert the current arg as a string. */ | ||||
1415 | { | ||||
1416 | char *string; | ||||
1417 | int string_len; | ||||
1418 | |||||
1419 | string = (char *)args[arg_index++]; | ||||
1420 | string_len = strlen (string); | ||||
1421 | |||||
1422 | if (formatted_len > string_len) | ||||
1423 | string_len = formatted_len; | ||||
1424 | message_buffer_resize (len + string_len); | ||||
1425 | sprintf | ||||
1426 | (message_buffer + message_buffer_index, fmt, string); | ||||
1427 | message_buffer_index += string_len; | ||||
1428 | } | ||||
1429 | break; | ||||
1430 | |||||
1431 | case 'd': /* Insert the current arg as an integer. */ | ||||
1432 | { | ||||
1433 | long long_val; | ||||
1434 | int integer; | ||||
1435 | |||||
1436 | long_val = (long)args[arg_index++]; | ||||
1437 | integer = (int)long_val; | ||||
1438 | |||||
1439 | message_buffer_resize (len + formatted_len > 32 | ||||
1440 | ? formatted_len : 32); | ||||
1441 | sprintf | ||||
1442 | (message_buffer + message_buffer_index, fmt, integer); | ||||
1443 | message_buffer_index = strlen (message_buffer); | ||||
1444 | } | ||||
1445 | break; | ||||
1446 | |||||
1447 | case 'c': /* Insert the current arg as a character. */ | ||||
1448 | { | ||||
1449 | long long_val; | ||||
1450 | int character; | ||||
1451 | |||||
1452 | long_val = (long)args[arg_index++]; | ||||
1453 | character = (int)long_val; | ||||
1454 | |||||
1455 | message_buffer_resize (len + formatted_len); | ||||
1456 | sprintf | ||||
1457 | (message_buffer + message_buffer_index, fmt, character); | ||||
1458 | message_buffer_index += formatted_len; | ||||
1459 | } | ||||
1460 | break; | ||||
1461 | |||||
1462 | default: | ||||
1463 | abort (); | ||||
1464 | } | ||||
1465 | free (fmt); | ||||
1466 | } | ||||
1467 | } | ||||
1468 | message_buffer[message_buffer_index] = '\0'; | ||||
1469 | } | ||||
1470 | |||||
1471 | /* Build a new node which has FORMAT printed with ARG1 and ARG2 as the | ||||
1472 | contents. */ | ||||
1473 | NODE * | ||||
1474 | build_message_node (char *format, void *arg1, void *arg2) | ||||
1475 | { | ||||
1476 | NODE *node; | ||||
1477 | |||||
1478 | message_buffer_index = 0; | ||||
1479 | build_message_buffer (format, arg1, arg2, 0); | ||||
1480 | |||||
1481 | node = message_buffer_to_node (); | ||||
1482 | return (node); | ||||
1483 | } | ||||
1484 | |||||
1485 | /* Convert the contents of the message buffer to a node. */ | ||||
1486 | NODE * | ||||
1487 | message_buffer_to_node (void) | ||||
1488 | { | ||||
1489 | NODE *node; | ||||
1490 | |||||
1491 | node = xmalloc (sizeof (NODE)); | ||||
1492 | node->filename = NULL((void *)0); | ||||
1493 | node->parent = NULL((void *)0); | ||||
1494 | node->nodename = NULL((void *)0); | ||||
1495 | node->flags = 0; | ||||
1496 | node->display_pos =0; | ||||
1497 | |||||
1498 | /* Make sure that this buffer ends with a newline. */ | ||||
1499 | node->nodelen = 1 + strlen (message_buffer); | ||||
1500 | node->contents = xmalloc (1 + node->nodelen); | ||||
1501 | strcpy (node->contents, message_buffer); | ||||
1502 | node->contents[node->nodelen - 1] = '\n'; | ||||
1503 | node->contents[node->nodelen] = '\0'; | ||||
1504 | return (node); | ||||
1505 | } | ||||
1506 | |||||
1507 | /* Useful functions can be called from outside of window.c. */ | ||||
1508 | void | ||||
1509 | initialize_message_buffer (void) | ||||
1510 | { | ||||
1511 | message_buffer_index = 0; | ||||
1512 | } | ||||
1513 | |||||
1514 | /* Print FORMAT with ARG1,2 to the end of the current message buffer. */ | ||||
1515 | void | ||||
1516 | printf_to_message_buffer (char *format, void *arg1, void *arg2, void *arg3) | ||||
1517 | { | ||||
1518 | build_message_buffer (format, arg1, arg2, arg3); | ||||
1519 | } | ||||
1520 | |||||
1521 | /* Return the current horizontal position of the "cursor" on the most | ||||
1522 | recently output message buffer line. */ | ||||
1523 | int | ||||
1524 | message_buffer_length_this_line (void) | ||||
1525 | { | ||||
1526 | register int i; | ||||
1527 | |||||
1528 | if (!message_buffer_index) | ||||
1529 | return (0); | ||||
1530 | |||||
1531 | for (i = message_buffer_index; i && message_buffer[i - 1] != '\n'; i--); | ||||
1532 | |||||
1533 | return (string_width (message_buffer + i, 0)); | ||||
1534 | } | ||||
1535 | |||||
1536 | /* Pad STRING to COUNT characters by inserting blanks. */ | ||||
1537 | int | ||||
1538 | pad_to (int count, char *string) | ||||
1539 | { | ||||
1540 | register int i; | ||||
1541 | |||||
1542 | i = strlen (string); | ||||
1543 | |||||
1544 | if (i >= count) | ||||
1545 | string[i++] = ' '; | ||||
1546 | else | ||||
1547 | { | ||||
1548 | while (i < count) | ||||
1549 | string[i++] = ' '; | ||||
1550 | } | ||||
1551 | string[i] = '\0'; | ||||
1552 | |||||
1553 | return (i); | ||||
1554 | } |