#include "info.h"
#include "nodes.h"
#include "window.h"
#include "display.h"
#include "info-utils.h"
#include "infomap.h"
WINDOW *the_screen = (WINDOW *)NULL;
WINDOW *the_echo_area = (WINDOW *)NULL;
WINDOW *windows = (WINDOW *)NULL;
WINDOW *active_window = (WINDOW *)NULL;
#define ECHO_AREA_HEIGHT 1
#define echo_area_required (1 + the_echo_area->height)
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));
the_echo_area->goal_column = -1;
active_window->goal_column = -1;
the_screen->goal_column = -1;
active_window->flags = W_WindowVisible;
the_echo_area->flags = W_WindowIsPerm | W_InhibitMode | W_WindowVisible;
the_screen->flags = W_WindowIsPerm;
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->keymap = echo_area_keymap;
active_window->keymap = info_keymap;
}
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 (width == the_screen->width && height == the_screen->height)
return;
if (height < (WINDOW_MIN_SIZE + the_echo_area->height))
height = 0;
if (width < 0)
width = 0;
for (numwins = 0, win = windows; win; win = win->next, numwins++);
while ((height - echo_area_required) / numwins <= WINDOW_MIN_SIZE)
{
if (!windows->next)
{
windows->height = 0;
maybe_free (windows->line_starts);
windows->line_starts = (char **)NULL;
windows->line_count = 0;
break;
}
for (win = windows; win; win = win->next)
if (win->flags & W_TempWindow)
break;
if (!win)
win = windows;
if (window_deletion_notifier)
(*window_deletion_notifier) (win);
window_delete_window (win);
numwins--;
}
delta_height = height - the_screen->height;
the_screen->height = height;
the_screen->width = width;
the_echo_area->first_row = height - the_echo_area->height;
the_echo_area->width = width;
if ((!windows->next) && ((windows->height == 0) && (delta_height < 0)))
return;
delta_each = delta_height / numwins;
delta_leftover = delta_height - (delta_each * numwins);
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 (win->height == delta_each)
win->height -= (1 + the_echo_area->height);
if (win->prev)
win->first_row = (win->prev->first_row + win->prev->height) + 1;
if (!win->next)
win->height += delta_leftover;
if (win->node)
recalculate_line_starts (win);
win->flags |= W_UpdateWindow;
}
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;
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;
}
lastwin->height += delta_leftover;
break;
}
else
win= win->next;
}
}
}
WINDOW *
window_make_window (node)
NODE *node;
{
WINDOW *window;
if (!node)
node = active_window->node;
if ((active_window->height / 2) < WINDOW_MIN_SIZE)
return ((WINDOW *)NULL);
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);
active_window->height -= (window->height + 1);
#if defined (SPLIT_BEFORE_ACTIVE)
active_window->first_row += (window->height + 1);
#endif
active_window->flags |= W_UpdateWindow;
#if defined (NOTDEF)
window_adjust_pagetop (window);
window_make_modeline (window);
#endif
window_adjust_pagetop (active_window);
window_make_modeline (active_window);
#if defined (SPLIT_BEFORE_ACTIVE)
window->next = active_window;
window->prev = active_window->prev;
active_window->prev = window;
if (window->prev)
window->prev->next = window;
else
windows = window;
#else
window->prev = active_window;
window->next = active_window->next;
active_window->next = window;
if (window->next)
window->next->prev = window;
#endif
return (window);
}
#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)
void
window_change_window_height (window, amount)
WINDOW *window;
int amount;
{
register WINDOW *win, *prev, *next;
if (!windows->next || amount == 0)
return;
for (win = windows; win; win = win->next)
if (win == window)
break;
if (!win)
return;
prev = window->prev;
next = window->next;
if (amount < 0)
{
int abs_amount = -amount;
if ((window->height - abs_amount) < WINDOW_MIN_HEIGHT)
return;
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);
}
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 (amount > total_avail)
return;
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;
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;
}
while (amount)
{
window->height++;
amount--;
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);
}
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;
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;
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;
}
}
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);
window_adjust_pagetop (window);
if (old_pagetop == window->pagetop)
display_scroll_line_starts
(window, old_pagetop, old_starts, old_lines);
maybe_free (old_starts);
}
window->flags |= W_UpdateWindow;
}
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);
}
void
window_delete_window (window)
WINDOW *window;
{
WINDOW *next, *prev, *window_to_fix;
next = window->next;
prev = window->prev;
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 (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;
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;
}
window_to_fix->height += window->height + 1;
window_to_fix->flags |= W_UpdateWindow;
free (window);
}
void
window_mark_chain (chain, flag)
WINDOW *chain;
int flag;
{
register WINDOW *win;
for (win = chain; win; win = win->next)
win->flags |= flag;
}
void
window_unmark_chain (chain, flag)
WINDOW *chain;
int flag;
{
register WINDOW *win;
for (win = chain; win; win = win->next)
win->flags &= ~flag;
}
int
character_width (character, hpos)
int character, hpos;
{
int printable_limit = 127;
int width = 1;
if (ISO_Latin_p)
printable_limit = 255;
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);
}
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);
}
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);
}
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;
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 ((hpos + cwidth) < window->width)
{
i++;
hpos += cwidth;
continue;
}
else
{
if (c == '\n' || c == '\r' || c == '\t')
{
i++;
hpos = 0;
break;
}
else
{
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;
}
void
recalculate_line_starts (window)
WINDOW *window;
{
maybe_free (window->line_starts);
calculate_line_starts (window);
}
int window_scroll_step = 0;
void
window_adjust_pagetop (window)
WINDOW *window;
{
register int line = 0;
char *contents;
if (!window->node)
return;
contents = window->node->contents;
for (line = 0; line < window->line_count; line++)
{
char *line_start;
line_start = window->line_starts[line];
if ((line_start - contents) > window->point)
break;
}
line--;
if ((line < window->pagetop) ||
(line - window->pagetop > (window->height - 1)))
{
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;
}
}
int
window_line_of_point (window)
WINDOW *window;
{
register int i, start = 0;
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);
}
int
window_get_goal_column (window)
WINDOW *window;
{
if (!window->node)
return (-1);
if (window->goal_column != -1)
return (window->goal_column);
return (window_get_cursor_column (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);
}
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);
}
void
window_make_modeline (window)
WINDOW *window;
{
register int i;
char *modeline;
char location_indicator[4];
int lines_remaining;
if (window->flags & W_InhibitMode)
return;
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);
}
}
{
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;
modeline_len += 10 + strlen (_("-----Info: (), lines ----, "));
modeline_len += window->width;
modeline = (char *)xmalloc (1 + modeline_len);
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);
}
}
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);
}
void
window_get_state (window, state)
WINDOW *window;
WINDOW_STATE *state;
{
state->node = window->node;
state->pagetop = window->pagetop;
state->point = window->point;
}
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;
}
static NODE *echo_area_node = (NODE *)NULL;
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);
}
void
window_clear_echo_area ()
{
free_echo_area ();
display_update_one_window (the_echo_area);
}
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);
}
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);
}
static char *message_buffer = (char *)NULL;
static int message_buffer_index = 0;
static int message_buffer_size = 0;
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));
}
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 '%':
message_buffer_resize (len + 1);
message_buffer[message_buffer_index++] = '%';
break;
case 's':
{
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':
{
long long_val;
int integer;
long_val = (long)args[arg_index++];
integer = (int)long_val;
message_buffer_resize (len + 32);
sprintf
(message_buffer + message_buffer_index, "%d", integer);
message_buffer_index = strlen (message_buffer);
}
break;
case 'c':
{
long long_val;
int character;
long_val = (long)args[arg_index++];
character = (int)long_val;
message_buffer_resize (len + 1);
message_buffer[message_buffer_index++] = character;
}
break;
default:
abort ();
}
}
}
message_buffer[message_buffer_index] = '\0';
}
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);
}
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;
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);
}
void
initialize_message_buffer ()
{
message_buffer_index = 0;
}
void
printf_to_message_buffer (format, arg1, arg2)
char *format;
void *arg1, *arg2;
{
build_message_buffer (format, arg1, arg2);
}
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));
}
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);
}