-
-/* Calculate new sizes for windows in the list FORWARD when their
- compound size goes from TOTAL to SIZE. TOTAL must be greater than
- SIZE. The number of windows in FORWARD is NCHILDREN, and the number
- that can shrink is SHRINKABLE. Fixed-size windows may be shrunk if
- and only if RESIZE_FIXED_P is non-zero. WIDTH_P non-zero means
- shrink columns, otherwise shrink lines.
-
- SAFE_P zero means windows may be sized down to window-min-height
- lines (window-min-window columns for WIDTH_P non-zero). SAFE_P
- non-zero means windows may be sized down to their minimum safe sizes
- taking into account the space needed to display modelines, fringes,
- and scrollbars.
-
- This function returns an allocated array of new sizes that the caller
- must free. A size -1 means the window is fixed and RESIZE_FIXED_P is
- zero. A size zero means the window shall be deleted. Array index 0
- refers to the first window in FORWARD, 1 to the second, and so on.
-
- This function resizes windows proportionally to their size. It also
- tries to preserve smaller windows by resizing larger windows before
- resizing any window to zero. If resize_proportionally is non-nil for
- a specific window, it will attempt to strictly resize that window
- proportionally, even at the expense of deleting smaller windows. */
-static int *
-shrink_windows (int total, int size, int nchildren, int shrinkable,
- int resize_fixed_p, Lisp_Object forward, int width_p, int safe_p)
-{
- int available_resize = 0;
- int *new_sizes, *min_sizes;
- struct window *c;
- Lisp_Object child;
- int smallest = total;
- int total_removed = 0;
- int total_shrink = total - size;
- int i;
-
- new_sizes = xmalloc (sizeof (*new_sizes) * nchildren);
- min_sizes = xmalloc (sizeof (*min_sizes) * nchildren);
-
- for (i = 0, child = forward; !NILP (child); child = c->next, ++i)
- {
- int child_size;
-
- c = XWINDOW (child);
- child_size = WINDOW_TOTAL_SIZE (c, width_p);
-
- if (!resize_fixed_p && window_fixed_size_p (c, width_p, 0))
- new_sizes[i] = -1;
- else
- {
- new_sizes[i] = child_size;
- min_sizes[i] = window_min_size_1 (c, width_p, safe_p);
- if (child_size > min_sizes[i]
- && NILP (c->resize_proportionally))
- available_resize += child_size - min_sizes[i];
- }
- }
- /* We might need to shrink some windows to zero. Find the smallest
- windows and set them to 0 until we can fulfil the new size. */
-
- while (shrinkable > 1 && size + available_resize < total)
- {
- for (i = 0; i < nchildren; ++i)
- if (new_sizes[i] > 0 && smallest > new_sizes[i])
- smallest = new_sizes[i];
-
- for (i = 0; i < nchildren; ++i)
- if (new_sizes[i] == smallest)
- {
- /* Resize this window down to zero. */
- new_sizes[i] = 0;
- if (smallest > min_sizes[i])
- available_resize -= smallest - min_sizes[i];
- available_resize += smallest;
- --shrinkable;
- total_removed += smallest;
-
- /* We don't know what the smallest is now. */
- smallest = total;
-
- /* Out of for, just remove one window at the time and
- check again if we have enough space. */
- break;
- }
- }
-
- /* Now, calculate the new sizes. Try to shrink each window
- proportional to its size. */
- for (i = 0; i < nchildren; ++i)
- {
- if (new_sizes[i] > min_sizes[i])
- {
- int to_shrink = total_shrink * new_sizes[i] / total;
-
- if (new_sizes[i] - to_shrink < min_sizes[i])
- to_shrink = new_sizes[i] - min_sizes[i];
- new_sizes[i] -= to_shrink;
- total_removed += to_shrink;
- }
- }
-
- /* Any reminder due to rounding, we just subtract from windows
- that are left and still can be shrunk. */
- while (total_shrink > total_removed)
- {
- int nonzero_sizes = 0;
-
- for (i = 0; i < nchildren; ++i)
- if (new_sizes[i] > 0)
- ++nonzero_sizes;
-
- for (i = 0; i < nchildren; ++i)
- if (new_sizes[i] > min_sizes[i])
- {
- --new_sizes[i];
- ++total_removed;
-
- /* Out of for, just shrink one window at the time and
- check again if we have enough space. */
- break;
- }
-
- /* Special case, only one window left. */
- if (nonzero_sizes == 1)
- break;
- }
-
- /* Any surplus due to rounding, we add to windows that are left. */
- while (total_shrink < total_removed)
- {
- for (i = 0; i < nchildren; ++i)
- {
- if (new_sizes[i] != 0 && total_shrink < total_removed)
- {
- ++new_sizes[i];
- --total_removed;
- break;
- }
- }
- }
-
- xfree (min_sizes);
-
- return new_sizes;
-}
-
-/* Set WINDOW's height or width to SIZE. WIDTH_P non-zero means set
- WINDOW's width. Resize WINDOW's children, if any, so that they keep
- their proportionate size relative to WINDOW.
-
- If FIRST_ONLY is 1, change only the first of WINDOW's children when
- they are in series. If LAST_ONLY is 1, change only the last of
- WINDOW's children when they are in series.
-
- Propagate WINDOW's top or left edge position to children. Delete
- windows that become too small unless NODELETE_P is 1. When
- NODELETE_P equals 2 do not honor settings for window-min-height and
- window-min-width when resizing windows but use safe defaults instead.
- This should give better behavior when resizing frames. */
-
-static void
-size_window (Lisp_Object window, int size, int width_p, int nodelete_p, int first_only, int last_only)
-{
- struct window *w = XWINDOW (window);
- struct window *c;
- Lisp_Object child, *forward, *sideward;
- int old_size = WINDOW_TOTAL_SIZE (w, width_p);
-
- size = max (0, size);
-
- /* Delete WINDOW if it's too small. */
- if (nodelete_p != 1 && !NILP (w->parent)
- && size < window_min_size_1 (w, width_p, nodelete_p == 2))
- {
- delete_window (window);
- return;
- }
-
- /* Set redisplay hints. */
- w->last_modified = make_number (0);
- w->last_overlay_modified = make_number (0);
- windows_or_buffers_changed++;
- FRAME_WINDOW_SIZES_CHANGED (XFRAME (w->frame)) = 1;
-
- if (width_p)
- {
- sideward = &w->vchild;
- forward = &w->hchild;
- w->total_cols = make_number (size);
- adjust_window_margins (w);
- }
- else
- {
- sideward = &w->hchild;
- forward = &w->vchild;
- w->total_lines = make_number (size);
- w->orig_total_lines = Qnil;
- }
-
- if (!NILP (*sideward))
- {
- /* We have a chain of parallel siblings whose size should all change. */
- for (child = *sideward; !NILP (child); child = c->next)
- {
- c = XWINDOW (child);
- if (width_p)
- c->left_col = w->left_col;
- else
- c->top_line = w->top_line;
- size_window (child, size, width_p, nodelete_p,
- first_only, last_only);
- }
- }
- else if (!NILP (*forward) && last_only)
- {
- /* Change the last in a series of siblings. */
- Lisp_Object last_child;
- int child_size;
-
- child = *forward;
- do
- {
- c = XWINDOW (child);
- last_child = child;
- child = c->next;
- }
- while (!NILP (child));
-
- child_size = WINDOW_TOTAL_SIZE (c, width_p);
- size_window (last_child, size - old_size + child_size,
- width_p, nodelete_p, first_only, last_only);
- }
- else if (!NILP (*forward) && first_only)
- {
- /* Change the first in a series of siblings. */
- int child_size;
-
- child = *forward;
- c = XWINDOW (child);
-
- if (width_p)
- c->left_col = w->left_col;
- else
- c->top_line = w->top_line;
-
- child_size = WINDOW_TOTAL_SIZE (c, width_p);
- size_window (child, size - old_size + child_size,
- width_p, nodelete_p, first_only, last_only);
- }
- else if (!NILP (*forward))
- {
- int fixed_size, each IF_LINT (= 0), extra IF_LINT (= 0), n;
- int resize_fixed_p, nfixed;
- int last_pos, first_pos, nchildren, total;
- int *new_sizes = NULL;
-
- /* Determine the fixed-size portion of this window, and the
- number of child windows. */
- fixed_size = nchildren = nfixed = total = 0;
- for (child = *forward; !NILP (child); child = c->next, ++nchildren)
- {
- int child_size;
-
- c = XWINDOW (child);
- child_size = WINDOW_TOTAL_SIZE (c, width_p);
- total += child_size;
-
- if (window_fixed_size_p (c, width_p, 0))
- {
- fixed_size += child_size;
- ++nfixed;
- }
- }
-
- /* If the new size is smaller than fixed_size, or if there
- aren't any resizable windows, allow resizing fixed-size
- windows. */
- resize_fixed_p = nfixed == nchildren || size < fixed_size;
-
- /* Compute how many lines/columns to add/remove to each child. The
- value of extra takes care of rounding errors. */
- n = resize_fixed_p ? nchildren : nchildren - nfixed;
- if (size < total && n > 1)
- new_sizes = shrink_windows (total, size, nchildren, n,
- resize_fixed_p, *forward, width_p,
- nodelete_p == 2);
- else
- {
- each = (size - total) / n;
- extra = (size - total) - n * each;
- }
-
- /* Compute new children heights and edge positions. */
- first_pos = width_p ? XINT (w->left_col) : XINT (w->top_line);
- last_pos = first_pos;
- for (n = 0, child = *forward; !NILP (child); child = c->next, ++n)
- {
- int new_child_size, old_child_size;
-
- c = XWINDOW (child);
- old_child_size = WINDOW_TOTAL_SIZE (c, width_p);
- new_child_size = old_child_size;
-
- /* The top or left edge position of this child equals the
- bottom or right edge of its predecessor. */
- if (width_p)
- c->left_col = make_number (last_pos);
- else
- c->top_line = make_number (last_pos);
-
- /* If this child can be resized, do it. */
- if (resize_fixed_p || !window_fixed_size_p (c, width_p, 0))
- {
- new_child_size =
- new_sizes ? new_sizes[n] : old_child_size + each + extra;
- extra = 0;
- }
-
- /* Set new size. Note that size_window also propagates
- edge positions to children, so it's not a no-op if we
- didn't change the child's size. */
- size_window (child, new_child_size, width_p, 1,
- first_only, last_only);
-
- /* Remember the bottom/right edge position of this child; it
- will be used to set the top/left edge of the next child. */
- last_pos += new_child_size;
- }
-
- xfree (new_sizes);
-
- /* We should have covered the parent exactly with child windows. */
- xassert (size == last_pos - first_pos);
-
- /* Now delete any children that became too small. */
- if (nodelete_p != 1)
- for (child = *forward; !NILP (child); child = c->next)
- {
- int child_size;
-
- c = XWINDOW (child);
- child_size = WINDOW_TOTAL_SIZE (c, width_p);
- size_window (child, child_size, width_p, nodelete_p,
- first_only, last_only);
- }
- }
-}
-
-/* Set WINDOW's height to HEIGHT, and recursively change the height of
- WINDOW's children. NODELETE zero means windows that have become
- smaller than window-min-height in the process may be deleted.
- NODELETE 1 means never delete windows that become too small in the
- process. (The caller should check later and do so if appropriate.)
- NODELETE 2 means delete only windows that have become too small to be
- displayed correctly. */
-
-void
-set_window_height (Lisp_Object window, int height, int nodelete)
-{
- size_window (window, height, 0, nodelete, 0, 0);
-}
-
-/* Set WINDOW's width to WIDTH, and recursively change the width of
- WINDOW's children. NODELETE zero means windows that have become
- smaller than window-min-width in the process may be deleted.
- NODELETE 1 means never delete windows that become too small in the
- process. (The caller should check later and do so if appropriate.)
- NODELETE 2 means delete only windows that have become too small to be
- displayed correctly. */
-
-void
-set_window_width (Lisp_Object window, int width, int nodelete)
-{
- size_window (window, width, 1, nodelete, 0, 0);
-}
-
-/* Change window heights in windows rooted in WINDOW by N lines. */
-
-void
-change_window_heights (Lisp_Object window, int n)
-{
- struct window *w = XWINDOW (window);
-
- XSETFASTINT (w->top_line, XFASTINT (w->top_line) + n);
- XSETFASTINT (w->total_lines, XFASTINT (w->total_lines) - n);
-
- if (INTEGERP (w->orig_top_line))
- XSETFASTINT (w->orig_top_line, XFASTINT (w->orig_top_line) + n);
- if (INTEGERP (w->orig_total_lines))
- XSETFASTINT (w->orig_total_lines, XFASTINT (w->orig_total_lines) - n);
-
- /* Handle just the top child in a vertical split. */
- if (!NILP (w->vchild))
- change_window_heights (w->vchild, n);
-
- /* Adjust all children in a horizontal split. */
- for (window = w->hchild; !NILP (window); window = w->next)
- {
- w = XWINDOW (window);
- change_window_heights (window, n);
- }
-}
-