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 }