home *** CD-ROM | disk | FTP | other *** search
- /* window.c -- Windows in Info. */
-
- /* This file is part of GNU Info, a program for reading online documentation
- stored in Info format.
-
- Copyright (C) 1993 Free Software Foundation, Inc.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2, or (at your option)
- any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
- Written by Brian Fox (bfox@ai.mit.edu). */
-
- #include <stdio.h>
- #include <ctype.h>
- #include <sys/types.h>
- #include <sys/stat.h>
- #include "nodes.h"
- #include "window.h"
- #include "display.h"
- #include "info-utils.h"
- #include "infomap.h"
-
- /* The window which describes the screen. */
- WINDOW *the_screen = (WINDOW *)NULL;
-
- /* The window which describes the echo area. */
- WINDOW *the_echo_area = (WINDOW *)NULL;
-
- /* The list of windows in Info. */
- WINDOW *windows = (WINDOW *)NULL;
-
- /* Pointer to the active window in WINDOW_LIST. */
- WINDOW *active_window = (WINDOW *)NULL;
-
- /* The size of the echo area in Info. It never changes, irregardless of the
- size of the screen. */
- #define ECHO_AREA_HEIGHT 1
-
- /* Macro returns the amount of space that the echo area truly requires relative
- to the entire screen. */
- #define echo_area_required (1 + the_echo_area->height)
-
- /* Initalize the window system by creating THE_SCREEN and THE_ECHO_AREA.
- Create the first window ever.
- You pass the dimensions of the total screen size. */
- void
- window_initialize_windows (width, height)
- int width, height;
- {
- the_screen = (WINDOW *)xmalloc (sizeof (WINDOW));
- the_echo_area = (WINDOW *)xmalloc (sizeof (WINDOW));
- windows = (WINDOW *)xmalloc (sizeof (WINDOW));
- active_window = windows;
-
- zero_mem (the_screen, sizeof (WINDOW));
- zero_mem (the_echo_area, sizeof (WINDOW));
- zero_mem (active_window, sizeof (WINDOW));
-
- /* None of these windows has a goal column yet. */
- the_echo_area->goal_column = -1;
- active_window->goal_column = -1;
- the_screen->goal_column = -1;
-
- /* The active and echo_area windows are visible.
- The echo_area is permanent.
- The screen is permanent. */
- active_window->flags = W_WindowVisible;
- the_echo_area->flags = W_WindowIsPerm | W_InhibitMode | W_WindowVisible;
- the_screen->flags = W_WindowIsPerm;
-
- /* The height of the echo area never changes. It is statically set right
- here, and it must be at least 1 line for display. The size of the
- initial window cannot be the same size as the screen, since the screen
- includes the echo area. So, we make the height of the initial window
- equal to the screen's displayable region minus the height of the echo
- area. */
- the_echo_area->height = ECHO_AREA_HEIGHT;
- active_window->height = the_screen->height - 1 - the_echo_area->height;
- window_new_screen_size (width, height, (VFunction *)NULL);
-
- /* The echo area uses a different keymap than normal info windows. */
- the_echo_area->keymap = echo_area_keymap;
- active_window->keymap = info_keymap;
- }
-
- /* Given that the size of the screen has changed to WIDTH and HEIGHT
- from whatever it was before (found in the_screen->height, ->width),
- change the size (and possibly location) of each window in the screen.
- If a window would become too small, call the function DELETER on it,
- after deleting the window from our chain of windows. If DELETER is NULL,
- nothing extra is done. The last window can never be deleted, but it can
- become invisible. */
-
- /* If non-null, a function to call with WINDOW as argument when the function
- window_new_screen_size () has deleted WINDOW. */
- VFunction *window_deletion_notifier = (VFunction *)NULL;
-
- void
- window_new_screen_size (width, height)
- int width, height;
- {
- register WINDOW *win;
- int delta_height, delta_each, delta_leftover;
- int numwins;
-
- /* If no change, do nothing. */
- if (width == the_screen->width && height == the_screen->height)
- return;
-
- /* If the new window height is too small, make it be zero. */
- if (height < (WINDOW_MIN_SIZE + the_echo_area->height))
- height = 0;
- if (width < 0)
- width = 0;
-
- /* Find out how many windows will change. */
- for (numwins = 0, win = windows; win; win = win->next, numwins++);
-
- /* See if some windows will need to be deleted. This is the case if
- the screen is getting smaller, and the available space divided by
- the number of windows is less than WINDOW_MIN_SIZE. In that case,
- delete some windows and try again until there is either enough
- space to divy up among the windows, or until there is only one
- window left. */
- while ((height - echo_area_required) / numwins <= WINDOW_MIN_SIZE)
- {
- /* If only one window, make the size of it be zero, and return
- immediately. */
- if (!windows->next)
- {
- windows->height = 0;
- maybe_free (windows->line_starts);
- windows->line_starts = (char **)NULL;
- windows->line_count = 0;
- break;
- }
-
- /* If we have some temporary windows, delete one of them. */
- for (win = windows; win; win = win->next)
- if (win->flags & W_TempWindow)
- break;
-
- /* Otherwise, delete the first window, and try again. */
- if (!win)
- win = windows;
-
- if (window_deletion_notifier)
- (*window_deletion_notifier) (win);
-
- window_delete_window (win);
- numwins--;
- }
-
- /* The screen has changed height and width. */
- delta_height = height - the_screen->height; /* This is how much. */
- the_screen->height = height; /* This is the new height. */
- the_screen->width = width; /* This is the new width. */
-
- /* Set the start of the echo area. */
- the_echo_area->first_row = height - the_echo_area->height;
- the_echo_area->width = width;
-
- /* Check to see if the screen can really be changed this way. */
- if ((!windows->next) && ((windows->height == 0) && (delta_height < 0)))
- return;
-
- /* Divide the change in height among the available windows. */
- delta_each = delta_height / numwins;
- delta_leftover = delta_height - (delta_each * numwins);
-
- /* Change the height of each window in the chain by delta_each. Change
- the height of the last window in the chain by delta_each and by the
- leftover amount of change. Change the width of each window to be
- WIDTH. */
- for (win = windows; win; win = win->next)
- {
- if ((win->width != width) && ((win->flags & W_InhibitMode) == 0))
- {
- win->width = width;
- maybe_free (win->modeline);
- win->modeline = (char *)xmalloc (1 + width);
- }
-
- win->height += delta_each;
-
- /* If the previous height of this window was zero, it was the only
- window, and it was not visible. Thus we need to compensate for
- the echo_area. */
- if (win->height == delta_each)
- win->height -= (1 + the_echo_area->height);
-
- /* If this is not the first window in the chain, then change the
- first row of it. We cannot just add delta_each to the first row,
- since this window's first row is the sum of the collective increases
- that have gone before it. So we just add one to the location of the
- previous window's modeline. */
- if (win->prev)
- win->first_row = (win->prev->first_row + win->prev->height) + 1;
-
- /* The last window in the chain gets the extra space (or shrinkage). */
- if (!win->next)
- win->height += delta_leftover;
-
- if (win->node)
- recalculate_line_starts (win);
-
- win->flags |= W_UpdateWindow;
- }
-
- /* If the screen got smaller, check over the windows just shrunk to
- keep them within bounds. Some of the windows may have gotten smaller
- than WINDOW_MIN_HEIGHT in which case some of the other windows are
- larger than the available display space in the screen. Because of our
- intial test above, we know that there is enough space for all of the
- windows. */
- if ((delta_each < 0) && ((windows->height != 0) && windows->next))
- {
- int avail;
-
- avail = the_screen->height - (numwins + the_echo_area->height);
- win = windows;
-
- while (win)
- {
- if ((win->height < WINDOW_MIN_HEIGHT) ||
- (win->height > avail))
- {
- WINDOW *lastwin;
-
- /* Split the space among the available windows. */
- delta_each = avail / numwins;
- delta_leftover = avail - (delta_each * numwins);
-
- for (win = windows; win; win = win->next)
- {
- lastwin = win;
- if (win->prev)
- win->first_row =
- (win->prev->first_row + win->prev->height) + 1;
- win->height = delta_each;
- }
-
- /* Give the leftover space (if any) to the last window. */
- lastwin->height += delta_leftover;
- break;
- }
- else
- win= win->next;
- }
- }
- }
-
- /* Make a new window showing NODE, and return that window structure.
- If NODE is passed as NULL, then show the node showing in the active
- window. If the window could not be made return a NULL pointer. The
- active window is not changed.*/
- WINDOW *
- window_make_window (node)
- NODE *node;
- {
- WINDOW *window;
-
- if (!node)
- node = active_window->node;
-
- /* If there isn't enough room to make another window, return now. */
- if ((active_window->height / 2) < WINDOW_MIN_SIZE)
- return ((WINDOW *)NULL);
-
- /* Make and initialize the new window.
- The fudging about with -1 and +1 is because the following window in the
- chain cannot start at window->height, since that is where the modeline
- for the previous window is displayed. The inverse adjustment is made
- in window_delete_window (). */
- window = (WINDOW *)xmalloc (sizeof (WINDOW));
- window->width = the_screen->width;
- window->height = (active_window->height / 2) - 1;
- #if defined (SPLIT_BEFORE_ACTIVE)
- window->first_row = active_window->first_row;
- #else
- window->first_row = active_window->first_row +
- (active_window->height - window->height);
- #endif
- window->keymap = info_keymap;
- window->goal_column = -1;
- window->modeline = (char *)xmalloc (1 + window->width);
- window->line_starts = (char **)NULL;
- window->flags = W_UpdateWindow | W_WindowVisible;
- window_set_node_of_window (window, node);
-
- /* Adjust the height of the old active window. */
- active_window->height -= (window->height + 1);
- #if defined (SPLIT_BEFORE_ACTIVE)
- active_window->first_row += (window->height + 1);
- #endif
- active_window->flags |= W_UpdateWindow;
-
- /* Readjust the new and old windows so that their modelines and contents
- will be displayed correctly. */
- #if defined (NOTDEF)
- /* We don't have to do this for WINDOW since window_set_node_of_window ()
- already did. */
- window_adjust_pagetop (window);
- window_make_modeline (window);
- #endif /* NOTDEF */
-
- /* We do have to readjust the existing active window. */
- window_adjust_pagetop (active_window);
- window_make_modeline (active_window);
-
- #if defined (SPLIT_BEFORE_ACTIVE)
- /* This window is just before the active one. The active window gets
- bumped down one. The active window is not changed. */
- window->next = active_window;
-
- window->prev = active_window->prev;
- active_window->prev = window;
-
- if (window->prev)
- window->prev->next = window;
- else
- windows = window;
- #else
- /* This window is just after the active one. Which window is active is
- not changed. */
- window->prev = active_window;
- window->next = active_window->next;
- active_window->next = window;
- if (window->next)
- window->next->prev = window;
- #endif /* !SPLIT_BEFORE_ACTIVE */
- return (window);
- }
-
- /* These useful macros make it possible to read the code in
- window_change_window_height (). */
- #define grow_me_shrinking_next(me, next, diff) \
- do { \
- me->height += diff; \
- next->height -= diff; \
- next->first_row += diff; \
- window_adjust_pagetop (next); \
- } while (0)
-
- #define grow_me_shrinking_prev(me, prev, diff) \
- do { \
- me->height += diff; \
- prev->height -= diff; \
- me->first_row -=diff; \
- window_adjust_pagetop (prev); \
- } while (0)
-
- #define shrink_me_growing_next(me, next, diff) \
- do { \
- me->height -= diff; \
- next->height += diff; \
- next->first_row -= diff; \
- window_adjust_pagetop (next); \
- } while (0)
-
- #define shrink_me_growing_prev(me, prev, diff) \
- do { \
- me->height -= diff; \
- prev->height += diff; \
- me->first_row += diff; \
- window_adjust_pagetop (prev); \
- } while (0)
-
- /* Change the height of WINDOW by AMOUNT. This also automagically adjusts
- the previous and next windows in the chain. If there is only one user
- window, then no change takes place. */
- void
- window_change_window_height (window, amount)
- WINDOW *window;
- int amount;
- {
- register WINDOW *win, *prev, *next;
-
- /* If there is only one window, or if the amount of change is zero,
- return immediately. */
- if (!windows->next || amount == 0)
- return;
-
- /* Find this window in our chain. */
- for (win = windows; win; win = win->next)
- if (win == window)
- break;
-
- /* If the window is isolated (i.e., doesn't appear in our window list,
- then quit now. */
- if (!win)
- return;
-
- /* Change the height of this window by AMOUNT, if that is possible.
- It can be impossible if there isn't enough available room on the
- screen, or if the resultant window would be too small. */
-
- prev = window->prev;
- next = window->next;
-
- /* WINDOW decreasing in size? */
- if (amount < 0)
- {
- int abs_amount = -amount; /* It is easier to deal with this way. */
-
- /* If the resultant window would be too small, stop here. */
- if ((window->height - abs_amount) < WINDOW_MIN_HEIGHT)
- return;
-
- /* If we have two neighboring windows, choose the smaller one to get
- larger. */
- if (next && prev)
- {
- if (prev->height < next->height)
- shrink_me_growing_prev (window, prev, abs_amount);
- else
- shrink_me_growing_next (window, next, abs_amount);
- }
- else if (next)
- shrink_me_growing_next (window, next, abs_amount);
- else
- shrink_me_growing_prev (window, prev, abs_amount);
- }
-
- /* WINDOW increasing in size? */
- if (amount > 0)
- {
- int total_avail, next_avail = 0, prev_avail = 0;
-
- if (next)
- next_avail = next->height - WINDOW_MIN_SIZE;
-
- if (prev)
- prev_avail = prev->height - WINDOW_MIN_SIZE;
-
- total_avail = next_avail + prev_avail;
-
- /* If there isn't enough space available to grow this window, give up. */
- if (amount > total_avail)
- return;
-
- /* If there aren't two neighboring windows, or if one of the neighbors
- is larger than the other one by at least AMOUNT, grow that one. */
- if ((next && !prev) || ((next_avail - amount) >= prev_avail))
- grow_me_shrinking_next (window, next, amount);
- else if ((prev && !next) || ((prev_avail - amount) >= next_avail))
- grow_me_shrinking_prev (window, prev, amount);
- else
- {
- int change;
-
- /* This window has two neighbors. They both must be shrunk in to
- make enough space for WINDOW to grow. Make them both the same
- size. */
- if (prev_avail > next_avail)
- {
- change = prev_avail - next_avail;
- grow_me_shrinking_prev (window, prev, change);
- amount -= change;
- }
- else
- {
- change = next_avail - prev_avail;
- grow_me_shrinking_next (window, next, change);
- amount -= change;
- }
-
- /* Both neighbors are the same size. Split the difference in
- AMOUNT between them. */
- while (amount)
- {
- window->height++;
- amount--;
-
- /* Odd numbers grow next, even grow prev. */
- if (amount & 1)
- {
- prev->height--;
- window->first_row--;
- }
- else
- {
- next->height--;
- next->first_row++;
- }
- }
- window_adjust_pagetop (prev);
- window_adjust_pagetop (next);
- }
- }
- if (prev)
- prev->flags |= W_UpdateWindow;
-
- if (next)
- next->flags |= W_UpdateWindow;
-
- window->flags |= W_UpdateWindow;
- window_adjust_pagetop (window);
- }
-
- /* Tile all of the windows currently displayed in the global variable
- WINDOWS. If argument STYLE is TILE_INTERNALS, tile windows displaying
- internal nodes as well, otherwise do not change the height of such
- windows. */
- void
- window_tile_windows (style)
- int style;
- {
- WINDOW *win, *last_adjusted;
- int numwins, avail, per_win_height, leftover;
- int do_internals;
-
- numwins = avail = 0;
- do_internals = (style == TILE_INTERNALS);
-
- for (win = windows; win; win = win->next)
- if (do_internals || !win->node ||
- (win->node->flags & N_IsInternal) == 0)
- {
- avail += win->height;
- numwins++;
- }
-
- if (numwins <= 1 || !the_screen->height)
- return;
-
- /* Find the size for each window. Divide the size of the usable portion
- of the screen by the number of windows. */
- per_win_height = avail / numwins;
- leftover = avail - (per_win_height * numwins);
-
- last_adjusted = (WINDOW *)NULL;
- for (win = windows; win; win = win->next)
- {
- if (do_internals || !win->node ||
- (win->node->flags & N_IsInternal) == 0)
- {
- last_adjusted = win;
- win->height = per_win_height;
- }
- }
-
- if (last_adjusted)
- last_adjusted->height += leftover;
-
- /* Readjust the first_row of every window in the chain. */
- for (win = windows; win; win = win->next)
- {
- if (win->prev)
- win->first_row = win->prev->first_row + win->prev->height + 1;
-
- window_adjust_pagetop (win);
- win->flags |= W_UpdateWindow;
- }
- }
-
- /* Toggle the state of line wrapping in WINDOW. This can do a bit of fancy
- redisplay. */
- void
- window_toggle_wrap (window)
- WINDOW *window;
- {
- if (window->flags & W_NoWrap)
- window->flags &= ~W_NoWrap;
- else
- window->flags |= W_NoWrap;
-
- if (window != the_echo_area)
- {
- char **old_starts;
- int old_lines, old_pagetop;
-
- old_starts = window->line_starts;
- old_lines = window->line_count;
- old_pagetop = window->pagetop;
-
- calculate_line_starts (window);
-
- /* Make sure that point appears within this window. */
- window_adjust_pagetop (window);
-
- /* If the pagetop hasn't changed maybe we can do some scrolling now
- to speed up the display. Many of the line starts will be the same,
- so scrolling here is a very good optimization.*/
- if (old_pagetop == window->pagetop)
- display_scroll_line_starts
- (window, old_pagetop, old_starts, old_lines);
- maybe_free (old_starts);
- }
- window->flags |= W_UpdateWindow;
- }
-
- /* Set WINDOW to display NODE. */
- void
- window_set_node_of_window (window, node)
- WINDOW *window;
- NODE *node;
- {
- window->node = node;
- window->pagetop = 0;
- window->point = 0;
- recalculate_line_starts (window);
- window->flags |= W_UpdateWindow;
- window_adjust_pagetop (window);
- window_make_modeline (window);
- }
-
- /* Delete WINDOW from the list of known windows. If this window was the
- active window, make the next window in the chain be the active window.
- If the active window is the next or previous window, choose that window
- as the recipient of the extra space. Otherwise, prefer the next window. */
- void
- window_delete_window (window)
- WINDOW *window;
- {
- WINDOW *next, *prev, *window_to_fix;
-
- next = window->next;
- prev = window->prev;
-
- /* You cannot delete the only window or a permanent window. */
- if ((!next && !prev) || (window->flags & W_WindowIsPerm))
- return;
-
- if (next)
- next->prev = prev;
-
- if (!prev)
- windows = next;
- else
- prev->next = next;
-
- if (window->line_starts)
- free (window->line_starts);
-
- if (window->modeline)
- free (window->modeline);
-
- if (window == active_window)
- {
- /* If there isn't a next window, then there must be a previous one,
- since we cannot delete the last window. If there is a next window,
- prefer to use that as the active window. */
- if (next)
- active_window = next;
- else
- active_window = prev;
- }
-
- if (next && active_window == next)
- window_to_fix = next;
- else if (prev && active_window == prev)
- window_to_fix = prev;
- else if (next)
- window_to_fix = next;
- else if (prev)
- window_to_fix = prev;
- else
- window_to_fix = windows;
-
- if (window_to_fix->first_row > window->first_row)
- {
- int diff;
-
- /* Try to adjust the visible part of the node so that as little
- text as possible has to move. */
- diff = window_to_fix->first_row - window->first_row;
- window_to_fix->first_row = window->first_row;
-
- window_to_fix->pagetop -= diff;
- if (window_to_fix->pagetop < 0)
- window_to_fix->pagetop = 0;
- }
-
- /* The `+ 1' is to offset the difference between the first_row locations.
- See the code in window_make_window (). */
- window_to_fix->height += window->height + 1;
- window_to_fix->flags |= W_UpdateWindow;
-
- free (window);
- }
-
- /* For every window in CHAIN, set the flags member to have FLAG set. */
- void
- window_mark_chain (chain, flag)
- WINDOW *chain;
- int flag;
- {
- register WINDOW *win;
-
- for (win = chain; win; win = win->next)
- win->flags |= flag;
- }
-
- /* For every window in CHAIN, clear the flags member of FLAG. */
- void
- window_unmark_chain (chain, flag)
- WINDOW *chain;
- int flag;
- {
- register WINDOW *win;
-
- for (win = chain; win; win = win->next)
- win->flags &= ~flag;
- }
-
- /* Return the number of characters it takes to display CHARACTER on the
- screen at HPOS. */
- int
- character_width (character, hpos)
- int character, hpos;
- {
- int printable_limit = 127;
- int width = 1;
-
- if (ISO_Latin_p)
- printable_limit = 160;
-
- if (character > printable_limit)
- width = 3;
- else if (iscntrl (character))
- {
- switch (character)
- {
- case '\r':
- case '\n':
- width = the_screen->width - hpos;
- break;
- case '\t':
- width = ((hpos + 8) & 0xf8) - hpos;
- break;
- default:
- width = 2;
- }
- }
- else if (character == DEL)
- width = 2;
-
- return (width);
- }
-
- /* Return the number of characters it takes to display STRING on the screen
- at HPOS. */
- int
- string_width (string, hpos)
- char *string;
- int hpos;
- {
- register int i, width, this_char_width;
-
- for (width = 0, i = 0; string[i]; i++)
- {
- this_char_width = character_width (string[i], hpos);
- width += this_char_width;
- hpos += this_char_width;
- }
- return (width);
- }
-
- /* Quickly guess the approximate number of lines to that NODE would
- take to display. This really only counts carriage returns. */
- int
- window_physical_lines (node)
- NODE *node;
- {
- register int i, lines;
- char *contents;
-
- if (!node)
- return (0);
-
- contents = node->contents;
- for (i = 0, lines = 1; i < node->nodelen; i++)
- if (contents[i] == '\n')
- lines++;
-
- return (lines);
- }
-
- /* Calculate a list of line starts for the node belonging to WINDOW. The line
- starts are pointers to the actual text within WINDOW->NODE. */
- void
- calculate_line_starts (window)
- WINDOW *window;
- {
- register int i, hpos;
- char **line_starts = (char **)NULL;
- int line_starts_index = 0, line_starts_slots = 0;
- int bump_index;
- NODE *node;
-
- window->line_starts = (char **)NULL;
- window->line_count = 0;
- node = window->node;
-
- if (!node)
- return;
-
- /* Grovel the node starting at the top, and for each line calculate the
- width of the characters appearing in that line. Add each line start
- to our array. */
- i = 0;
- hpos = 0;
- bump_index = 0;
-
- while (i < node->nodelen)
- {
- char *line = node->contents + i;
- unsigned int cwidth, c;
-
- add_pointer_to_array (line, line_starts_index, line_starts,
- line_starts_slots, 100, char *);
- if (bump_index)
- {
- i++;
- bump_index = 0;
- }
-
- while (1)
- {
- c = node->contents[i];
- cwidth = character_width (c, hpos);
-
- /* If this character fits within this line, just do the next one. */
- if ((hpos + cwidth) < window->width)
- {
- i++;
- hpos += cwidth;
- continue;
- }
- else
- {
- /* If this character would position the cursor at the start of
- the next printed screen line, then do the next line. */
- if (c == '\n' || c == '\r' || c == '\t')
- {
- i++;
- hpos = 0;
- break;
- }
- else
- {
- /* This character passes the window width border. Postion
- the cursor after the printed character, but remember this
- line start as where this character is. A bit tricky. */
-
- /* If this window doesn't wrap lines, proceed to the next
- physical line here. */
- if (window->flags & W_NoWrap)
- {
- hpos = 0;
- while (i < node->nodelen && node->contents[i] != '\n')
- i++;
-
- if (node->contents[i] == '\n')
- i++;
- }
- else
- {
- hpos = the_screen->width - hpos;
- bump_index++;
- }
- break;
- }
- }
- }
- }
- window->line_starts = line_starts;
- window->line_count = line_starts_index;
- }
-
- /* Given WINDOW, recalculate the line starts for the node it displays. */
- void
- recalculate_line_starts (window)
- WINDOW *window;
- {
- maybe_free (window->line_starts);
- calculate_line_starts (window);
- }
-
- /* Global variable control redisplay of scrolled windows. If non-zero, it
- is the desired number of lines to scroll the window in order to make
- point visible. A user might set this to 1 for smooth scrolling. If
- set to zero, the line containing point is centered within the window. */
- int window_scroll_step = 0;
-
- /* Adjust the pagetop of WINDOW such that the cursor point will be visible. */
- void
- window_adjust_pagetop (window)
- WINDOW *window;
- {
- register int line = 0;
- char *contents;
-
- if (!window->node)
- return;
-
- contents = window->node->contents;
-
- /* Find the first printed line start which is after WINDOW->point. */
- for (line = 0; line < window->line_count; line++)
- {
- char *line_start;
-
- line_start = window->line_starts[line];
-
- if ((line_start - contents) > window->point)
- break;
- }
-
- /* The line index preceding the line start which is past point is the
- one containing point. */
- line--;
-
- /* If this line appears in the current displayable page, do nothing.
- Otherwise, adjust the top of the page to make this line visible. */
- if ((line < window->pagetop) ||
- (line - window->pagetop > (window->height - 1)))
- {
- /* The user-settable variable "scroll-step" is used to attempt
- to make point visible, iff it is non-zero. If that variable
- is zero, then the line containing point is centered within
- the window. */
- if (window_scroll_step < window->height)
- {
- if ((line < window->pagetop) &&
- ((window->pagetop - window_scroll_step) <= line))
- window->pagetop -= window_scroll_step;
- else if ((line - window->pagetop > (window->height - 1)) &&
- ((line - (window->pagetop + window_scroll_step)
- < window->height)))
- window->pagetop += window_scroll_step;
- else
- window->pagetop = line - ((window->height - 1) / 2);
- }
- else
- window->pagetop = line - ((window->height - 1) / 2);
-
- if (window->pagetop < 0)
- window->pagetop = 0;
- window->flags |= W_UpdateWindow;
- }
- }
-
- /* Return the index of the line containing point. */
- int
- window_line_of_point (window)
- WINDOW *window;
- {
- register int i, start = 0;
-
- /* Try to optimize. Check to see if point is past the pagetop for
- this window, and if so, start searching forward from there. */
- if ((window->pagetop > -1 && window->pagetop < window->line_count) &&
- (window->line_starts[window->pagetop] - window->node->contents)
- <= window->point)
- start = window->pagetop;
-
- for (i = start; i < window->line_count; i++)
- {
- if ((window->line_starts[i] - window->node->contents) > window->point)
- break;
- }
-
- return (i - 1);
- }
-
- /* Get and return the goal column for this window. */
- int
- window_get_goal_column (window)
- WINDOW *window;
- {
- if (!window->node)
- return (-1);
-
- if (window->goal_column != -1)
- return (window->goal_column);
-
- /* Okay, do the work. Find the printed offset of the cursor
- in this window. */
- return (window_get_cursor_column (window));
- }
-
- /* Get and return the printed column offset of the cursor in this window. */
- int
- window_get_cursor_column (window)
- WINDOW *window;
- {
- int i, hpos, end;
- char *line;
-
- i = window_line_of_point (window);
-
- if (i < 0)
- return (-1);
-
- line = window->line_starts[i];
- end = window->point - (line - window->node->contents);
-
- for (hpos = 0, i = 0; i < end; i++)
- hpos += character_width (line[i], hpos);
-
- return (hpos);
- }
-
- /* Count the number of characters in LINE that precede the printed column
- offset of GOAL. */
- int
- window_chars_to_goal (line, goal)
- char *line;
- int goal;
- {
- register int i, check, hpos;
-
- for (hpos = 0, i = 0; line[i] != '\n'; i++)
- {
-
- check = hpos + character_width (line[i], hpos);
-
- if (check > goal)
- break;
-
- hpos = check;
- }
- return (i);
- }
-
- /* Create a modeline for WINDOW, and store it in window->modeline. */
- void
- window_make_modeline (window)
- WINDOW *window;
- {
- register int i;
- char *modeline;
- char location_indicator[4];
- int lines_remaining;
-
- /* Only make modelines for those windows which have one. */
- if (window->flags & W_InhibitMode)
- return;
-
- /* Find the number of lines actually displayed in this window. */
- lines_remaining = window->line_count - window->pagetop;
-
- if (window->pagetop == 0)
- {
- if (lines_remaining <= window->height)
- strcpy (location_indicator, "All");
- else
- strcpy (location_indicator, "Top");
- }
- else
- {
- if (lines_remaining <= window->height)
- strcpy (location_indicator, "Bot");
- else
- {
- float pt, lc;
- int percentage;
-
- pt = (float)window->pagetop;
- lc = (float)window->line_count;
-
- percentage = 100 * (pt / lc);
-
- sprintf (location_indicator, "%2d%%", percentage);
- }
- }
-
- /* Calculate the maximum size of the information to stick in MODELINE. */
- {
- int modeline_len = 0;
- char *parent = (char *)NULL, *filename = "*no file*";
- char *nodename = "*no node*";
- char *update_message = (char *)NULL;
- NODE *node = window->node;
-
- if (node)
- {
- if (node->nodename)
- nodename = node->nodename;
-
- if (node->parent)
- {
- parent = filename_non_directory (node->parent);
- modeline_len += strlen ("Subfile: ") + strlen (node->filename);
- }
-
- if (node->filename)
- filename = filename_non_directory (node->filename);
-
- if (node->flags & N_UpdateTags)
- update_message = "--*** Tags out of Date ***";
- }
-
- if (update_message)
- modeline_len += strlen (update_message);
- modeline_len += strlen (filename);
- modeline_len += strlen (nodename);
- modeline_len += 4; /* strlen (location_indicator). */
-
- /* 10 for the decimal representation of the number of lines in this
- node, and the remainder of the text that can appear in the line. */
- modeline_len += 10 + strlen ("-----Info: (), lines ----, ");
- modeline_len += window->width;
-
- modeline = (char *)xmalloc (1 + modeline_len);
-
- /* Special internal windows have no filename. */
- if (!parent && !*filename)
- sprintf (modeline, "-%s---Info: %s, %d lines --%s--",
- (window->flags & W_NoWrap) ? "$" : "-",
- nodename, window->line_count, location_indicator);
- else
- sprintf (modeline, "-%s%s-Info: (%s)%s, %d lines --%s--",
- (window->flags & W_NoWrap) ? "$" : "-",
- (node && (node->flags & N_IsCompressed)) ? "zz" : "--",
- parent ? parent : filename,
- nodename, window->line_count, location_indicator);
-
- if (parent)
- sprintf (modeline + strlen (modeline), " Subfile: %s", filename);
-
- if (update_message)
- sprintf (modeline + strlen (modeline), "%s", update_message);
-
- i = strlen (modeline);
-
- if (i >= window->width)
- modeline[window->width] = '\0';
- else
- {
- while (i < window->width)
- modeline[i++] = '-';
- modeline[i] = '\0';
- }
-
- strcpy (window->modeline, modeline);
- free (modeline);
- }
- }
-
- /* Make WINDOW start displaying at PERCENT percentage of its node. */
- void
- window_goto_percentage (window, percent)
- WINDOW *window;
- int percent;
- {
- int desired_line;
-
- if (!percent)
- desired_line = 0;
- else
- desired_line =
- (int) ((float)window->line_count * ((float)percent / 100.0));
-
- window->pagetop = desired_line;
- window->point =
- window->line_starts[window->pagetop] - window->node->contents;
- window->flags |= W_UpdateWindow;
- window_make_modeline (window);
- }
-
- /* Get the state of WINDOW, and save it in STATE. */
- void
- window_get_state (window, state)
- WINDOW *window;
- WINDOW_STATE *state;
- {
- state->node = window->node;
- state->pagetop = window->pagetop;
- state->point = window->point;
- }
-
- /* Set the node, pagetop, and point of WINDOW. */
- void
- window_set_state (window, state)
- WINDOW *window;
- WINDOW_STATE *state;
- {
- if (window->node != state->node)
- window_set_node_of_window (window, state->node);
- window->pagetop = state->pagetop;
- window->point = state->point;
- }
-
-
- /* **************************************************************** */
- /* */
- /* Manipulating Home-Made Nodes */
- /* */
- /* **************************************************************** */
-
- /* A place to buffer echo area messages. */
- static NODE *echo_area_node = (NODE *)NULL;
-
- /* Make the node of the_echo_area be an empty one. */
- static void
- free_echo_area ()
- {
- if (echo_area_node)
- {
- maybe_free (echo_area_node->contents);
- free (echo_area_node);
- }
-
- echo_area_node = (NODE *)NULL;
- window_set_node_of_window (the_echo_area, echo_area_node);
- }
-
- /* Clear the echo area, removing any message that is already present.
- The echo area is cleared immediately. */
- void
- window_clear_echo_area ()
- {
- free_echo_area ();
- display_update_one_window (the_echo_area);
- }
-
- /* Make a message appear in the echo area, built from FORMAT, ARG1 and ARG2.
- The arguments are treated similar to printf () arguments, but not all of
- printf () hair is present. The message appears immediately. If there was
- already a message appearing in the echo area, it is removed. */
- void
- window_message_in_echo_area (format, arg1, arg2)
- char *format;
- void *arg1, *arg2;
- {
- free_echo_area ();
- echo_area_node = build_message_node (format, arg1, arg2);
- window_set_node_of_window (the_echo_area, echo_area_node);
- display_update_one_window (the_echo_area);
- }
-
- /* Place a temporary message in the echo area built from FORMAT, ARG1
- and ARG2. The message appears immediately, but does not destroy
- any existing message. A future call to unmessage_in_echo_area ()
- restores the old contents. */
- static NODE **old_echo_area_nodes = (NODE **)NULL;
- static int old_echo_area_nodes_index = 0;
- static int old_echo_area_nodes_slots = 0;
-
- void
- message_in_echo_area (format, arg1, arg2)
- char *format;
- void *arg1, *arg2;
- {
- if (echo_area_node)
- {
- add_pointer_to_array (echo_area_node, old_echo_area_nodes_index,
- old_echo_area_nodes, old_echo_area_nodes_slots,
- 4, NODE *);
- }
- echo_area_node = (NODE *)NULL;
- window_message_in_echo_area (format, arg1, arg2);
- }
-
- void
- unmessage_in_echo_area ()
- {
- free_echo_area ();
-
- if (old_echo_area_nodes_index)
- echo_area_node = old_echo_area_nodes[--old_echo_area_nodes_index];
-
- window_set_node_of_window (the_echo_area, echo_area_node);
- display_update_one_window (the_echo_area);
- }
-
- /* A place to build a message. */
- static char *message_buffer = (char *)NULL;
- static int message_buffer_index = 0;
- static int message_buffer_size = 0;
-
- /* Ensure that there is enough space to stuff LENGTH characters into
- MESSAGE_BUFFER. */
- static void
- message_buffer_resize (length)
- int length;
- {
- if (!message_buffer)
- {
- message_buffer_size = length + 1;
- message_buffer = (char *)xmalloc (message_buffer_size);
- message_buffer_index = 0;
- }
-
- while (message_buffer_size <= message_buffer_index + length)
- message_buffer = (char *)
- xrealloc (message_buffer,
- message_buffer_size += 100 + (2 * length));
- }
-
- /* Format MESSAGE_BUFFER with the results of printing FORMAT with ARG1 and
- ARG2. */
- static void
- build_message_buffer (format, arg1, arg2)
- char *format;
- void *arg1, *arg2;
- {
- register int i, len;
- void *args[2];
- int arg_index = 0;
-
- args[0] = arg1;
- args[1] = arg2;
-
- len = strlen (format);
-
- message_buffer_resize (len);
-
- for (i = 0; format[i]; i++)
- {
- if (format[i] != '%')
- {
- message_buffer[message_buffer_index++] = format[i];
- len--;
- }
- else
- {
- char c;
-
- c = format[++i];
-
- switch (c)
- {
- case '%': /* Insert a percent sign. */
- message_buffer_resize (len + 1);
- message_buffer[message_buffer_index++] = '%';
- break;
-
- case 's': /* Insert the current arg as a string. */
- {
- char *string;
- int string_len;
-
- string = (char *)args[arg_index++];
- string_len = strlen (string);
-
- message_buffer_resize (len + string_len);
- sprintf
- (message_buffer + message_buffer_index, "%s", string);
- message_buffer_index += string_len;
- }
- break;
-
- case 'd': /* Insert the current arg as an integer. */
- {
- int integer;
-
- integer = (int)args[arg_index++];
-
- message_buffer_resize (len + 32);
- sprintf
- (message_buffer + message_buffer_index, "%d", integer);
- message_buffer_index = strlen (message_buffer);
- }
- break;
-
- case 'c': /* Insert the current arg as a character. */
- {
- int character;
-
- character = (int)args[arg_index++];
-
- message_buffer_resize (len + 1);
- message_buffer[message_buffer_index++] = character;
- }
- break;
-
- default:
- abort ();
- }
- }
- }
- message_buffer[message_buffer_index] = '\0';
- }
-
- /* Build a new node which has FORMAT printed with ARG1 and ARG2 as the
- contents. */
- NODE *
- build_message_node (format, arg1, arg2)
- char *format;
- void *arg1, *arg2;
- {
- NODE *node;
-
- message_buffer_index = 0;
- build_message_buffer (format, arg1, arg2);
-
- node = message_buffer_to_node ();
- return (node);
- }
-
- /* Convert the contents of the message buffer to a node. */
- NODE *
- message_buffer_to_node ()
- {
- NODE *node;
-
- node = (NODE *)xmalloc (sizeof (NODE));
- node->filename = (char *)NULL;
- node->parent = (char *)NULL;
- node->nodename = (char *)NULL;
- node->flags = 0;
-
- /* Make sure that this buffer ends with a newline. */
- node->nodelen = 1 + strlen (message_buffer);
- node->contents = (char *)xmalloc (1 + node->nodelen);
- strcpy (node->contents, message_buffer);
- node->contents[node->nodelen - 1] = '\n';
- node->contents[node->nodelen] = '\0';
- return (node);
- }
-
- /* Useful functions can be called from outside of window.c. */
- void
- initialize_message_buffer ()
- {
- message_buffer_index = 0;
- }
-
- /* Print FORMAT with ARG1,2 to the end of the current message buffer. */
- void
- printf_to_message_buffer (format, arg1, arg2)
- char *format;
- void *arg1, *arg2;
- {
- build_message_buffer (format, arg1, arg2);
- }
-
- /* Return the current horizontal position of the "cursor" on the most
- recently output message buffer line. */
- int
- message_buffer_length_this_line ()
- {
- register int i;
-
- if (!message_buffer_index)
- return (0);
-
- for (i = message_buffer_index; i && message_buffer[i - 1] != '\n'; i--);
-
- return (string_width (message_buffer + i, 0));
- }
-
- /* Pad STRING to COUNT characters by inserting blanks. */
- int
- pad_to (count, string)
- int count;
- char *string;
- {
- register int i;
-
- i = strlen (string);
-
- if (i >= count)
- string[i++] = ' ';
- else
- {
- while (i < count)
- string[i++] = ' ';
- }
- string[i] = '\0';
-
- return (i);
- }
-