1 /* window.c -- windows in Info.
2 $Id: window.c,v 1.16 2002/03/08 21:41:44 karl Exp $
4 Copyright (C) 1993, 97, 98, 2001, 02 Free Software Foundation, Inc.
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 Written by Brian Fox (bfox@ai.mit.edu). */
26 #include "info-utils.h"
29 /* The window which describes the screen. */
30 WINDOW *the_screen = NULL;
32 /* The window which describes the echo area. */
33 WINDOW *the_echo_area = NULL;
35 /* The list of windows in Info. */
36 WINDOW *windows = NULL;
38 /* Pointer to the active window in WINDOW_LIST. */
39 WINDOW *active_window = NULL;
41 /* The size of the echo area in Info. It never changes, irregardless of the
42 size of the screen. */
43 #define ECHO_AREA_HEIGHT 1
45 /* Macro returns the amount of space that the echo area truly requires relative
46 to the entire screen. */
47 #define echo_area_required (1 + the_echo_area->height)
49 /* Initalize the window system by creating THE_SCREEN and THE_ECHO_AREA.
50 Create the first window ever.
51 You pass the dimensions of the total screen size. */
53 window_initialize_windows (width, height)
56 the_screen = xmalloc (sizeof (WINDOW));
57 the_echo_area = xmalloc (sizeof (WINDOW));
58 windows = xmalloc (sizeof (WINDOW));
59 active_window = windows;
61 zero_mem (the_screen, sizeof (WINDOW));
62 zero_mem (the_echo_area, sizeof (WINDOW));
63 zero_mem (active_window, sizeof (WINDOW));
65 /* None of these windows has a goal column yet. */
66 the_echo_area->goal_column = -1;
67 active_window->goal_column = -1;
68 the_screen->goal_column = -1;
70 /* The active and echo_area windows are visible.
71 The echo_area is permanent.
72 The screen is permanent. */
73 active_window->flags = W_WindowVisible;
74 the_echo_area->flags = W_WindowIsPerm | W_InhibitMode | W_WindowVisible;
75 the_screen->flags = W_WindowIsPerm;
77 /* The height of the echo area never changes. It is statically set right
78 here, and it must be at least 1 line for display. The size of the
79 initial window cannot be the same size as the screen, since the screen
80 includes the echo area. So, we make the height of the initial window
81 equal to the screen's displayable region minus the height of the echo
83 the_echo_area->height = ECHO_AREA_HEIGHT;
84 active_window->height = the_screen->height - 1 - the_echo_area->height;
85 window_new_screen_size (width, height, NULL);
87 /* The echo area uses a different keymap than normal info windows. */
88 the_echo_area->keymap = echo_area_keymap;
89 active_window->keymap = info_keymap;
92 /* Given that the size of the screen has changed to WIDTH and HEIGHT
93 from whatever it was before (found in the_screen->height, ->width),
94 change the size (and possibly location) of each window in the screen.
95 If a window would become too small, call the function DELETER on it,
96 after deleting the window from our chain of windows. If DELETER is NULL,
97 nothing extra is done. The last window can never be deleted, but it can
100 /* If non-null, a function to call with WINDOW as argument when the function
101 window_new_screen_size () has deleted WINDOW. */
102 VFunction *window_deletion_notifier = NULL;
105 window_new_screen_size (width, height)
108 register WINDOW *win;
109 int delta_height, delta_each, delta_leftover;
112 /* If no change, do nothing. */
113 if (width == the_screen->width && height == the_screen->height)
116 /* If the new window height is too small, make it be zero. */
117 if (height < (WINDOW_MIN_SIZE + the_echo_area->height))
122 /* Find out how many windows will change. */
123 for (numwins = 0, win = windows; win; win = win->next, numwins++);
125 /* See if some windows will need to be deleted. This is the case if
126 the screen is getting smaller, and the available space divided by
127 the number of windows is less than WINDOW_MIN_SIZE. In that case,
128 delete some windows and try again until there is either enough
129 space to divy up among the windows, or until there is only one
131 while ((height - echo_area_required) / numwins <= WINDOW_MIN_SIZE)
133 /* If only one window, make the size of it be zero, and return
138 maybe_free (windows->line_starts);
139 windows->line_starts = NULL;
140 windows->line_count = 0;
144 /* If we have some temporary windows, delete one of them. */
145 for (win = windows; win; win = win->next)
146 if (win->flags & W_TempWindow)
149 /* Otherwise, delete the first window, and try again. */
153 if (window_deletion_notifier)
154 (*window_deletion_notifier) (win);
156 window_delete_window (win);
160 /* The screen has changed height and width. */
161 delta_height = height - the_screen->height; /* This is how much. */
162 the_screen->height = height; /* This is the new height. */
163 the_screen->width = width; /* This is the new width. */
165 /* Set the start of the echo area. */
166 the_echo_area->first_row = height - the_echo_area->height;
167 the_echo_area->width = width;
169 /* Check to see if the screen can really be changed this way. */
170 if ((!windows->next) && ((windows->height == 0) && (delta_height < 0)))
173 /* Divide the change in height among the available windows. */
174 delta_each = delta_height / numwins;
175 delta_leftover = delta_height - (delta_each * numwins);
177 /* Change the height of each window in the chain by delta_each. Change
178 the height of the last window in the chain by delta_each and by the
179 leftover amount of change. Change the width of each window to be
181 for (win = windows; win; win = win->next)
183 if ((win->width != width) && ((win->flags & W_InhibitMode) == 0))
186 maybe_free (win->modeline);
187 win->modeline = xmalloc (1 + width);
190 win->height += delta_each;
192 /* If the previous height of this window was zero, it was the only
193 window, and it was not visible. Thus we need to compensate for
195 if (win->height == delta_each)
196 win->height -= (1 + the_echo_area->height);
198 /* If this is not the first window in the chain, then change the
199 first row of it. We cannot just add delta_each to the first row,
200 since this window's first row is the sum of the collective increases
201 that have gone before it. So we just add one to the location of the
202 previous window's modeline. */
204 win->first_row = (win->prev->first_row + win->prev->height) + 1;
206 /* The last window in the chain gets the extra space (or shrinkage). */
208 win->height += delta_leftover;
211 recalculate_line_starts (win);
213 win->flags |= W_UpdateWindow;
216 /* If the screen got smaller, check over the windows just shrunk to
217 keep them within bounds. Some of the windows may have gotten smaller
218 than WINDOW_MIN_HEIGHT in which case some of the other windows are
219 larger than the available display space in the screen. Because of our
220 intial test above, we know that there is enough space for all of the
222 if ((delta_each < 0) && ((windows->height != 0) && windows->next))
226 avail = the_screen->height - (numwins + the_echo_area->height);
231 if ((win->height < WINDOW_MIN_HEIGHT) ||
232 (win->height > avail))
236 /* Split the space among the available windows. */
237 delta_each = avail / numwins;
238 delta_leftover = avail - (delta_each * numwins);
240 for (win = windows; win; win = win->next)
245 (win->prev->first_row + win->prev->height) + 1;
246 win->height = delta_each;
249 /* Give the leftover space (if any) to the last window. */
250 lastwin->height += delta_leftover;
259 /* Make a new window showing NODE, and return that window structure.
260 If NODE is passed as NULL, then show the node showing in the active
261 window. If the window could not be made return a NULL pointer. The
262 active window is not changed.*/
264 window_make_window (node)
270 node = active_window->node;
272 /* If there isn't enough room to make another window, return now. */
273 if ((active_window->height / 2) < WINDOW_MIN_SIZE)
276 /* Make and initialize the new window.
277 The fudging about with -1 and +1 is because the following window in the
278 chain cannot start at window->height, since that is where the modeline
279 for the previous window is displayed. The inverse adjustment is made
280 in window_delete_window (). */
281 window = xmalloc (sizeof (WINDOW));
282 window->width = the_screen->width;
283 window->height = (active_window->height / 2) - 1;
284 #if defined (SPLIT_BEFORE_ACTIVE)
285 window->first_row = active_window->first_row;
287 window->first_row = active_window->first_row +
288 (active_window->height - window->height);
290 window->keymap = info_keymap;
291 window->goal_column = -1;
292 window->modeline = xmalloc (1 + window->width);
293 window->line_starts = NULL;
294 window->flags = W_UpdateWindow | W_WindowVisible;
295 window_set_node_of_window (window, node);
297 /* Adjust the height of the old active window. */
298 active_window->height -= (window->height + 1);
299 #if defined (SPLIT_BEFORE_ACTIVE)
300 active_window->first_row += (window->height + 1);
302 active_window->flags |= W_UpdateWindow;
304 /* Readjust the new and old windows so that their modelines and contents
305 will be displayed correctly. */
307 /* We don't have to do this for WINDOW since window_set_node_of_window ()
309 window_adjust_pagetop (window);
310 window_make_modeline (window);
313 /* We do have to readjust the existing active window. */
314 window_adjust_pagetop (active_window);
315 window_make_modeline (active_window);
317 #if defined (SPLIT_BEFORE_ACTIVE)
318 /* This window is just before the active one. The active window gets
319 bumped down one. The active window is not changed. */
320 window->next = active_window;
322 window->prev = active_window->prev;
323 active_window->prev = window;
326 window->prev->next = window;
330 /* This window is just after the active one. Which window is active is
332 window->prev = active_window;
333 window->next = active_window->next;
334 active_window->next = window;
336 window->next->prev = window;
337 #endif /* !SPLIT_BEFORE_ACTIVE */
341 /* These useful macros make it possible to read the code in
342 window_change_window_height (). */
343 #define grow_me_shrinking_next(me, next, diff) \
345 me->height += diff; \
346 next->height -= diff; \
347 next->first_row += diff; \
348 window_adjust_pagetop (next); \
351 #define grow_me_shrinking_prev(me, prev, diff) \
353 me->height += diff; \
354 prev->height -= diff; \
355 me->first_row -=diff; \
356 window_adjust_pagetop (prev); \
359 #define shrink_me_growing_next(me, next, diff) \
361 me->height -= diff; \
362 next->height += diff; \
363 next->first_row -= diff; \
364 window_adjust_pagetop (next); \
367 #define shrink_me_growing_prev(me, prev, diff) \
369 me->height -= diff; \
370 prev->height += diff; \
371 me->first_row += diff; \
372 window_adjust_pagetop (prev); \
375 /* Change the height of WINDOW by AMOUNT. This also automagically adjusts
376 the previous and next windows in the chain. If there is only one user
377 window, then no change takes place. */
379 window_change_window_height (window, amount)
383 register WINDOW *win, *prev, *next;
385 /* If there is only one window, or if the amount of change is zero,
386 return immediately. */
387 if (!windows->next || amount == 0)
390 /* Find this window in our chain. */
391 for (win = windows; win; win = win->next)
395 /* If the window is isolated (i.e., doesn't appear in our window list,
400 /* Change the height of this window by AMOUNT, if that is possible.
401 It can be impossible if there isn't enough available room on the
402 screen, or if the resultant window would be too small. */
407 /* WINDOW decreasing in size? */
410 int abs_amount = -amount; /* It is easier to deal with this way. */
412 /* If the resultant window would be too small, stop here. */
413 if ((window->height - abs_amount) < WINDOW_MIN_HEIGHT)
416 /* If we have two neighboring windows, choose the smaller one to get
420 if (prev->height < next->height)
421 shrink_me_growing_prev (window, prev, abs_amount);
423 shrink_me_growing_next (window, next, abs_amount);
426 shrink_me_growing_next (window, next, abs_amount);
428 shrink_me_growing_prev (window, prev, abs_amount);
431 /* WINDOW increasing in size? */
434 int total_avail, next_avail = 0, prev_avail = 0;
437 next_avail = next->height - WINDOW_MIN_SIZE;
440 prev_avail = prev->height - WINDOW_MIN_SIZE;
442 total_avail = next_avail + prev_avail;
444 /* If there isn't enough space available to grow this window, give up. */
445 if (amount > total_avail)
448 /* If there aren't two neighboring windows, or if one of the neighbors
449 is larger than the other one by at least AMOUNT, grow that one. */
450 if ((next && !prev) || ((next_avail - amount) >= prev_avail))
451 grow_me_shrinking_next (window, next, amount);
452 else if ((prev && !next) || ((prev_avail - amount) >= next_avail))
453 grow_me_shrinking_prev (window, prev, amount);
458 /* This window has two neighbors. They both must be shrunk in to
459 make enough space for WINDOW to grow. Make them both the same
461 if (prev_avail > next_avail)
463 change = prev_avail - next_avail;
464 grow_me_shrinking_prev (window, prev, change);
469 change = next_avail - prev_avail;
470 grow_me_shrinking_next (window, next, change);
474 /* Both neighbors are the same size. Split the difference in
475 AMOUNT between them. */
481 /* Odd numbers grow next, even grow prev. */
493 window_adjust_pagetop (prev);
494 window_adjust_pagetop (next);
498 prev->flags |= W_UpdateWindow;
501 next->flags |= W_UpdateWindow;
503 window->flags |= W_UpdateWindow;
504 window_adjust_pagetop (window);
507 /* Tile all of the windows currently displayed in the global variable
508 WINDOWS. If argument STYLE is TILE_INTERNALS, tile windows displaying
509 internal nodes as well, otherwise do not change the height of such
512 window_tile_windows (style)
515 WINDOW *win, *last_adjusted;
516 int numwins, avail, per_win_height, leftover;
520 do_internals = (style == TILE_INTERNALS);
522 for (win = windows; win; win = win->next)
523 if (do_internals || !win->node ||
524 (win->node->flags & N_IsInternal) == 0)
526 avail += win->height;
530 if (numwins <= 1 || !the_screen->height)
533 /* Find the size for each window. Divide the size of the usable portion
534 of the screen by the number of windows. */
535 per_win_height = avail / numwins;
536 leftover = avail - (per_win_height * numwins);
538 last_adjusted = NULL;
539 for (win = windows; win; win = win->next)
541 if (do_internals || !win->node ||
542 (win->node->flags & N_IsInternal) == 0)
545 win->height = per_win_height;
550 last_adjusted->height += leftover;
552 /* Readjust the first_row of every window in the chain. */
553 for (win = windows; win; win = win->next)
556 win->first_row = win->prev->first_row + win->prev->height + 1;
558 window_adjust_pagetop (win);
559 win->flags |= W_UpdateWindow;
563 /* Toggle the state of line wrapping in WINDOW. This can do a bit of fancy
566 window_toggle_wrap (window)
569 if (window->flags & W_NoWrap)
570 window->flags &= ~W_NoWrap;
572 window->flags |= W_NoWrap;
574 if (window != the_echo_area)
577 int old_lines, old_pagetop;
579 old_starts = window->line_starts;
580 old_lines = window->line_count;
581 old_pagetop = window->pagetop;
583 calculate_line_starts (window);
585 /* Make sure that point appears within this window. */
586 window_adjust_pagetop (window);
588 /* If the pagetop hasn't changed maybe we can do some scrolling now
589 to speed up the display. Many of the line starts will be the same,
590 so scrolling here is a very good optimization.*/
591 if (old_pagetop == window->pagetop)
592 display_scroll_line_starts
593 (window, old_pagetop, old_starts, old_lines);
594 maybe_free (old_starts);
596 window->flags |= W_UpdateWindow;
599 /* Set WINDOW to display NODE. */
601 window_set_node_of_window (window, node)
608 recalculate_line_starts (window);
609 window->flags |= W_UpdateWindow;
610 /* The display_pos member is nonzero if we're displaying an anchor. */
611 window->point = node ? node->display_pos : 0;
612 window_adjust_pagetop (window);
613 window_make_modeline (window);
616 /* Delete WINDOW from the list of known windows. If this window was the
617 active window, make the next window in the chain be the active window.
618 If the active window is the next or previous window, choose that window
619 as the recipient of the extra space. Otherwise, prefer the next window. */
621 window_delete_window (window)
624 WINDOW *next, *prev, *window_to_fix;
629 /* You cannot delete the only window or a permanent window. */
630 if ((!next && !prev) || (window->flags & W_WindowIsPerm))
641 if (window->line_starts)
642 free (window->line_starts);
644 if (window->modeline)
645 free (window->modeline);
647 if (window == active_window)
649 /* If there isn't a next window, then there must be a previous one,
650 since we cannot delete the last window. If there is a next window,
651 prefer to use that as the active window. */
653 active_window = next;
655 active_window = prev;
658 if (next && active_window == next)
659 window_to_fix = next;
660 else if (prev && active_window == prev)
661 window_to_fix = prev;
663 window_to_fix = next;
665 window_to_fix = prev;
667 window_to_fix = windows;
669 if (window_to_fix->first_row > window->first_row)
673 /* Try to adjust the visible part of the node so that as little
674 text as possible has to move. */
675 diff = window_to_fix->first_row - window->first_row;
676 window_to_fix->first_row = window->first_row;
678 window_to_fix->pagetop -= diff;
679 if (window_to_fix->pagetop < 0)
680 window_to_fix->pagetop = 0;
683 /* The `+ 1' is to offset the difference between the first_row locations.
684 See the code in window_make_window (). */
685 window_to_fix->height += window->height + 1;
686 window_to_fix->flags |= W_UpdateWindow;
691 /* For every window in CHAIN, set the flags member to have FLAG set. */
693 window_mark_chain (chain, flag)
697 register WINDOW *win;
699 for (win = chain; win; win = win->next)
703 /* For every window in CHAIN, clear the flags member of FLAG. */
705 window_unmark_chain (chain, flag)
709 register WINDOW *win;
711 for (win = chain; win; win = win->next)
715 /* Return the number of characters it takes to display CHARACTER on the
718 character_width (character, hpos)
721 int printable_limit = 127;
725 printable_limit = 255;
727 if (character > printable_limit)
729 else if (iscntrl (character))
735 width = the_screen->width - hpos;
738 width = ((hpos + 8) & 0xf8) - hpos;
744 else if (character == DEL)
750 /* Return the number of characters it takes to display STRING on the screen
753 string_width (string, hpos)
757 register int i, width, this_char_width;
759 for (width = 0, i = 0; string[i]; i++)
761 /* Support ANSI escape sequences for -R. */
763 && string[i] == '\033'
764 && string[i+1] == '['
765 && isdigit (string[i+2])
766 && (string[i+3] == 'm'
767 || (isdigit (string[i+3]) && string[i+4] == 'm')))
769 while (string[i] != 'm')
774 this_char_width = character_width (string[i], hpos);
775 width += this_char_width;
776 hpos += this_char_width;
781 /* Quickly guess the approximate number of lines that NODE would
782 take to display. This really only counts carriage returns. */
784 window_physical_lines (node)
787 register int i, lines;
793 contents = node->contents;
794 for (i = 0, lines = 1; i < node->nodelen; i++)
795 if (contents[i] == '\n')
801 /* Calculate a list of line starts for the node belonging to WINDOW. The line
802 starts are pointers to the actual text within WINDOW->NODE. */
804 calculate_line_starts (window)
807 register int i, hpos;
808 char **line_starts = NULL;
809 int line_starts_index = 0, line_starts_slots = 0;
813 window->line_starts = NULL;
814 window->line_count = 0;
820 /* Grovel the node starting at the top, and for each line calculate the
821 width of the characters appearing in that line. Add each line start
827 while (i < node->nodelen)
829 char *line = node->contents + i;
830 unsigned int cwidth, c;
832 add_pointer_to_array (line, line_starts_index, line_starts,
833 line_starts_slots, 100, char *);
842 /* The cast to unsigned char is for 8-bit characters, which
843 could be passed as negative integers to character_width
844 and wreak havoc on some naive implementations of iscntrl. */
845 c = (unsigned char) node->contents[i];
847 /* Support ANSI escape sequences for -R. */
850 && node->contents[i+1] == '['
851 && isdigit (node->contents[i+2]))
853 if (node->contents[i+3] == 'm')
858 else if (isdigit (node->contents[i+3])
859 && node->contents[i+4] == 'm')
865 cwidth = character_width (c, hpos);
868 cwidth = character_width (c, hpos);
870 /* If this character fits within this line, just do the next one. */
871 if ((hpos + cwidth) < window->width)
879 /* If this character would position the cursor at the start of
880 the next printed screen line, then do the next line. */
881 if (c == '\n' || c == '\r' || c == '\t')
889 /* This character passes the window width border. Postion
890 the cursor after the printed character, but remember this
891 line start as where this character is. A bit tricky. */
893 /* If this window doesn't wrap lines, proceed to the next
894 physical line here. */
895 if (window->flags & W_NoWrap)
898 while (i < node->nodelen && node->contents[i] != '\n')
901 if (node->contents[i] == '\n')
906 hpos = the_screen->width - hpos;
914 window->line_starts = line_starts;
915 window->line_count = line_starts_index;
918 /* Given WINDOW, recalculate the line starts for the node it displays. */
920 recalculate_line_starts (window)
923 maybe_free (window->line_starts);
924 calculate_line_starts (window);
927 /* Global variable control redisplay of scrolled windows. If non-zero, it
928 is the desired number of lines to scroll the window in order to make
929 point visible. A user might set this to 1 for smooth scrolling. If
930 set to zero, the line containing point is centered within the window. */
931 int window_scroll_step = 0;
933 /* Adjust the pagetop of WINDOW such that the cursor point will be visible. */
935 window_adjust_pagetop (window)
938 register int line = 0;
944 contents = window->node->contents;
946 /* Find the first printed line start which is after WINDOW->point. */
947 for (line = 0; line < window->line_count; line++)
951 line_start = window->line_starts[line];
953 if ((line_start - contents) > window->point)
957 /* The line index preceding the line start which is past point is the
958 one containing point. */
961 /* If this line appears in the current displayable page, do nothing.
962 Otherwise, adjust the top of the page to make this line visible. */
963 if ((line < window->pagetop) ||
964 (line - window->pagetop > (window->height - 1)))
966 /* The user-settable variable "scroll-step" is used to attempt
967 to make point visible, iff it is non-zero. If that variable
968 is zero, then the line containing point is centered within
970 if (window_scroll_step < window->height)
972 if ((line < window->pagetop) &&
973 ((window->pagetop - window_scroll_step) <= line))
974 window->pagetop -= window_scroll_step;
975 else if ((line - window->pagetop > (window->height - 1)) &&
976 ((line - (window->pagetop + window_scroll_step)
978 window->pagetop += window_scroll_step;
980 window->pagetop = line - ((window->height - 1) / 2);
983 window->pagetop = line - ((window->height - 1) / 2);
985 if (window->pagetop < 0)
987 window->flags |= W_UpdateWindow;
991 /* Return the index of the line containing point. */
993 window_line_of_point (window)
996 register int i, start = 0;
998 /* Try to optimize. Check to see if point is past the pagetop for
999 this window, and if so, start searching forward from there. */
1000 if ((window->pagetop > -1 && window->pagetop < window->line_count) &&
1001 (window->line_starts[window->pagetop] - window->node->contents)
1003 start = window->pagetop;
1005 for (i = start; i < window->line_count; i++)
1007 if ((window->line_starts[i] - window->node->contents) > window->point)
1014 /* Get and return the goal column for this window. */
1016 window_get_goal_column (window)
1022 if (window->goal_column != -1)
1023 return (window->goal_column);
1025 /* Okay, do the work. Find the printed offset of the cursor
1027 return (window_get_cursor_column (window));
1030 /* Get and return the printed column offset of the cursor in this window. */
1032 window_get_cursor_column (window)
1038 i = window_line_of_point (window);
1043 line = window->line_starts[i];
1044 end = window->point - (line - window->node->contents);
1046 for (hpos = 0, i = 0; i < end; i++)
1048 /* Support ANSI escape sequences for -R. */
1050 && line[i] == '\033'
1052 && isdigit (line[i+2]))
1054 if (line[i+3] == 'm')
1056 else if (isdigit (line[i+3]) && line[i+4] == 'm')
1059 hpos += character_width (line[i], hpos);
1062 hpos += character_width (line[i], hpos);
1068 /* Count the number of characters in LINE that precede the printed column
1071 window_chars_to_goal (line, goal)
1075 register int i, check, hpos;
1077 for (hpos = 0, i = 0; line[i] != '\n'; i++)
1079 /* Support ANSI escape sequences for -R. */
1081 && line[i] == '\033'
1083 && isdigit (line[i+2])
1084 && (line[i+3] == 'm'
1085 || (isdigit (line[i+3]) && line[i+4] == 'm')))
1086 while (line[i] != 'm')
1089 check = hpos + character_width (line[i], hpos);
1099 /* Create a modeline for WINDOW, and store it in window->modeline. */
1101 window_make_modeline (window)
1106 char location_indicator[4];
1107 int lines_remaining;
1109 /* Only make modelines for those windows which have one. */
1110 if (window->flags & W_InhibitMode)
1113 /* Find the number of lines actually displayed in this window. */
1114 lines_remaining = window->line_count - window->pagetop;
1116 if (window->pagetop == 0)
1118 if (lines_remaining <= window->height)
1119 strcpy (location_indicator, "All");
1121 strcpy (location_indicator, "Top");
1125 if (lines_remaining <= window->height)
1126 strcpy (location_indicator, "Bot");
1132 pt = (float)window->pagetop;
1133 lc = (float)window->line_count;
1135 percentage = 100 * (pt / lc);
1137 sprintf (location_indicator, "%2d%%", percentage);
1141 /* Calculate the maximum size of the information to stick in MODELINE. */
1143 int modeline_len = 0;
1144 char *parent = NULL, *filename = "*no file*";
1145 char *nodename = "*no node*";
1146 char *update_message = NULL;
1147 NODE *node = window->node;
1152 nodename = node->nodename;
1156 parent = filename_non_directory (node->parent);
1157 modeline_len += strlen ("Subfile: ") + strlen (node->filename);
1161 filename = filename_non_directory (node->filename);
1163 if (node->flags & N_UpdateTags)
1164 update_message = _("--*** Tags out of Date ***");
1168 modeline_len += strlen (update_message);
1169 modeline_len += strlen (filename);
1170 modeline_len += strlen (nodename);
1171 modeline_len += 4; /* strlen (location_indicator). */
1173 /* 10 for the decimal representation of the number of lines in this
1174 node, and the remainder of the text that can appear in the line. */
1175 modeline_len += 10 + strlen (_("-----Info: (), lines ----, "));
1176 modeline_len += window->width;
1178 modeline = xmalloc (1 + modeline_len);
1180 /* Special internal windows have no filename. */
1181 if (!parent && !*filename)
1182 sprintf (modeline, _("-%s---Info: %s, %d lines --%s--"),
1183 (window->flags & W_NoWrap) ? "$" : "-",
1184 nodename, window->line_count, location_indicator);
1186 sprintf (modeline, _("-%s%s-Info: (%s)%s, %d lines --%s--"),
1187 (window->flags & W_NoWrap) ? "$" : "-",
1188 (node && (node->flags & N_IsCompressed)) ? "zz" : "--",
1189 parent ? parent : filename,
1190 nodename, window->line_count, location_indicator);
1193 sprintf (modeline + strlen (modeline), _(" Subfile: %s"), filename);
1196 sprintf (modeline + strlen (modeline), "%s", update_message);
1198 i = strlen (modeline);
1200 if (i >= window->width)
1201 modeline[window->width] = '\0';
1204 while (i < window->width)
1205 modeline[i++] = '-';
1209 strcpy (window->modeline, modeline);
1214 /* Make WINDOW start displaying at PERCENT percentage of its node. */
1216 window_goto_percentage (window, percent)
1226 (int) ((float)window->line_count * ((float)percent / 100.0));
1228 window->pagetop = desired_line;
1230 window->line_starts[window->pagetop] - window->node->contents;
1231 window->flags |= W_UpdateWindow;
1232 window_make_modeline (window);
1235 /* Get the state of WINDOW, and save it in STATE. */
1237 window_get_state (window, state)
1239 WINDOW_STATE *state;
1241 state->node = window->node;
1242 state->pagetop = window->pagetop;
1243 state->point = window->point;
1246 /* Set the node, pagetop, and point of WINDOW. */
1248 window_set_state (window, state)
1250 WINDOW_STATE *state;
1252 if (window->node != state->node)
1253 window_set_node_of_window (window, state->node);
1254 window->pagetop = state->pagetop;
1255 window->point = state->point;
1259 /* Manipulating home-made nodes. */
1261 /* A place to buffer echo area messages. */
1262 static NODE *echo_area_node = NULL;
1264 /* Make the node of the_echo_area be an empty one. */
1270 maybe_free (echo_area_node->contents);
1271 free (echo_area_node);
1274 echo_area_node = NULL;
1275 window_set_node_of_window (the_echo_area, echo_area_node);
1278 /* Clear the echo area, removing any message that is already present.
1279 The echo area is cleared immediately. */
1281 window_clear_echo_area ()
1284 display_update_one_window (the_echo_area);
1287 /* Make a message appear in the echo area, built from FORMAT, ARG1 and ARG2.
1288 The arguments are treated similar to printf () arguments, but not all of
1289 printf () hair is present. The message appears immediately. If there was
1290 already a message appearing in the echo area, it is removed. */
1292 window_message_in_echo_area (format, arg1, arg2)
1297 echo_area_node = build_message_node (format, arg1, arg2);
1298 window_set_node_of_window (the_echo_area, echo_area_node);
1299 display_update_one_window (the_echo_area);
1302 /* Place a temporary message in the echo area built from FORMAT, ARG1
1303 and ARG2. The message appears immediately, but does not destroy
1304 any existing message. A future call to unmessage_in_echo_area ()
1305 restores the old contents. */
1306 static NODE **old_echo_area_nodes = NULL;
1307 static int old_echo_area_nodes_index = 0;
1308 static int old_echo_area_nodes_slots = 0;
1311 message_in_echo_area (format, arg1, arg2)
1317 add_pointer_to_array (echo_area_node, old_echo_area_nodes_index,
1318 old_echo_area_nodes, old_echo_area_nodes_slots,
1321 echo_area_node = NULL;
1322 window_message_in_echo_area (format, arg1, arg2);
1326 unmessage_in_echo_area ()
1330 if (old_echo_area_nodes_index)
1331 echo_area_node = old_echo_area_nodes[--old_echo_area_nodes_index];
1333 window_set_node_of_window (the_echo_area, echo_area_node);
1334 display_update_one_window (the_echo_area);
1337 /* A place to build a message. */
1338 static char *message_buffer = NULL;
1339 static int message_buffer_index = 0;
1340 static int message_buffer_size = 0;
1342 /* Ensure that there is enough space to stuff LENGTH characters into
1345 message_buffer_resize (length)
1348 if (!message_buffer)
1350 message_buffer_size = length + 1;
1351 message_buffer = xmalloc (message_buffer_size);
1352 message_buffer_index = 0;
1355 while (message_buffer_size <= message_buffer_index + length)
1356 message_buffer = (char *)
1357 xrealloc (message_buffer,
1358 message_buffer_size += 100 + (2 * length));
1361 /* Format MESSAGE_BUFFER with the results of printing FORMAT with ARG1 and
1364 build_message_buffer (format, arg1, arg2, arg3)
1366 void *arg1, *arg2, *arg3;
1368 register int i, len;
1376 len = strlen (format);
1378 message_buffer_resize (len);
1380 for (i = 0; format[i]; i++)
1382 if (format[i] != '%')
1384 message_buffer[message_buffer_index++] = format[i];
1390 char *fmt_start = format + i;
1392 int fmt_len, formatted_len;
1397 while (format[i] && strchr ("-. +0123456789", format[i]))
1405 /* position parameter parameter */
1406 /* better to use bprintf from bfox's metahtml? */
1407 arg_index = atoi(fmt_start + 1) - 1;
1416 fmt_len = format + i - fmt_start + 1;
1417 fmt = (char *) xmalloc (fmt_len + 1);
1418 strncpy (fmt, fmt_start, fmt_len);
1419 fmt[fmt_len] = '\0';
1422 /* removed positioned parameter */
1424 for (p = fmt + 1; *p && *p != '$'; p++) {
1427 strcpy(fmt + 1, p + 1);
1430 /* If we have "%-98s", maybe 98 calls for a longer string. */
1435 for (j = fmt_len - 2; j >= 0; j--)
1436 if (isdigit (fmt[j]) || fmt[j] == '$')
1439 formatted_len = atoi (fmt + j);
1442 formatted_len = c == 's' ? 0 : 1; /* %s can produce empty string */
1446 case '%': /* Insert a percent sign. */
1447 message_buffer_resize (len + formatted_len);
1449 (message_buffer + message_buffer_index, fmt, "%");
1450 message_buffer_index += formatted_len;
1453 case 's': /* Insert the current arg as a string. */
1458 string = (char *)args[arg_index++];
1459 string_len = strlen (string);
1461 if (formatted_len > string_len)
1462 string_len = formatted_len;
1463 message_buffer_resize (len + string_len);
1465 (message_buffer + message_buffer_index, fmt, string);
1466 message_buffer_index += string_len;
1470 case 'd': /* Insert the current arg as an integer. */
1475 long_val = (long)args[arg_index++];
1476 integer = (int)long_val;
1478 message_buffer_resize (len + formatted_len > 32
1479 ? formatted_len : 32);
1481 (message_buffer + message_buffer_index, fmt, integer);
1482 message_buffer_index = strlen (message_buffer);
1486 case 'c': /* Insert the current arg as a character. */
1491 long_val = (long)args[arg_index++];
1492 character = (int)long_val;
1494 message_buffer_resize (len + formatted_len);
1496 (message_buffer + message_buffer_index, fmt, character);
1497 message_buffer_index += formatted_len;
1507 message_buffer[message_buffer_index] = '\0';
1510 /* Build a new node which has FORMAT printed with ARG1 and ARG2 as the
1513 build_message_node (format, arg1, arg2)
1519 message_buffer_index = 0;
1520 build_message_buffer (format, arg1, arg2, 0);
1522 node = message_buffer_to_node ();
1526 /* Convert the contents of the message buffer to a node. */
1528 message_buffer_to_node ()
1532 node = xmalloc (sizeof (NODE));
1533 node->filename = NULL;
1534 node->parent = NULL;
1535 node->nodename = NULL;
1537 node->display_pos =0;
1539 /* Make sure that this buffer ends with a newline. */
1540 node->nodelen = 1 + strlen (message_buffer);
1541 node->contents = xmalloc (1 + node->nodelen);
1542 strcpy (node->contents, message_buffer);
1543 node->contents[node->nodelen - 1] = '\n';
1544 node->contents[node->nodelen] = '\0';
1548 /* Useful functions can be called from outside of window.c. */
1550 initialize_message_buffer ()
1552 message_buffer_index = 0;
1555 /* Print FORMAT with ARG1,2 to the end of the current message buffer. */
1557 printf_to_message_buffer (format, arg1, arg2, arg3)
1559 void *arg1, *arg2, *arg3;
1561 build_message_buffer (format, arg1, arg2, arg3);
1564 /* Return the current horizontal position of the "cursor" on the most
1565 recently output message buffer line. */
1567 message_buffer_length_this_line ()
1571 if (!message_buffer_index)
1574 for (i = message_buffer_index; i && message_buffer[i - 1] != '\n'; i--);
1576 return (string_width (message_buffer + i, 0));
1579 /* Pad STRING to COUNT characters by inserting blanks. */
1581 pad_to (count, string)
1587 i = strlen (string);