File: | src/gnu/usr.bin/texinfo/info/window.c |
Warning: | line 447, column 9 Access to field 'height' results in a dereference of a null pointer (loaded from variable 'next') |
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
| |||
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
| |||
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 && !prev) || (window->flags & W_WindowIsPerm0x02)) | |||
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 && active_window == next) | |||
650 | window_to_fix = next; | |||
651 | else if (prev && active_window == 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 | } |