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