1 /* window.c -- Windows in Info.
2 $Id: window.c,v 1.1.1.3 1998/03/24 18:20:20 law Exp $
4 This file is part of GNU Info, a program for reading online documentation
7 Copyright (C) 1993, 97 Free Software Foundation, Inc.
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2, or (at your option)
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23 Written by Brian Fox (bfox@ai.mit.edu). */
29 #include "info-utils.h"
32 /* The window which describes the screen. */
33 WINDOW
*the_screen
= (WINDOW
*)NULL
;
35 /* The window which describes the echo area. */
36 WINDOW
*the_echo_area
= (WINDOW
*)NULL
;
38 /* The list of windows in Info. */
39 WINDOW
*windows
= (WINDOW
*)NULL
;
41 /* Pointer to the active window in WINDOW_LIST. */
42 WINDOW
*active_window
= (WINDOW
*)NULL
;
44 /* The size of the echo area in Info. It never changes, irregardless of the
45 size of the screen. */
46 #define ECHO_AREA_HEIGHT 1
48 /* Macro returns the amount of space that the echo area truly requires relative
49 to the entire screen. */
50 #define echo_area_required (1 + the_echo_area->height)
52 /* Initalize the window system by creating THE_SCREEN and THE_ECHO_AREA.
53 Create the first window ever.
54 You pass the dimensions of the total screen size. */
56 window_initialize_windows (width
, height
)
59 the_screen
= (WINDOW
*)xmalloc (sizeof (WINDOW
));
60 the_echo_area
= (WINDOW
*)xmalloc (sizeof (WINDOW
));
61 windows
= (WINDOW
*)xmalloc (sizeof (WINDOW
));
62 active_window
= windows
;
64 zero_mem (the_screen
, sizeof (WINDOW
));
65 zero_mem (the_echo_area
, sizeof (WINDOW
));
66 zero_mem (active_window
, sizeof (WINDOW
));
68 /* None of these windows has a goal column yet. */
69 the_echo_area
->goal_column
= -1;
70 active_window
->goal_column
= -1;
71 the_screen
->goal_column
= -1;
73 /* The active and echo_area windows are visible.
74 The echo_area is permanent.
75 The screen is permanent. */
76 active_window
->flags
= W_WindowVisible
;
77 the_echo_area
->flags
= W_WindowIsPerm
| W_InhibitMode
| W_WindowVisible
;
78 the_screen
->flags
= W_WindowIsPerm
;
80 /* The height of the echo area never changes. It is statically set right
81 here, and it must be at least 1 line for display. The size of the
82 initial window cannot be the same size as the screen, since the screen
83 includes the echo area. So, we make the height of the initial window
84 equal to the screen's displayable region minus the height of the echo
86 the_echo_area
->height
= ECHO_AREA_HEIGHT
;
87 active_window
->height
= the_screen
->height
- 1 - the_echo_area
->height
;
88 window_new_screen_size (width
, height
, (VFunction
*)NULL
);
90 /* The echo area uses a different keymap than normal info windows. */
91 the_echo_area
->keymap
= echo_area_keymap
;
92 active_window
->keymap
= info_keymap
;
95 /* Given that the size of the screen has changed to WIDTH and HEIGHT
96 from whatever it was before (found in the_screen->height, ->width),
97 change the size (and possibly location) of each window in the screen.
98 If a window would become too small, call the function DELETER on it,
99 after deleting the window from our chain of windows. If DELETER is NULL,
100 nothing extra is done. The last window can never be deleted, but it can
103 /* If non-null, a function to call with WINDOW as argument when the function
104 window_new_screen_size () has deleted WINDOW. */
105 VFunction
*window_deletion_notifier
= (VFunction
*)NULL
;
108 window_new_screen_size (width
, height
)
111 register WINDOW
*win
;
112 int delta_height
, delta_each
, delta_leftover
;
115 /* If no change, do nothing. */
116 if (width
== the_screen
->width
&& height
== the_screen
->height
)
119 /* If the new window height is too small, make it be zero. */
120 if (height
< (WINDOW_MIN_SIZE
+ the_echo_area
->height
))
125 /* Find out how many windows will change. */
126 for (numwins
= 0, win
= windows
; win
; win
= win
->next
, numwins
++);
128 /* See if some windows will need to be deleted. This is the case if
129 the screen is getting smaller, and the available space divided by
130 the number of windows is less than WINDOW_MIN_SIZE. In that case,
131 delete some windows and try again until there is either enough
132 space to divy up among the windows, or until there is only one
134 while ((height
- echo_area_required
) / numwins
<= WINDOW_MIN_SIZE
)
136 /* If only one window, make the size of it be zero, and return
141 maybe_free (windows
->line_starts
);
142 windows
->line_starts
= (char **)NULL
;
143 windows
->line_count
= 0;
147 /* If we have some temporary windows, delete one of them. */
148 for (win
= windows
; win
; win
= win
->next
)
149 if (win
->flags
& W_TempWindow
)
152 /* Otherwise, delete the first window, and try again. */
156 if (window_deletion_notifier
)
157 (*window_deletion_notifier
) (win
);
159 window_delete_window (win
);
163 /* The screen has changed height and width. */
164 delta_height
= height
- the_screen
->height
; /* This is how much. */
165 the_screen
->height
= height
; /* This is the new height. */
166 the_screen
->width
= width
; /* This is the new width. */
168 /* Set the start of the echo area. */
169 the_echo_area
->first_row
= height
- the_echo_area
->height
;
170 the_echo_area
->width
= width
;
172 /* Check to see if the screen can really be changed this way. */
173 if ((!windows
->next
) && ((windows
->height
== 0) && (delta_height
< 0)))
176 /* Divide the change in height among the available windows. */
177 delta_each
= delta_height
/ numwins
;
178 delta_leftover
= delta_height
- (delta_each
* numwins
);
180 /* Change the height of each window in the chain by delta_each. Change
181 the height of the last window in the chain by delta_each and by the
182 leftover amount of change. Change the width of each window to be
184 for (win
= windows
; win
; win
= win
->next
)
186 if ((win
->width
!= width
) && ((win
->flags
& W_InhibitMode
) == 0))
189 maybe_free (win
->modeline
);
190 win
->modeline
= (char *)xmalloc (1 + width
);
193 win
->height
+= delta_each
;
195 /* If the previous height of this window was zero, it was the only
196 window, and it was not visible. Thus we need to compensate for
198 if (win
->height
== delta_each
)
199 win
->height
-= (1 + the_echo_area
->height
);
201 /* If this is not the first window in the chain, then change the
202 first row of it. We cannot just add delta_each to the first row,
203 since this window's first row is the sum of the collective increases
204 that have gone before it. So we just add one to the location of the
205 previous window's modeline. */
207 win
->first_row
= (win
->prev
->first_row
+ win
->prev
->height
) + 1;
209 /* The last window in the chain gets the extra space (or shrinkage). */
211 win
->height
+= delta_leftover
;
214 recalculate_line_starts (win
);
216 win
->flags
|= W_UpdateWindow
;
219 /* If the screen got smaller, check over the windows just shrunk to
220 keep them within bounds. Some of the windows may have gotten smaller
221 than WINDOW_MIN_HEIGHT in which case some of the other windows are
222 larger than the available display space in the screen. Because of our
223 intial test above, we know that there is enough space for all of the
225 if ((delta_each
< 0) && ((windows
->height
!= 0) && windows
->next
))
229 avail
= the_screen
->height
- (numwins
+ the_echo_area
->height
);
234 if ((win
->height
< WINDOW_MIN_HEIGHT
) ||
235 (win
->height
> avail
))
239 /* Split the space among the available windows. */
240 delta_each
= avail
/ numwins
;
241 delta_leftover
= avail
- (delta_each
* numwins
);
243 for (win
= windows
; win
; win
= win
->next
)
248 (win
->prev
->first_row
+ win
->prev
->height
) + 1;
249 win
->height
= delta_each
;
252 /* Give the leftover space (if any) to the last window. */
253 lastwin
->height
+= delta_leftover
;
262 /* Make a new window showing NODE, and return that window structure.
263 If NODE is passed as NULL, then show the node showing in the active
264 window. If the window could not be made return a NULL pointer. The
265 active window is not changed.*/
267 window_make_window (node
)
273 node
= active_window
->node
;
275 /* If there isn't enough room to make another window, return now. */
276 if ((active_window
->height
/ 2) < WINDOW_MIN_SIZE
)
277 return ((WINDOW
*)NULL
);
279 /* Make and initialize the new window.
280 The fudging about with -1 and +1 is because the following window in the
281 chain cannot start at window->height, since that is where the modeline
282 for the previous window is displayed. The inverse adjustment is made
283 in window_delete_window (). */
284 window
= (WINDOW
*)xmalloc (sizeof (WINDOW
));
285 window
->width
= the_screen
->width
;
286 window
->height
= (active_window
->height
/ 2) - 1;
287 #if defined (SPLIT_BEFORE_ACTIVE)
288 window
->first_row
= active_window
->first_row
;
290 window
->first_row
= active_window
->first_row
+
291 (active_window
->height
- window
->height
);
293 window
->keymap
= info_keymap
;
294 window
->goal_column
= -1;
295 window
->modeline
= (char *)xmalloc (1 + window
->width
);
296 window
->line_starts
= (char **)NULL
;
297 window
->flags
= W_UpdateWindow
| W_WindowVisible
;
298 window_set_node_of_window (window
, node
);
300 /* Adjust the height of the old active window. */
301 active_window
->height
-= (window
->height
+ 1);
302 #if defined (SPLIT_BEFORE_ACTIVE)
303 active_window
->first_row
+= (window
->height
+ 1);
305 active_window
->flags
|= W_UpdateWindow
;
307 /* Readjust the new and old windows so that their modelines and contents
308 will be displayed correctly. */
310 /* We don't have to do this for WINDOW since window_set_node_of_window ()
312 window_adjust_pagetop (window
);
313 window_make_modeline (window
);
316 /* We do have to readjust the existing active window. */
317 window_adjust_pagetop (active_window
);
318 window_make_modeline (active_window
);
320 #if defined (SPLIT_BEFORE_ACTIVE)
321 /* This window is just before the active one. The active window gets
322 bumped down one. The active window is not changed. */
323 window
->next
= active_window
;
325 window
->prev
= active_window
->prev
;
326 active_window
->prev
= window
;
329 window
->prev
->next
= window
;
333 /* This window is just after the active one. Which window is active is
335 window
->prev
= active_window
;
336 window
->next
= active_window
->next
;
337 active_window
->next
= window
;
339 window
->next
->prev
= window
;
340 #endif /* !SPLIT_BEFORE_ACTIVE */
344 /* These useful macros make it possible to read the code in
345 window_change_window_height (). */
346 #define grow_me_shrinking_next(me, next, diff) \
348 me->height += diff; \
349 next->height -= diff; \
350 next->first_row += diff; \
351 window_adjust_pagetop (next); \
354 #define grow_me_shrinking_prev(me, prev, diff) \
356 me->height += diff; \
357 prev->height -= diff; \
358 me->first_row -=diff; \
359 window_adjust_pagetop (prev); \
362 #define shrink_me_growing_next(me, next, diff) \
364 me->height -= diff; \
365 next->height += diff; \
366 next->first_row -= diff; \
367 window_adjust_pagetop (next); \
370 #define shrink_me_growing_prev(me, prev, diff) \
372 me->height -= diff; \
373 prev->height += diff; \
374 me->first_row += diff; \
375 window_adjust_pagetop (prev); \
378 /* Change the height of WINDOW by AMOUNT. This also automagically adjusts
379 the previous and next windows in the chain. If there is only one user
380 window, then no change takes place. */
382 window_change_window_height (window
, amount
)
386 register WINDOW
*win
, *prev
, *next
;
388 /* If there is only one window, or if the amount of change is zero,
389 return immediately. */
390 if (!windows
->next
|| amount
== 0)
393 /* Find this window in our chain. */
394 for (win
= windows
; win
; win
= win
->next
)
398 /* If the window is isolated (i.e., doesn't appear in our window list,
403 /* Change the height of this window by AMOUNT, if that is possible.
404 It can be impossible if there isn't enough available room on the
405 screen, or if the resultant window would be too small. */
410 /* WINDOW decreasing in size? */
413 int abs_amount
= -amount
; /* It is easier to deal with this way. */
415 /* If the resultant window would be too small, stop here. */
416 if ((window
->height
- abs_amount
) < WINDOW_MIN_HEIGHT
)
419 /* If we have two neighboring windows, choose the smaller one to get
423 if (prev
->height
< next
->height
)
424 shrink_me_growing_prev (window
, prev
, abs_amount
);
426 shrink_me_growing_next (window
, next
, abs_amount
);
429 shrink_me_growing_next (window
, next
, abs_amount
);
431 shrink_me_growing_prev (window
, prev
, abs_amount
);
434 /* WINDOW increasing in size? */
437 int total_avail
, next_avail
= 0, prev_avail
= 0;
440 next_avail
= next
->height
- WINDOW_MIN_SIZE
;
443 prev_avail
= prev
->height
- WINDOW_MIN_SIZE
;
445 total_avail
= next_avail
+ prev_avail
;
447 /* If there isn't enough space available to grow this window, give up. */
448 if (amount
> total_avail
)
451 /* If there aren't two neighboring windows, or if one of the neighbors
452 is larger than the other one by at least AMOUNT, grow that one. */
453 if ((next
&& !prev
) || ((next_avail
- amount
) >= prev_avail
))
454 grow_me_shrinking_next (window
, next
, amount
);
455 else if ((prev
&& !next
) || ((prev_avail
- amount
) >= next_avail
))
456 grow_me_shrinking_prev (window
, prev
, amount
);
461 /* This window has two neighbors. They both must be shrunk in to
462 make enough space for WINDOW to grow. Make them both the same
464 if (prev_avail
> next_avail
)
466 change
= prev_avail
- next_avail
;
467 grow_me_shrinking_prev (window
, prev
, change
);
472 change
= next_avail
- prev_avail
;
473 grow_me_shrinking_next (window
, next
, change
);
477 /* Both neighbors are the same size. Split the difference in
478 AMOUNT between them. */
484 /* Odd numbers grow next, even grow prev. */
496 window_adjust_pagetop (prev
);
497 window_adjust_pagetop (next
);
501 prev
->flags
|= W_UpdateWindow
;
504 next
->flags
|= W_UpdateWindow
;
506 window
->flags
|= W_UpdateWindow
;
507 window_adjust_pagetop (window
);
510 /* Tile all of the windows currently displayed in the global variable
511 WINDOWS. If argument STYLE is TILE_INTERNALS, tile windows displaying
512 internal nodes as well, otherwise do not change the height of such
515 window_tile_windows (style
)
518 WINDOW
*win
, *last_adjusted
;
519 int numwins
, avail
, per_win_height
, leftover
;
523 do_internals
= (style
== TILE_INTERNALS
);
525 for (win
= windows
; win
; win
= win
->next
)
526 if (do_internals
|| !win
->node
||
527 (win
->node
->flags
& N_IsInternal
) == 0)
529 avail
+= win
->height
;
533 if (numwins
<= 1 || !the_screen
->height
)
536 /* Find the size for each window. Divide the size of the usable portion
537 of the screen by the number of windows. */
538 per_win_height
= avail
/ numwins
;
539 leftover
= avail
- (per_win_height
* numwins
);
541 last_adjusted
= (WINDOW
*)NULL
;
542 for (win
= windows
; win
; win
= win
->next
)
544 if (do_internals
|| !win
->node
||
545 (win
->node
->flags
& N_IsInternal
) == 0)
548 win
->height
= per_win_height
;
553 last_adjusted
->height
+= leftover
;
555 /* Readjust the first_row of every window in the chain. */
556 for (win
= windows
; win
; win
= win
->next
)
559 win
->first_row
= win
->prev
->first_row
+ win
->prev
->height
+ 1;
561 window_adjust_pagetop (win
);
562 win
->flags
|= W_UpdateWindow
;
566 /* Toggle the state of line wrapping in WINDOW. This can do a bit of fancy
569 window_toggle_wrap (window
)
572 if (window
->flags
& W_NoWrap
)
573 window
->flags
&= ~W_NoWrap
;
575 window
->flags
|= W_NoWrap
;
577 if (window
!= the_echo_area
)
580 int old_lines
, old_pagetop
;
582 old_starts
= window
->line_starts
;
583 old_lines
= window
->line_count
;
584 old_pagetop
= window
->pagetop
;
586 calculate_line_starts (window
);
588 /* Make sure that point appears within this window. */
589 window_adjust_pagetop (window
);
591 /* If the pagetop hasn't changed maybe we can do some scrolling now
592 to speed up the display. Many of the line starts will be the same,
593 so scrolling here is a very good optimization.*/
594 if (old_pagetop
== window
->pagetop
)
595 display_scroll_line_starts
596 (window
, old_pagetop
, old_starts
, old_lines
);
597 maybe_free (old_starts
);
599 window
->flags
|= W_UpdateWindow
;
602 /* Set WINDOW to display NODE. */
604 window_set_node_of_window (window
, node
)
611 recalculate_line_starts (window
);
612 window
->flags
|= W_UpdateWindow
;
613 window_adjust_pagetop (window
);
614 window_make_modeline (window
);
617 /* Delete WINDOW from the list of known windows. If this window was the
618 active window, make the next window in the chain be the active window.
619 If the active window is the next or previous window, choose that window
620 as the recipient of the extra space. Otherwise, prefer the next window. */
622 window_delete_window (window
)
625 WINDOW
*next
, *prev
, *window_to_fix
;
630 /* You cannot delete the only window or a permanent window. */
631 if ((!next
&& !prev
) || (window
->flags
& W_WindowIsPerm
))
642 if (window
->line_starts
)
643 free (window
->line_starts
);
645 if (window
->modeline
)
646 free (window
->modeline
);
648 if (window
== active_window
)
650 /* If there isn't a next window, then there must be a previous one,
651 since we cannot delete the last window. If there is a next window,
652 prefer to use that as the active window. */
654 active_window
= next
;
656 active_window
= prev
;
659 if (next
&& active_window
== next
)
660 window_to_fix
= next
;
661 else if (prev
&& active_window
== prev
)
662 window_to_fix
= prev
;
664 window_to_fix
= next
;
666 window_to_fix
= prev
;
668 window_to_fix
= windows
;
670 if (window_to_fix
->first_row
> window
->first_row
)
674 /* Try to adjust the visible part of the node so that as little
675 text as possible has to move. */
676 diff
= window_to_fix
->first_row
- window
->first_row
;
677 window_to_fix
->first_row
= window
->first_row
;
679 window_to_fix
->pagetop
-= diff
;
680 if (window_to_fix
->pagetop
< 0)
681 window_to_fix
->pagetop
= 0;
684 /* The `+ 1' is to offset the difference between the first_row locations.
685 See the code in window_make_window (). */
686 window_to_fix
->height
+= window
->height
+ 1;
687 window_to_fix
->flags
|= W_UpdateWindow
;
692 /* For every window in CHAIN, set the flags member to have FLAG set. */
694 window_mark_chain (chain
, flag
)
698 register WINDOW
*win
;
700 for (win
= chain
; win
; win
= win
->next
)
704 /* For every window in CHAIN, clear the flags member of FLAG. */
706 window_unmark_chain (chain
, flag
)
710 register WINDOW
*win
;
712 for (win
= chain
; win
; win
= win
->next
)
716 /* Return the number of characters it takes to display CHARACTER on the
719 character_width (character
, hpos
)
722 int printable_limit
= 127;
726 printable_limit
= 255;
728 if (character
> printable_limit
)
730 else if (iscntrl (character
))
736 width
= the_screen
->width
- hpos
;
739 width
= ((hpos
+ 8) & 0xf8) - hpos
;
745 else if (character
== DEL
)
751 /* Return the number of characters it takes to display STRING on the screen
754 string_width (string
, hpos
)
758 register int i
, width
, this_char_width
;
760 for (width
= 0, i
= 0; string
[i
]; i
++)
762 this_char_width
= character_width (string
[i
], hpos
);
763 width
+= this_char_width
;
764 hpos
+= this_char_width
;
769 /* Quickly guess the approximate number of lines to that NODE would
770 take to display. This really only counts carriage returns. */
772 window_physical_lines (node
)
775 register int i
, lines
;
781 contents
= node
->contents
;
782 for (i
= 0, lines
= 1; i
< node
->nodelen
; i
++)
783 if (contents
[i
] == '\n')
789 /* Calculate a list of line starts for the node belonging to WINDOW. The line
790 starts are pointers to the actual text within WINDOW->NODE. */
792 calculate_line_starts (window
)
795 register int i
, hpos
;
796 char **line_starts
= (char **)NULL
;
797 int line_starts_index
= 0, line_starts_slots
= 0;
801 window
->line_starts
= (char **)NULL
;
802 window
->line_count
= 0;
808 /* Grovel the node starting at the top, and for each line calculate the
809 width of the characters appearing in that line. Add each line start
815 while (i
< node
->nodelen
)
817 char *line
= node
->contents
+ i
;
818 unsigned int cwidth
, c
;
820 add_pointer_to_array (line
, line_starts_index
, line_starts
,
821 line_starts_slots
, 100, char *);
830 c
= node
->contents
[i
];
831 cwidth
= character_width (c
, hpos
);
833 /* If this character fits within this line, just do the next one. */
834 if ((hpos
+ cwidth
) < window
->width
)
842 /* If this character would position the cursor at the start of
843 the next printed screen line, then do the next line. */
844 if (c
== '\n' || c
== '\r' || c
== '\t')
852 /* This character passes the window width border. Postion
853 the cursor after the printed character, but remember this
854 line start as where this character is. A bit tricky. */
856 /* If this window doesn't wrap lines, proceed to the next
857 physical line here. */
858 if (window
->flags
& W_NoWrap
)
861 while (i
< node
->nodelen
&& node
->contents
[i
] != '\n')
864 if (node
->contents
[i
] == '\n')
869 hpos
= the_screen
->width
- hpos
;
877 window
->line_starts
= line_starts
;
878 window
->line_count
= line_starts_index
;
881 /* Given WINDOW, recalculate the line starts for the node it displays. */
883 recalculate_line_starts (window
)
886 maybe_free (window
->line_starts
);
887 calculate_line_starts (window
);
890 /* Global variable control redisplay of scrolled windows. If non-zero, it
891 is the desired number of lines to scroll the window in order to make
892 point visible. A user might set this to 1 for smooth scrolling. If
893 set to zero, the line containing point is centered within the window. */
894 int window_scroll_step
= 0;
896 /* Adjust the pagetop of WINDOW such that the cursor point will be visible. */
898 window_adjust_pagetop (window
)
901 register int line
= 0;
907 contents
= window
->node
->contents
;
909 /* Find the first printed line start which is after WINDOW->point. */
910 for (line
= 0; line
< window
->line_count
; line
++)
914 line_start
= window
->line_starts
[line
];
916 if ((line_start
- contents
) > window
->point
)
920 /* The line index preceding the line start which is past point is the
921 one containing point. */
924 /* If this line appears in the current displayable page, do nothing.
925 Otherwise, adjust the top of the page to make this line visible. */
926 if ((line
< window
->pagetop
) ||
927 (line
- window
->pagetop
> (window
->height
- 1)))
929 /* The user-settable variable "scroll-step" is used to attempt
930 to make point visible, iff it is non-zero. If that variable
931 is zero, then the line containing point is centered within
933 if (window_scroll_step
< window
->height
)
935 if ((line
< window
->pagetop
) &&
936 ((window
->pagetop
- window_scroll_step
) <= line
))
937 window
->pagetop
-= window_scroll_step
;
938 else if ((line
- window
->pagetop
> (window
->height
- 1)) &&
939 ((line
- (window
->pagetop
+ window_scroll_step
)
941 window
->pagetop
+= window_scroll_step
;
943 window
->pagetop
= line
- ((window
->height
- 1) / 2);
946 window
->pagetop
= line
- ((window
->height
- 1) / 2);
948 if (window
->pagetop
< 0)
950 window
->flags
|= W_UpdateWindow
;
954 /* Return the index of the line containing point. */
956 window_line_of_point (window
)
959 register int i
, start
= 0;
961 /* Try to optimize. Check to see if point is past the pagetop for
962 this window, and if so, start searching forward from there. */
963 if ((window
->pagetop
> -1 && window
->pagetop
< window
->line_count
) &&
964 (window
->line_starts
[window
->pagetop
] - window
->node
->contents
)
966 start
= window
->pagetop
;
968 for (i
= start
; i
< window
->line_count
; i
++)
970 if ((window
->line_starts
[i
] - window
->node
->contents
) > window
->point
)
977 /* Get and return the goal column for this window. */
979 window_get_goal_column (window
)
985 if (window
->goal_column
!= -1)
986 return (window
->goal_column
);
988 /* Okay, do the work. Find the printed offset of the cursor
990 return (window_get_cursor_column (window
));
993 /* Get and return the printed column offset of the cursor in this window. */
995 window_get_cursor_column (window
)
1001 i
= window_line_of_point (window
);
1006 line
= window
->line_starts
[i
];
1007 end
= window
->point
- (line
- window
->node
->contents
);
1009 for (hpos
= 0, i
= 0; i
< end
; i
++)
1010 hpos
+= character_width (line
[i
], hpos
);
1015 /* Count the number of characters in LINE that precede the printed column
1018 window_chars_to_goal (line
, goal
)
1022 register int i
, check
, hpos
;
1024 for (hpos
= 0, i
= 0; line
[i
] != '\n'; i
++)
1027 check
= hpos
+ character_width (line
[i
], hpos
);
1037 /* Create a modeline for WINDOW, and store it in window->modeline. */
1039 window_make_modeline (window
)
1044 char location_indicator
[4];
1045 int lines_remaining
;
1047 /* Only make modelines for those windows which have one. */
1048 if (window
->flags
& W_InhibitMode
)
1051 /* Find the number of lines actually displayed in this window. */
1052 lines_remaining
= window
->line_count
- window
->pagetop
;
1054 if (window
->pagetop
== 0)
1056 if (lines_remaining
<= window
->height
)
1057 strcpy (location_indicator
, "All");
1059 strcpy (location_indicator
, "Top");
1063 if (lines_remaining
<= window
->height
)
1064 strcpy (location_indicator
, "Bot");
1070 pt
= (float)window
->pagetop
;
1071 lc
= (float)window
->line_count
;
1073 percentage
= 100 * (pt
/ lc
);
1075 sprintf (location_indicator
, "%2d%%", percentage
);
1079 /* Calculate the maximum size of the information to stick in MODELINE. */
1081 int modeline_len
= 0;
1082 char *parent
= (char *)NULL
, *filename
= "*no file*";
1083 char *nodename
= "*no node*";
1084 char *update_message
= (char *)NULL
;
1085 NODE
*node
= window
->node
;
1090 nodename
= node
->nodename
;
1094 parent
= filename_non_directory (node
->parent
);
1095 modeline_len
+= strlen ("Subfile: ") + strlen (node
->filename
);
1099 filename
= filename_non_directory (node
->filename
);
1101 if (node
->flags
& N_UpdateTags
)
1102 update_message
= _("--*** Tags out of Date ***");
1106 modeline_len
+= strlen (update_message
);
1107 modeline_len
+= strlen (filename
);
1108 modeline_len
+= strlen (nodename
);
1109 modeline_len
+= 4; /* strlen (location_indicator). */
1111 /* 10 for the decimal representation of the number of lines in this
1112 node, and the remainder of the text that can appear in the line. */
1113 modeline_len
+= 10 + strlen (_("-----Info: (), lines ----, "));
1114 modeline_len
+= window
->width
;
1116 modeline
= (char *)xmalloc (1 + modeline_len
);
1118 /* Special internal windows have no filename. */
1119 if (!parent
&& !*filename
)
1120 sprintf (modeline
, _("-%s---Info: %s, %d lines --%s--"),
1121 (window
->flags
& W_NoWrap
) ? "$" : "-",
1122 nodename
, window
->line_count
, location_indicator
);
1124 sprintf (modeline
, _("-%s%s-Info: (%s)%s, %d lines --%s--"),
1125 (window
->flags
& W_NoWrap
) ? "$" : "-",
1126 (node
&& (node
->flags
& N_IsCompressed
)) ? "zz" : "--",
1127 parent
? parent
: filename
,
1128 nodename
, window
->line_count
, location_indicator
);
1131 sprintf (modeline
+ strlen (modeline
), _(" Subfile: %s"), filename
);
1134 sprintf (modeline
+ strlen (modeline
), "%s", update_message
);
1136 i
= strlen (modeline
);
1138 if (i
>= window
->width
)
1139 modeline
[window
->width
] = '\0';
1142 while (i
< window
->width
)
1143 modeline
[i
++] = '-';
1147 strcpy (window
->modeline
, modeline
);
1152 /* Make WINDOW start displaying at PERCENT percentage of its node. */
1154 window_goto_percentage (window
, percent
)
1164 (int) ((float)window
->line_count
* ((float)percent
/ 100.0));
1166 window
->pagetop
= desired_line
;
1168 window
->line_starts
[window
->pagetop
] - window
->node
->contents
;
1169 window
->flags
|= W_UpdateWindow
;
1170 window_make_modeline (window
);
1173 /* Get the state of WINDOW, and save it in STATE. */
1175 window_get_state (window
, state
)
1177 WINDOW_STATE
*state
;
1179 state
->node
= window
->node
;
1180 state
->pagetop
= window
->pagetop
;
1181 state
->point
= window
->point
;
1184 /* Set the node, pagetop, and point of WINDOW. */
1186 window_set_state (window
, state
)
1188 WINDOW_STATE
*state
;
1190 if (window
->node
!= state
->node
)
1191 window_set_node_of_window (window
, state
->node
);
1192 window
->pagetop
= state
->pagetop
;
1193 window
->point
= state
->point
;
1197 /* **************************************************************** */
1199 /* Manipulating Home-Made Nodes */
1201 /* **************************************************************** */
1203 /* A place to buffer echo area messages. */
1204 static NODE
*echo_area_node
= (NODE
*)NULL
;
1206 /* Make the node of the_echo_area be an empty one. */
1212 maybe_free (echo_area_node
->contents
);
1213 free (echo_area_node
);
1216 echo_area_node
= (NODE
*)NULL
;
1217 window_set_node_of_window (the_echo_area
, echo_area_node
);
1220 /* Clear the echo area, removing any message that is already present.
1221 The echo area is cleared immediately. */
1223 window_clear_echo_area ()
1226 display_update_one_window (the_echo_area
);
1229 /* Make a message appear in the echo area, built from FORMAT, ARG1 and ARG2.
1230 The arguments are treated similar to printf () arguments, but not all of
1231 printf () hair is present. The message appears immediately. If there was
1232 already a message appearing in the echo area, it is removed. */
1234 window_message_in_echo_area (format
, arg1
, arg2
)
1239 echo_area_node
= build_message_node (format
, arg1
, arg2
);
1240 window_set_node_of_window (the_echo_area
, echo_area_node
);
1241 display_update_one_window (the_echo_area
);
1244 /* Place a temporary message in the echo area built from FORMAT, ARG1
1245 and ARG2. The message appears immediately, but does not destroy
1246 any existing message. A future call to unmessage_in_echo_area ()
1247 restores the old contents. */
1248 static NODE
**old_echo_area_nodes
= (NODE
**)NULL
;
1249 static int old_echo_area_nodes_index
= 0;
1250 static int old_echo_area_nodes_slots
= 0;
1253 message_in_echo_area (format
, arg1
, arg2
)
1259 add_pointer_to_array (echo_area_node
, old_echo_area_nodes_index
,
1260 old_echo_area_nodes
, old_echo_area_nodes_slots
,
1263 echo_area_node
= (NODE
*)NULL
;
1264 window_message_in_echo_area (format
, arg1
, arg2
);
1268 unmessage_in_echo_area ()
1272 if (old_echo_area_nodes_index
)
1273 echo_area_node
= old_echo_area_nodes
[--old_echo_area_nodes_index
];
1275 window_set_node_of_window (the_echo_area
, echo_area_node
);
1276 display_update_one_window (the_echo_area
);
1279 /* A place to build a message. */
1280 static char *message_buffer
= (char *)NULL
;
1281 static int message_buffer_index
= 0;
1282 static int message_buffer_size
= 0;
1284 /* Ensure that there is enough space to stuff LENGTH characters into
1287 message_buffer_resize (length
)
1290 if (!message_buffer
)
1292 message_buffer_size
= length
+ 1;
1293 message_buffer
= (char *)xmalloc (message_buffer_size
);
1294 message_buffer_index
= 0;
1297 while (message_buffer_size
<= message_buffer_index
+ length
)
1298 message_buffer
= (char *)
1299 xrealloc (message_buffer
,
1300 message_buffer_size
+= 100 + (2 * length
));
1303 /* Format MESSAGE_BUFFER with the results of printing FORMAT with ARG1 and
1306 build_message_buffer (format
, arg1
, arg2
)
1310 register int i
, len
;
1317 len
= strlen (format
);
1319 message_buffer_resize (len
);
1321 for (i
= 0; format
[i
]; i
++)
1323 if (format
[i
] != '%')
1325 message_buffer
[message_buffer_index
++] = format
[i
];
1336 case '%': /* Insert a percent sign. */
1337 message_buffer_resize (len
+ 1);
1338 message_buffer
[message_buffer_index
++] = '%';
1341 case 's': /* Insert the current arg as a string. */
1346 string
= (char *)args
[arg_index
++];
1347 string_len
= strlen (string
);
1349 message_buffer_resize (len
+ string_len
);
1351 (message_buffer
+ message_buffer_index
, "%s", string
);
1352 message_buffer_index
+= string_len
;
1356 case 'd': /* Insert the current arg as an integer. */
1361 long_val
= (long)args
[arg_index
++];
1362 integer
= (int)long_val
;
1364 message_buffer_resize (len
+ 32);
1366 (message_buffer
+ message_buffer_index
, "%d", integer
);
1367 message_buffer_index
= strlen (message_buffer
);
1371 case 'c': /* Insert the current arg as a character. */
1376 long_val
= (long)args
[arg_index
++];
1377 character
= (int)long_val
;
1379 message_buffer_resize (len
+ 1);
1380 message_buffer
[message_buffer_index
++] = character
;
1389 message_buffer
[message_buffer_index
] = '\0';
1392 /* Build a new node which has FORMAT printed with ARG1 and ARG2 as the
1395 build_message_node (format
, arg1
, arg2
)
1401 message_buffer_index
= 0;
1402 build_message_buffer (format
, arg1
, arg2
);
1404 node
= message_buffer_to_node ();
1408 /* Convert the contents of the message buffer to a node. */
1410 message_buffer_to_node ()
1414 node
= (NODE
*)xmalloc (sizeof (NODE
));
1415 node
->filename
= (char *)NULL
;
1416 node
->parent
= (char *)NULL
;
1417 node
->nodename
= (char *)NULL
;
1420 /* Make sure that this buffer ends with a newline. */
1421 node
->nodelen
= 1 + strlen (message_buffer
);
1422 node
->contents
= (char *)xmalloc (1 + node
->nodelen
);
1423 strcpy (node
->contents
, message_buffer
);
1424 node
->contents
[node
->nodelen
- 1] = '\n';
1425 node
->contents
[node
->nodelen
] = '\0';
1429 /* Useful functions can be called from outside of window.c. */
1431 initialize_message_buffer ()
1433 message_buffer_index
= 0;
1436 /* Print FORMAT with ARG1,2 to the end of the current message buffer. */
1438 printf_to_message_buffer (format
, arg1
, arg2
)
1442 build_message_buffer (format
, arg1
, arg2
);
1445 /* Return the current horizontal position of the "cursor" on the most
1446 recently output message buffer line. */
1448 message_buffer_length_this_line ()
1452 if (!message_buffer_index
)
1455 for (i
= message_buffer_index
; i
&& message_buffer
[i
- 1] != '\n'; i
--);
1457 return (string_width (message_buffer
+ i
, 0));
1460 /* Pad STRING to COUNT characters by inserting blanks. */
1462 pad_to (count
, string
)
1468 i
= strlen (string
);