1 /* window.c -- windows in Info.
2 $Id: window.c,v 1.17 2008/09/13 10:01:31 gray Exp $
4 Copyright (C) 1993, 1997, 1998, 2001, 2002, 2003, 2004, 2007, 2008
5 Free Software Foundation, Inc.
7 This program is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>.
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 /* Show malformed multibyte sequences */
50 int show_malformed_multibyte_p = 0;
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 (int width, int height)
58 the_screen = xmalloc (sizeof (WINDOW));
59 the_echo_area = xmalloc (sizeof (WINDOW));
60 windows = xmalloc (sizeof (WINDOW));
61 active_window = windows;
63 zero_mem (the_screen, sizeof (WINDOW));
64 zero_mem (the_echo_area, sizeof (WINDOW));
65 zero_mem (active_window, sizeof (WINDOW));
67 /* None of these windows has a goal column yet. */
68 the_echo_area->goal_column = -1;
69 active_window->goal_column = -1;
70 the_screen->goal_column = -1;
72 /* The active and echo_area windows are visible.
73 The echo_area is permanent.
74 The screen is permanent. */
75 active_window->flags = W_WindowVisible;
76 the_echo_area->flags = W_WindowIsPerm | W_InhibitMode | W_WindowVisible;
77 the_screen->flags = W_WindowIsPerm;
79 /* The height of the echo area never changes. It is statically set right
80 here, and it must be at least 1 line for display. The size of the
81 initial window cannot be the same size as the screen, since the screen
82 includes the echo area. So, we make the height of the initial window
83 equal to the screen's displayable region minus the height of the echo
85 the_echo_area->height = ECHO_AREA_HEIGHT;
86 active_window->height = the_screen->height - 1 - the_echo_area->height;
87 window_new_screen_size (width, height);
89 /* The echo area uses a different keymap than normal info windows. */
90 the_echo_area->keymap = echo_area_keymap;
91 active_window->keymap = info_keymap;
94 /* Given that the size of the screen has changed to WIDTH and HEIGHT
95 from whatever it was before (found in the_screen->height, ->width),
96 change the size (and possibly location) of each window in the screen.
97 If a window would become too small, call the function DELETER on it,
98 after deleting the window from our chain of windows. If DELETER is NULL,
99 nothing extra is done. The last window can never be deleted, but it can
102 /* If non-null, a function to call with WINDOW as argument when the function
103 window_new_screen_size () has deleted WINDOW. */
104 VFunction *window_deletion_notifier = NULL;
107 window_new_screen_size (int width, int height)
109 register WINDOW *win;
110 int delta_height, delta_each, delta_leftover;
113 /* If no change, do nothing. */
114 if (width == the_screen->width && height == the_screen->height)
117 /* If the new window height is too small, make it be zero. */
118 if (height < (WINDOW_MIN_SIZE + the_echo_area->height))
123 /* Find out how many windows will change. */
124 for (numwins = 0, win = windows; win; win = win->next, numwins++);
126 /* See if some windows will need to be deleted. This is the case if
127 the screen is getting smaller, and the available space divided by
128 the number of windows is less than WINDOW_MIN_SIZE. In that case,
129 delete some windows and try again until there is either enough
130 space to divy up among the windows, or until there is only one
132 while ((height - echo_area_required) / numwins <= WINDOW_MIN_SIZE)
134 /* If only one window, make the size of it be zero, and return
139 maybe_free (windows->line_starts);
140 windows->line_starts = NULL;
141 windows->line_count = 0;
145 /* If we have some temporary windows, delete one of them. */
146 for (win = windows; win; win = win->next)
147 if (win->flags & W_TempWindow)
150 /* Otherwise, delete the first window, and try again. */
154 if (window_deletion_notifier)
155 (*window_deletion_notifier) (win);
157 window_delete_window (win);
161 /* The screen has changed height and width. */
162 delta_height = height - the_screen->height; /* This is how much. */
163 the_screen->height = height; /* This is the new height. */
164 the_screen->width = width; /* This is the new width. */
166 /* Set the start of the echo area. */
167 the_echo_area->first_row = height - the_echo_area->height;
168 the_echo_area->width = width;
170 /* Check to see if the screen can really be changed this way. */
171 if ((!windows->next) && ((windows->height == 0) && (delta_height < 0)))
174 /* Divide the change in height among the available windows. */
175 delta_each = delta_height / numwins;
176 delta_leftover = delta_height - (delta_each * numwins);
178 /* Change the height of each window in the chain by delta_each. Change
179 the height of the last window in the chain by delta_each and by the
180 leftover amount of change. Change the width of each window to be
182 for (win = windows; win; win = win->next)
184 if ((win->width != width) && ((win->flags & W_InhibitMode) == 0))
187 maybe_free (win->modeline);
188 win->modeline = xmalloc (1 + width);
191 win->height += delta_each;
193 /* If the previous height of this window was zero, it was the only
194 window, and it was not visible. Thus we need to compensate for
196 if (win->height == delta_each)
197 win->height -= (1 + the_echo_area->height);
199 /* If this is not the first window in the chain, then change the
200 first row of it. We cannot just add delta_each to the first row,
201 since this window's first row is the sum of the collective increases
202 that have gone before it. So we just add one to the location of the
203 previous window's modeline. */
205 win->first_row = (win->prev->first_row + win->prev->height) + 1;
207 /* The last window in the chain gets the extra space (or shrinkage). */
209 win->height += delta_leftover;
212 recalculate_line_starts (win);
214 win->flags |= W_UpdateWindow;
217 /* If the screen got smaller, check over the windows just shrunk to
218 keep them within bounds. Some of the windows may have gotten smaller
219 than WINDOW_MIN_HEIGHT in which case some of the other windows are
220 larger than the available display space in the screen. Because of our
221 intial test above, we know that there is enough space for all of the
223 if ((delta_each < 0) && ((windows->height != 0) && windows->next))
227 avail = the_screen->height - (numwins + the_echo_area->height);
232 if ((win->height < WINDOW_MIN_HEIGHT) ||
233 (win->height > avail))
235 WINDOW *lastwin = NULL;
237 /* Split the space among the available windows. */
238 delta_each = avail / numwins;
239 delta_leftover = avail - (delta_each * numwins);
241 for (win = windows; win; win = win->next)
246 (win->prev->first_row + win->prev->height) + 1;
247 win->height = delta_each;
250 /* Give the leftover space (if any) to the last window. */
251 lastwin->height += delta_leftover;
259 /* One more loop. If any heights or widths have become negative,
260 set them to zero. This can apparently happen with resizing down to
261 very small sizes. Sadly, it is not apparent to me where in the
262 above calculations it goes wrong. */
263 for (win = windows; win; win = win->next)
273 /* Make a new window showing NODE, and return that window structure.
274 If NODE is passed as NULL, then show the node showing in the active
275 window. If the window could not be made return a NULL pointer. The
276 active window is not changed.*/
278 window_make_window (NODE *node)
283 node = active_window->node;
285 /* If there isn't enough room to make another window, return now. */
286 if ((active_window->height / 2) < WINDOW_MIN_SIZE)
289 /* Make and initialize the new window.
290 The fudging about with -1 and +1 is because the following window in the
291 chain cannot start at window->height, since that is where the modeline
292 for the previous window is displayed. The inverse adjustment is made
293 in window_delete_window (). */
294 window = xmalloc (sizeof (WINDOW));
295 window->width = the_screen->width;
296 window->height = (active_window->height / 2) - 1;
297 #if defined (SPLIT_BEFORE_ACTIVE)
298 window->first_row = active_window->first_row;
300 window->first_row = active_window->first_row +
301 (active_window->height - window->height);
303 window->keymap = info_keymap;
304 window->goal_column = -1;
305 memset (&window->line_map, 0, sizeof (window->line_map));
306 window->modeline = xmalloc (1 + window->width);
307 window->line_starts = NULL;
308 window->flags = W_UpdateWindow | W_WindowVisible;
309 window_set_node_of_window (window, node);
311 /* Adjust the height of the old active window. */
312 active_window->height -= (window->height + 1);
313 #if defined (SPLIT_BEFORE_ACTIVE)
314 active_window->first_row += (window->height + 1);
316 active_window->flags |= W_UpdateWindow;
318 /* Readjust the new and old windows so that their modelines and contents
319 will be displayed correctly. */
321 /* We don't have to do this for WINDOW since window_set_node_of_window ()
323 window_adjust_pagetop (window);
324 window_make_modeline (window);
327 /* We do have to readjust the existing active window. */
328 window_adjust_pagetop (active_window);
329 window_make_modeline (active_window);
331 #if defined (SPLIT_BEFORE_ACTIVE)
332 /* This window is just before the active one. The active window gets
333 bumped down one. The active window is not changed. */
334 window->next = active_window;
336 window->prev = active_window->prev;
337 active_window->prev = window;
340 window->prev->next = window;
344 /* This window is just after the active one. Which window is active is
346 window->prev = active_window;
347 window->next = active_window->next;
348 active_window->next = window;
350 window->next->prev = window;
351 #endif /* !SPLIT_BEFORE_ACTIVE */
355 /* These useful macros make it possible to read the code in
356 window_change_window_height (). */
357 #define grow_me_shrinking_next(me, next, diff) \
359 me->height += diff; \
360 next->height -= diff; \
361 next->first_row += diff; \
362 window_adjust_pagetop (next); \
365 #define grow_me_shrinking_prev(me, prev, diff) \
367 me->height += diff; \
368 prev->height -= diff; \
369 me->first_row -=diff; \
370 window_adjust_pagetop (prev); \
373 #define shrink_me_growing_next(me, next, diff) \
375 me->height -= diff; \
376 next->height += diff; \
377 next->first_row -= diff; \
378 window_adjust_pagetop (next); \
381 #define shrink_me_growing_prev(me, prev, diff) \
383 me->height -= diff; \
384 prev->height += diff; \
385 me->first_row += diff; \
386 window_adjust_pagetop (prev); \
389 /* Change the height of WINDOW by AMOUNT. This also automagically adjusts
390 the previous and next windows in the chain. If there is only one user
391 window, then no change takes place. */
393 window_change_window_height (WINDOW *window, int amount)
395 register WINDOW *win, *prev, *next;
397 /* If there is only one window, or if the amount of change is zero,
398 return immediately. */
399 if (!windows->next || amount == 0)
402 /* Find this window in our chain. */
403 for (win = windows; win; win = win->next)
407 /* If the window is isolated (i.e., doesn't appear in our window list,
412 /* Change the height of this window by AMOUNT, if that is possible.
413 It can be impossible if there isn't enough available room on the
414 screen, or if the resultant window would be too small. */
419 /* WINDOW decreasing in size? */
422 int abs_amount = -amount; /* It is easier to deal with this way. */
424 /* If the resultant window would be too small, stop here. */
425 if ((window->height - abs_amount) < WINDOW_MIN_HEIGHT)
428 /* If we have two neighboring windows, choose the smaller one to get
432 if (prev->height < next->height)
433 shrink_me_growing_prev (window, prev, abs_amount);
435 shrink_me_growing_next (window, next, abs_amount);
438 shrink_me_growing_next (window, next, abs_amount);
440 shrink_me_growing_prev (window, prev, abs_amount);
443 /* WINDOW increasing in size? */
446 int total_avail, next_avail = 0, prev_avail = 0;
449 next_avail = next->height - WINDOW_MIN_SIZE;
452 prev_avail = prev->height - WINDOW_MIN_SIZE;
454 total_avail = next_avail + prev_avail;
456 /* If there isn't enough space available to grow this window, give up. */
457 if (amount > total_avail)
460 /* If there aren't two neighboring windows, or if one of the neighbors
461 is larger than the other one by at least AMOUNT, grow that one. */
462 if ((next && !prev) || ((next_avail - amount) >= prev_avail))
463 grow_me_shrinking_next (window, next, amount);
464 else if ((prev && !next) || ((prev_avail - amount) >= next_avail))
465 grow_me_shrinking_prev (window, prev, amount);
470 /* This window has two neighbors. They both must be shrunk in to
471 make enough space for WINDOW to grow. Make them both the same
473 if (prev_avail > next_avail)
475 change = prev_avail - next_avail;
476 grow_me_shrinking_prev (window, prev, change);
481 change = next_avail - prev_avail;
482 grow_me_shrinking_next (window, next, change);
486 /* Both neighbors are the same size. Split the difference in
487 AMOUNT between them. */
493 /* Odd numbers grow next, even grow prev. */
505 window_adjust_pagetop (prev);
506 window_adjust_pagetop (next);
510 prev->flags |= W_UpdateWindow;
513 next->flags |= W_UpdateWindow;
515 window->flags |= W_UpdateWindow;
516 window_adjust_pagetop (window);
519 /* Tile all of the windows currently displayed in the global variable
520 WINDOWS. If argument STYLE is TILE_INTERNALS, tile windows displaying
521 internal nodes as well, otherwise do not change the height of such
524 window_tile_windows (int style)
526 WINDOW *win, *last_adjusted;
527 int numwins, avail, per_win_height, leftover;
531 do_internals = (style == TILE_INTERNALS);
533 for (win = windows; win; win = win->next)
534 if (do_internals || !win->node ||
535 (win->node->flags & N_IsInternal) == 0)
537 avail += win->height;
541 if (numwins <= 1 || !the_screen->height)
544 /* Find the size for each window. Divide the size of the usable portion
545 of the screen by the number of windows. */
546 per_win_height = avail / numwins;
547 leftover = avail - (per_win_height * numwins);
549 last_adjusted = NULL;
550 for (win = windows; win; win = win->next)
552 if (do_internals || !win->node ||
553 (win->node->flags & N_IsInternal) == 0)
556 win->height = per_win_height;
561 last_adjusted->height += leftover;
563 /* Readjust the first_row of every window in the chain. */
564 for (win = windows; win; win = win->next)
567 win->first_row = win->prev->first_row + win->prev->height + 1;
569 window_adjust_pagetop (win);
570 win->flags |= W_UpdateWindow;
574 /* Toggle the state of line wrapping in WINDOW. This can do a bit of fancy
577 window_toggle_wrap (WINDOW *window)
579 if (window->flags & W_NoWrap)
580 window->flags &= ~W_NoWrap;
582 window->flags |= W_NoWrap;
584 if (window != the_echo_area)
587 int old_lines, old_pagetop;
589 old_starts = window->line_starts;
590 old_lines = window->line_count;
591 old_pagetop = window->pagetop;
593 calculate_line_starts (window);
595 /* Make sure that point appears within this window. */
596 window_adjust_pagetop (window);
598 /* If the pagetop hasn't changed maybe we can do some scrolling now
599 to speed up the display. Many of the line starts will be the same,
600 so scrolling here is a very good optimization.*/
601 if (old_pagetop == window->pagetop)
602 display_scroll_line_starts
603 (window, old_pagetop, old_starts, old_lines);
604 maybe_free (old_starts);
606 window->flags |= W_UpdateWindow;
609 /* Set WINDOW to display NODE. */
611 window_set_node_of_window (WINDOW *window, NODE *node)
616 recalculate_line_starts (window);
617 window->flags |= W_UpdateWindow;
618 /* The display_pos member is nonzero if we're displaying an anchor. */
619 window->point = node ? node->display_pos : 0;
620 window_adjust_pagetop (window);
621 window_make_modeline (window);
624 /* Delete WINDOW from the list of known windows. If this window was the
625 active window, make the next window in the chain be the active window.
626 If the active window is the next or previous window, choose that window
627 as the recipient of the extra space. Otherwise, prefer the next window. */
629 window_delete_window (WINDOW *window)
631 WINDOW *next, *prev, *window_to_fix;
636 /* You cannot delete the only window or a permanent window. */
637 if ((!next && !prev) || (window->flags & W_WindowIsPerm))
648 if (window->line_starts)
649 free (window->line_starts);
651 if (window->modeline)
652 free (window->modeline);
654 if (window == active_window)
656 /* If there isn't a next window, then there must be a previous one,
657 since we cannot delete the last window. If there is a next window,
658 prefer to use that as the active window. */
660 active_window = next;
662 active_window = prev;
665 if (next && active_window == next)
666 window_to_fix = next;
667 else if (prev && active_window == prev)
668 window_to_fix = prev;
670 window_to_fix = next;
672 window_to_fix = prev;
674 window_to_fix = windows;
676 if (window_to_fix->first_row > window->first_row)
680 /* Try to adjust the visible part of the node so that as little
681 text as possible has to move. */
682 diff = window_to_fix->first_row - window->first_row;
683 window_to_fix->first_row = window->first_row;
685 window_to_fix->pagetop -= diff;
686 if (window_to_fix->pagetop < 0)
687 window_to_fix->pagetop = 0;
690 /* The `+ 1' is to offset the difference between the first_row locations.
691 See the code in window_make_window (). */
692 window_to_fix->height += window->height + 1;
693 window_to_fix->flags |= W_UpdateWindow;
698 /* For every window in CHAIN, set the flags member to have FLAG set. */
700 window_mark_chain (WINDOW *chain, int flag)
702 register WINDOW *win;
704 for (win = chain; win; win = win->next)
708 /* For every window in CHAIN, clear the flags member of FLAG. */
710 window_unmark_chain (WINDOW *chain, int flag)
712 register WINDOW *win;
714 for (win = chain; win; win = win->next)
718 /* Return the number of characters it takes to display CHARACTER on the
721 character_width (int character, int hpos)
723 int printable_limit = 127;
727 printable_limit = 255;
729 if (character > printable_limit)
731 else if (iscntrl (character))
737 width = the_screen->width - hpos;
740 width = ((hpos + 8) & 0xf8) - hpos;
746 else if (character == DEL)
752 /* Return the number of characters it takes to display STRING on the screen
755 string_width (char *string, int 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 *node)
786 register int i, lines;
792 contents = node->contents;
793 for (i = 0, lines = 1; i < node->nodelen; i++)
794 if (contents[i] == '\n')
801 struct calc_closure {
803 int line_starts_slots; /* FIXME: size_t */
807 _calc_line_starts (void *closure, size_t line_index,
808 const char *src_line,
809 char *printed_line, size_t pl_index, size_t pl_count)
811 struct calc_closure *cp = closure;
812 add_pointer_to_array (src_line,
813 cp->win->line_count, cp->win->line_starts,
814 cp->line_starts_slots, 100, char *);
819 calculate_line_starts (WINDOW *window)
821 struct calc_closure closure;
823 window->line_starts = NULL;
824 window->line_count = 0;
829 closure.win = window;
830 closure.line_starts_slots = 0;
831 process_node_text (window, window->node->contents, 0,
832 _calc_line_starts, &closure);
835 /* Given WINDOW, recalculate the line starts for the node it displays. */
837 recalculate_line_starts (WINDOW *window)
839 maybe_free (window->line_starts);
840 calculate_line_starts (window);
844 /* Global variable control redisplay of scrolled windows. If non-zero,
845 it is the desired number of lines to scroll the window in order to
846 make point visible. A value of 1 produces smooth scrolling. If set
847 to zero, the line containing point is centered within the window. */
848 int window_scroll_step = 1;
850 /* Adjust the pagetop of WINDOW such that the cursor point will be visible. */
852 window_adjust_pagetop (WINDOW *window)
854 register int line = 0;
860 contents = window->node->contents;
862 /* Find the first printed line start which is after WINDOW->point. */
863 for (line = 0; line < window->line_count; line++)
867 line_start = window->line_starts[line];
869 if ((line_start - contents) > window->point)
873 /* The line index preceding the line start which is past point is the
874 one containing point. */
877 /* If this line appears in the current displayable page, do nothing.
878 Otherwise, adjust the top of the page to make this line visible. */
879 if ((line < window->pagetop) ||
880 (line - window->pagetop > (window->height - 1)))
882 /* The user-settable variable "scroll-step" is used to attempt
883 to make point visible, iff it is non-zero. If that variable
884 is zero, then the line containing point is centered within
886 if (window_scroll_step < window->height)
888 if ((line < window->pagetop) &&
889 ((window->pagetop - window_scroll_step) <= line))
890 window->pagetop -= window_scroll_step;
891 else if ((line - window->pagetop > (window->height - 1)) &&
892 ((line - (window->pagetop + window_scroll_step)
894 window->pagetop += window_scroll_step;
896 window->pagetop = line - ((window->height - 1) / 2);
899 window->pagetop = line - ((window->height - 1) / 2);
901 if (window->pagetop < 0)
903 window->flags |= W_UpdateWindow;
907 /* Return the index of the line containing point. */
909 window_line_of_point (WINDOW *window)
911 register int i, start = 0;
913 /* Try to optimize. Check to see if point is past the pagetop for
914 this window, and if so, start searching forward from there. */
915 if ((window->pagetop > -1 && window->pagetop < window->line_count) &&
916 (window->line_starts[window->pagetop] - window->node->contents)
918 start = window->pagetop;
920 for (i = start; i < window->line_count; i++)
922 if ((window->line_starts[i] - window->node->contents) > window->point)
929 /* Get and return the goal column for this window. */
931 window_get_goal_column (WINDOW *window)
936 if (window->goal_column != -1)
937 return window->goal_column;
939 /* Okay, do the work. Find the printed offset of the cursor
941 return window_get_cursor_column (window);
944 /* Get and return the printed column offset of the cursor in this window. */
946 window_get_cursor_column (WINDOW *window)
948 return window_point_to_column (window, window->point, &window->point);
951 /* Count the number of characters in LINE that precede the printed column
954 window_chars_to_goal (WINDOW *win, int goal)
956 window_compute_line_map (win);
957 if (goal >= win->line_map.used)
958 goal = win->line_map.used - 1;
959 return win->line_map.map[goal] - win->line_map.map[0];
962 /* Create a modeline for WINDOW, and store it in window->modeline. */
964 window_make_modeline (WINDOW *window)
968 char location_indicator[4];
971 /* Only make modelines for those windows which have one. */
972 if (window->flags & W_InhibitMode)
975 /* Find the number of lines actually displayed in this window. */
976 lines_remaining = window->line_count - window->pagetop;
978 if (window->pagetop == 0)
980 if (lines_remaining <= window->height)
981 strcpy (location_indicator, "All");
983 strcpy (location_indicator, "Top");
987 if (lines_remaining <= window->height)
988 strcpy (location_indicator, "Bot");
994 pt = (float)window->pagetop;
995 lc = (float)window->line_count;
997 percentage = 100 * (pt / lc);
999 sprintf (location_indicator, "%2d%%", percentage);
1003 /* Calculate the maximum size of the information to stick in MODELINE. */
1005 int modeline_len = 0;
1006 char *parent = NULL, *filename = "*no file*";
1007 char *nodename = "*no node*";
1008 const char *update_message = NULL;
1009 NODE *node = window->node;
1014 nodename = node->nodename;
1018 parent = filename_non_directory (node->parent);
1019 modeline_len += strlen ("Subfile: ") + strlen (node->filename);
1023 filename = filename_non_directory (node->filename);
1025 if (node->flags & N_UpdateTags)
1026 update_message = _("--*** Tags out of Date ***");
1030 modeline_len += strlen (update_message);
1031 modeline_len += strlen (filename);
1032 modeline_len += strlen (nodename);
1033 modeline_len += 4; /* strlen (location_indicator). */
1035 /* 10 for the decimal representation of the number of lines in this
1036 node, and the remainder of the text that can appear in the line. */
1037 modeline_len += 10 + strlen (_("-----Info: (), lines ----, "));
1038 modeline_len += window->width;
1040 modeline = xmalloc (1 + modeline_len);
1042 /* Special internal windows have no filename. */
1043 if (!parent && !*filename)
1044 sprintf (modeline, _("-%s---Info: %s, %d lines --%s--"),
1045 (window->flags & W_NoWrap) ? "$" : "-",
1046 nodename, window->line_count, location_indicator);
1048 sprintf (modeline, _("-%s%s-Info: (%s)%s, %d lines --%s--"),
1049 (window->flags & W_NoWrap) ? "$" : "-",
1050 (node && (node->flags & N_IsCompressed)) ? "zz" : "--",
1051 parent ? parent : filename,
1052 nodename, window->line_count, location_indicator);
1055 sprintf (modeline + strlen (modeline), _(" Subfile: %s"), filename);
1058 sprintf (modeline + strlen (modeline), "%s", update_message);
1060 i = strlen (modeline);
1062 if (i >= window->width)
1063 modeline[window->width] = '\0';
1066 while (i < window->width)
1067 modeline[i++] = '-';
1071 strcpy (window->modeline, modeline);
1076 /* Make WINDOW start displaying at PERCENT percentage of its node. */
1078 window_goto_percentage (WINDOW *window, int percent)
1086 (int) ((float)window->line_count * ((float)percent / 100.0));
1088 window->pagetop = desired_line;
1090 window->line_starts[window->pagetop] - window->node->contents;
1091 window->flags |= W_UpdateWindow;
1092 window_make_modeline (window);
1095 /* Get the state of WINDOW, and save it in STATE. */
1097 window_get_state (WINDOW *window, SEARCH_STATE *state)
1099 state->node = window->node;
1100 state->pagetop = window->pagetop;
1101 state->point = window->point;
1104 /* Set the node, pagetop, and point of WINDOW. */
1106 window_set_state (WINDOW *window, SEARCH_STATE *state)
1108 if (window->node != state->node)
1109 window_set_node_of_window (window, state->node);
1110 window->pagetop = state->pagetop;
1111 window->point = state->point;
1115 /* Manipulating home-made nodes. */
1117 /* A place to buffer echo area messages. */
1118 static NODE *echo_area_node = NULL;
1120 /* Make the node of the_echo_area be an empty one. */
1122 free_echo_area (void)
1126 maybe_free (echo_area_node->contents);
1127 free (echo_area_node);
1130 echo_area_node = NULL;
1131 window_set_node_of_window (the_echo_area, echo_area_node);
1134 /* Clear the echo area, removing any message that is already present.
1135 The echo area is cleared immediately. */
1137 window_clear_echo_area (void)
1140 display_update_one_window (the_echo_area);
1143 /* Make a message appear in the echo area, built from FORMAT, ARG1 and ARG2.
1144 The arguments are treated similar to printf () arguments, but not all of
1145 printf () hair is present. The message appears immediately. If there was
1146 already a message appearing in the echo area, it is removed. */
1148 window_message_in_echo_area (const char *format, void *arg1, void *arg2)
1151 echo_area_node = build_message_node (format, arg1, arg2);
1152 window_set_node_of_window (the_echo_area, echo_area_node);
1153 display_update_one_window (the_echo_area);
1156 /* Place a temporary message in the echo area built from FORMAT, ARG1
1157 and ARG2. The message appears immediately, but does not destroy
1158 any existing message. A future call to unmessage_in_echo_area ()
1159 restores the old contents. */
1160 static NODE **old_echo_area_nodes = NULL;
1161 static int old_echo_area_nodes_index = 0;
1162 static int old_echo_area_nodes_slots = 0;
1165 message_in_echo_area (const char *format, void *arg1, void *arg2)
1169 add_pointer_to_array (echo_area_node, old_echo_area_nodes_index,
1170 old_echo_area_nodes, old_echo_area_nodes_slots,
1173 echo_area_node = NULL;
1174 window_message_in_echo_area (format, arg1, arg2);
1178 unmessage_in_echo_area (void)
1182 if (old_echo_area_nodes_index)
1183 echo_area_node = old_echo_area_nodes[--old_echo_area_nodes_index];
1185 window_set_node_of_window (the_echo_area, echo_area_node);
1186 display_update_one_window (the_echo_area);
1189 /* A place to build a message. */
1190 static char *message_buffer = NULL;
1191 static int message_buffer_index = 0;
1192 static int message_buffer_size = 0;
1194 /* Ensure that there is enough space to stuff LENGTH characters into
1197 message_buffer_resize (int length)
1199 if (!message_buffer)
1201 message_buffer_size = length + 1;
1202 message_buffer = xmalloc (message_buffer_size);
1203 message_buffer_index = 0;
1206 while (message_buffer_size <= message_buffer_index + length)
1207 message_buffer = (char *)
1208 xrealloc (message_buffer,
1209 message_buffer_size += 100 + (2 * length));
1212 /* Format MESSAGE_BUFFER with the results of printing FORMAT with ARG1 and
1215 build_message_buffer (const char *format, void *arg1, void *arg2, void *arg3)
1217 register int i, len;
1225 len = strlen (format);
1227 message_buffer_resize (len);
1229 for (i = 0; format[i]; i++)
1231 if (format[i] != '%')
1233 message_buffer[message_buffer_index++] = format[i];
1239 const char *fmt_start = format + i;
1241 int fmt_len, formatted_len;
1246 while (format[i] && strchr ("-. +0123456789", format[i]))
1254 /* position parameter parameter */
1255 /* better to use bprintf from bfox's metahtml? */
1256 arg_index = atoi(fmt_start + 1) - 1;
1265 fmt_len = format + i - fmt_start + 1;
1266 fmt = xmalloc (fmt_len + 1);
1267 strncpy (fmt, fmt_start, fmt_len);
1268 fmt[fmt_len] = '\0';
1271 /* removed positioned parameter */
1273 for (p = fmt + 1; *p && *p != '$'; p++) {
1276 strcpy(fmt + 1, p + 1);
1279 /* If we have "%-98s", maybe 98 calls for a longer string. */
1284 for (j = fmt_len - 2; j >= 0; j--)
1285 if (isdigit (fmt[j]) || fmt[j] == '$')
1288 formatted_len = atoi (fmt + j);
1291 formatted_len = c == 's' ? 0 : 1; /* %s can produce empty string */
1295 case '%': /* Insert a percent sign. */
1296 message_buffer_resize (len + formatted_len);
1298 (message_buffer + message_buffer_index, fmt, "%");
1299 message_buffer_index += formatted_len;
1302 case 's': /* Insert the current arg as a string. */
1307 string = (char *)args[arg_index++];
1308 string_len = strlen (string);
1310 if (formatted_len > string_len)
1311 string_len = formatted_len;
1312 message_buffer_resize (len + string_len);
1314 (message_buffer + message_buffer_index, fmt, string);
1315 message_buffer_index += string_len;
1319 case 'd': /* Insert the current arg as an integer. */
1324 long_val = (long)args[arg_index++];
1325 integer = (int)long_val;
1327 message_buffer_resize (len + formatted_len > 32
1328 ? formatted_len : 32);
1330 (message_buffer + message_buffer_index, fmt, integer);
1331 message_buffer_index = strlen (message_buffer);
1335 case 'c': /* Insert the current arg as a character. */
1340 long_val = (long)args[arg_index++];
1341 character = (int)long_val;
1343 message_buffer_resize (len + formatted_len);
1345 (message_buffer + message_buffer_index, fmt, character);
1346 message_buffer_index += formatted_len;
1356 message_buffer[message_buffer_index] = '\0';
1359 /* Build a new node which has FORMAT printed with ARG1 and ARG2 as the
1362 build_message_node (const char *format, void *arg1, void *arg2)
1366 message_buffer_index = 0;
1367 build_message_buffer (format, arg1, arg2, 0);
1369 node = message_buffer_to_node ();
1373 /* Convert the contents of the message buffer to a node. */
1375 message_buffer_to_node (void)
1379 node = xmalloc (sizeof (NODE));
1380 node->filename = NULL;
1381 node->parent = NULL;
1382 node->nodename = NULL;
1384 node->display_pos =0;
1386 /* Make sure that this buffer ends with a newline. */
1387 node->nodelen = 1 + strlen (message_buffer);
1388 node->contents = xmalloc (1 + node->nodelen);
1389 strcpy (node->contents, message_buffer);
1390 node->contents[node->nodelen - 1] = '\n';
1391 node->contents[node->nodelen] = '\0';
1395 /* Useful functions can be called from outside of window.c. */
1397 initialize_message_buffer (void)
1399 message_buffer_index = 0;
1402 /* Print FORMAT with ARG1,2 to the end of the current message buffer. */
1404 printf_to_message_buffer (const char *format, void *arg1, void *arg2, void *arg3)
1406 build_message_buffer (format, arg1, arg2, arg3);
1409 /* Return the current horizontal position of the "cursor" on the most
1410 recently output message buffer line. */
1412 message_buffer_length_this_line (void)
1416 if (!message_buffer_index)
1419 for (i = message_buffer_index; i && message_buffer[i - 1] != '\n'; i--);
1421 return string_width (message_buffer + i, 0);
1424 /* Pad STRING to COUNT characters by inserting blanks. */
1426 pad_to (int count, char *string)
1430 i = strlen (string);
1445 #define ITER_SETBYTES(iter,n) ((iter).cur.bytes = n)
1446 #define ITER_LIMIT(iter) ((iter).limit - (iter).cur.ptr)
1448 /* If ITER points to an ANSI escape sequence, process it, set PLEN to its
1449 length in bytes, and return 1.
1450 Otherwise, return 0.
1453 ansi_escape (mbi_iterator_t iter, size_t *plen)
1455 if (raw_escapes_p && *mbi_cur_ptr (iter) == '\033' && mbi_avail (iter))
1458 if (*mbi_cur_ptr (iter) == '[' && mbi_avail (iter))
1460 ITER_SETBYTES (iter, 1);
1462 if (isdigit (*mbi_cur_ptr (iter)) && mbi_avail (iter))
1464 ITER_SETBYTES (iter, 1);
1466 if (*mbi_cur_ptr (iter) == 'm')
1471 else if (isdigit (*mbi_cur_ptr (iter)) && mbi_avail (iter))
1473 ITER_SETBYTES (iter, 1);
1475 if (*mbi_cur_ptr (iter) == 'm')
1488 /* If ITER points to an info tag, process it, set PLEN to its
1489 length in bytes, and return 1.
1490 Otherwise, return 0.
1492 Collected tag is processed if HANDLE!=0.
1495 info_tag (mbi_iterator_t iter, int handle, size_t *plen)
1497 if (*mbi_cur_ptr (iter) == '\0' && mbi_avail (iter))
1500 if (*mbi_cur_ptr (iter) == '\b' && mbi_avail (iter))
1503 if (*mbi_cur_ptr (iter) == '[' && mbi_avail (iter))
1505 const char *ptr, *end;
1507 ptr = mbi_cur_ptr (iter);
1508 end = memmem (ptr, ITER_LIMIT (iter), "\0\b]", 3);
1511 size_t len = end - ptr;
1515 char *elt = xmalloc (len + 1);
1516 memcpy (elt, ptr, len);
1531 /* Process contents of the current node from WIN, beginning from START, using
1532 callback function FUN.
1534 FUN is called for every line collected from the node. Its arguments:
1536 int (*fun) (void *closure, size_t line_no,
1537 const char *src_line, char *prt_line,
1538 size_t prt_bytes, size_t prt_chars)
1540 closure -- An opaque pointer passed as 5th parameter to process_node_text;
1541 line_no -- Number of processed line (starts from 0);
1542 src_line -- Pointer to the source line (unmodified);
1543 prt_line -- Collected line contents, ready for output;
1544 prt_bytes -- Number of bytes in prt_line;
1545 prt_chars -- Number of characters in prt_line.
1547 If FUN returns non zero, process_node_text stops processing and returns
1550 If DO_TAGS is not zero, process info tags, otherwise ignore them.
1552 Return value: number of lines processed.
1556 process_node_text (WINDOW *win, char *start,
1558 int (*fun) (void *, size_t, const char *, char *, size_t, size_t),
1561 char *printed_line; /* Buffer for a printed line. */
1562 size_t pl_count = 0; /* Number of *characters* written to PRINTED_LINE */
1563 size_t pl_index = 0; /* Index into PRINTED_LINE. */
1564 size_t in_index = 0;
1565 size_t line_index = 0; /* Number of lines done so far. */
1566 size_t allocated_win_width;
1567 mbi_iterator_t iter;
1569 /* Print each line in the window into our local buffer, and then
1570 check the contents of that buffer against the display. If they
1571 differ, update the display. */
1572 allocated_win_width = win->width + 1;
1573 printed_line = xmalloc (allocated_win_width);
1575 for (mbi_init (iter, start,
1576 win->node->contents + win->node->nodelen - start),
1581 const char *carried_over_ptr;
1582 size_t carried_over_len, carried_over_count;
1583 const char *cur_ptr = mbi_cur_ptr (iter);
1584 int cur_len = mb_len (mbi_cur (iter));
1589 if (mb_isprint (mbi_cur (iter)))
1593 else if (cur_len == 1)
1595 if (*cur_ptr == '\r' || *cur_ptr == '\n')
1597 replen = win->width - pl_count;
1600 else if (ansi_escape (iter, &cur_len))
1603 ITER_SETBYTES (iter, cur_len);
1605 else if (info_tag (iter, do_tags, &cur_len))
1607 ITER_SETBYTES (iter, cur_len);
1612 if (*cur_ptr == '\t')
1614 cur_ptr = printed_representation (cur_ptr, cur_len, pl_count,
1619 else if (show_malformed_multibyte_p || mbi_cur (iter).wc_valid)
1621 /* FIXME: I'm not sure it's the best way to deal with unprintable
1622 multibyte characters */
1623 cur_ptr = printed_representation (cur_ptr, cur_len, pl_count,
1628 /* Ensure there is enough space in the buffer */
1629 while (pl_index + cur_len + 2 > allocated_win_width - 1)
1630 printed_line = x2realloc (printed_line, &allocated_win_width);
1632 /* If this character can be printed without passing the width of
1633 the line, then stuff it into the line. */
1634 if (pl_count + replen < win->width)
1638 for (i = 0; i < cur_len; i++)
1639 printed_line[pl_index++] = cur_ptr[i];
1641 in_index += mb_len (mbi_cur (iter));
1645 /* If this character cannot be printed in this line, we have
1646 found the end of this line as it would appear on the screen.
1647 Carefully print the end of the line, and then compare. */
1650 printed_line[pl_index] = '\0';
1651 carried_over_ptr = NULL;
1655 /* The printed representation of this character extends into
1658 carried_over_count = replen;
1661 /* It is a single (possibly multibyte) character */
1663 carried_over_ptr = cur_ptr;
1664 carried_over_len = cur_len;
1670 /* Remember the offset of the last character printed out of
1671 REP so that we can carry the character over to the next
1673 for (i = 0; pl_count < (win->width - 1);
1675 printed_line[pl_index++] = cur_ptr[i++];
1677 carried_over_ptr = cur_ptr + i;
1678 carried_over_len = cur_len;
1681 /* If printing the last character in this window couldn't
1682 possibly cause the screen to scroll, place a backslash
1683 in the rightmost column. */
1684 if (1 + line_index + win->first_row < the_screen->height)
1686 if (win->flags & W_NoWrap)
1687 printed_line[pl_index++] = '$';
1689 printed_line[pl_index++] = '\\';
1692 printed_line[pl_index] = '\0';
1695 rc = fun (closure, line_index, mbi_cur_ptr (iter) - in_index,
1696 printed_line, pl_index, pl_count);
1700 /* Reset all data to the start of the line. */
1708 /* If there are bytes carried over, stuff them
1709 into the buffer now. */
1710 if (carried_over_ptr)
1712 for (; carried_over_len;
1713 carried_over_len--, carried_over_ptr++, pl_index++)
1714 printed_line[pl_index] = *carried_over_ptr;
1715 pl_count += carried_over_count;
1718 /* If this window has chosen not to wrap lines, skip to the end
1719 of the physical line in the buffer, and start a new line here. */
1720 if (pl_index && win->flags & W_NoWrap)
1722 for (; mbi_avail (iter); mbi_advance (iter))
1723 if (mb_len (mbi_cur (iter)) == 1
1724 && *mbi_cur_ptr (iter) == '\n')
1730 printed_line[0] = 0;
1736 fun (closure, line_index, mbi_cur_ptr (iter) - in_index,
1737 printed_line, pl_index, pl_count);
1739 free (printed_line);
1744 clean_manpage (char *manpage)
1746 mbi_iterator_t iter;
1747 size_t len = strlen (manpage);
1748 char *newpage = xmalloc (len + 1);
1752 for (mbi_init (iter, manpage, len);
1756 const char *cur_ptr = mbi_cur_ptr (iter);
1757 int cur_len = mb_len (mbi_cur (iter));
1761 if (*cur_ptr == '\b' || *cur_ptr == '\f')
1763 if (np >= newpage + prev_len)
1766 else if (ansi_escape (iter, &cur_len))
1768 memcpy (np, cur_ptr, cur_len);
1770 ITER_SETBYTES (iter, cur_len);
1772 else if (show_malformed_multibyte_p || mbi_cur (iter).wc_valid)
1777 memcpy (np, cur_ptr, cur_len);
1784 strcpy (manpage, newpage);
1789 line_map_init (LINE_MAP *map, NODE *node, int line)
1797 line_map_add (LINE_MAP *map, long pos)
1799 if (map->used == map->size)
1802 map->size = 80; /* Initial allocation */
1803 map->map = x2nrealloc (map->map,
1805 sizeof (map->map[0]));
1808 map->map[map->used++] = pos;
1811 /* Initialize (clear) WIN's line map. */
1813 window_line_map_init (WINDOW *win)
1815 win->line_map.used = 0;
1818 /* Scan the line number LINE in WIN. If PHYS is true, stop scanning at
1819 the end of physical line, i.e. at the newline character. Otherwise,
1820 stop it at the end of logical line.
1822 If FUN is supplied, call it for each processed multibyte character.
1823 Arguments of FUN are
1825 closure - Function-specific data passed as 5th argument to
1827 cpos - Current point value;
1828 replen - Size of screen representation of this character, in
1829 columns. This value may be 0 (for ANSI sequences and
1830 info tags), or > 1 (for tabs).
1833 window_scan_line (WINDOW *win, int line, int phys,
1834 void (*fun) (void *closure, long cpos, int replen),
1837 mbi_iterator_t iter;
1838 long cpos = win->line_starts[line] - win->node->contents;
1842 if (!phys && line + 1 < win->line_count)
1843 endp = win->line_starts[line + 1];
1845 endp = win->node->contents + win->node->nodelen;
1847 for (mbi_init (iter,
1848 win->line_starts[line],
1849 win->node->contents + win->node->nodelen -
1850 win->line_starts[line]);
1851 !delim && mbi_avail (iter);
1854 const char *cur_ptr = mbi_cur_ptr (iter);
1855 int cur_len = mb_len (mbi_cur (iter));
1858 if (cur_ptr >= endp)
1861 if (mb_isprint (mbi_cur (iter)))
1865 else if (cur_len == 1)
1867 if (*cur_ptr == '\r' || *cur_ptr == '\n')
1872 else if (ansi_escape (iter, &cur_len))
1874 ITER_SETBYTES (iter, cur_len);
1877 else if (info_tag (iter, 0, &cur_len))
1879 ITER_SETBYTES (iter, cur_len);
1885 printed_representation (cur_ptr, cur_len,
1892 /* FIXME: I'm not sure it's the best way to deal with unprintable
1893 multibyte characters */
1894 printed_representation (cur_ptr, cur_len, win->line_map.used,
1899 fun (closure, cpos, replen);
1906 add_line_map (void *closure, long cpos, int replen)
1908 WINDOW *win = closure;
1911 line_map_add (&win->line_map, cpos);
1914 /* Compute the line map for the current line in WIN. */
1916 window_compute_line_map (WINDOW *win)
1918 int line = window_line_of_point (win);
1920 if (win->line_map.node == win->node && win->line_map.nline == line
1921 && win->line_map.used)
1923 line_map_init (&win->line_map, win->node, line);
1925 window_scan_line (win, line, 0, add_line_map, win);
1928 /* Return offset of the end of current physical line.
1931 window_end_of_line (WINDOW *win)
1933 int line = window_line_of_point (win);
1935 return window_scan_line (win, line, 1, NULL, NULL) - 1;
1939 /* Translate the value of POINT into a column number. If NP is given
1940 store there the value of point corresponding to the beginning of a
1941 multibyte character in this column.
1944 window_point_to_column (WINDOW *win, long point, long *np)
1948 window_compute_line_map (win);
1949 if (!win->line_map.map || point < win->line_map.map[0])
1951 for (i = 0; i < win->line_map.used; i++)
1952 if (win->line_map.map[i] > point)
1955 *np = win->line_map.map[i-1];