Merged in changes from CVS HEAD
[bpt/emacs.git] / src / window.c
index b5df84b..8f971ab 100644 (file)
@@ -21,6 +21,8 @@ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 Boston, MA 02111-1307, USA.  */
 
 #include <config.h>
+#include <stdio.h>
+
 #include "lisp.h"
 #include "buffer.h"
 #include "keyboard.h"
@@ -305,7 +307,8 @@ used by that frame.  */)
 }
 
 DEFUN ("window-minibuffer-p", Fwindow_minibuffer_p, Swindow_minibuffer_p, 0, 1, 0,
-       doc: /* Returns non-nil if WINDOW is a minibuffer window.  */)
+       doc: /* Returns non-nil if WINDOW is a minibuffer window.
+WINDOW defaults to the selected window.  */)
      (window)
      Lisp_Object window;
 {
@@ -392,6 +395,17 @@ decode_window (window)
   return XWINDOW (window);
 }
 
+static struct window *
+decode_any_window (window)
+     register Lisp_Object window;
+{
+  if (NILP (window))
+    return XWINDOW (selected_window);
+
+  CHECK_WINDOW (window);
+  return XWINDOW (window);
+}
+
 DEFUN ("window-buffer", Fwindow_buffer, Swindow_buffer, 0, 1, 0,
        doc: /* Return the buffer that WINDOW is displaying.  */)
      (window)
@@ -405,7 +419,7 @@ DEFUN ("window-height", Fwindow_height, Swindow_height, 0, 1, 0,
      (window)
      Lisp_Object window;
 {
-  return decode_window (window)->total_lines;
+  return decode_any_window (window)->total_lines;
 }
 
 DEFUN ("window-width", Fwindow_width, Swindow_width, 0, 1, 0,
@@ -416,7 +430,7 @@ use  (let ((edges (window-edges))) (- (nth 2 edges) (nth 0 edges))).  */)
      (window)
      Lisp_Object window;
 {
-  return make_number (window_box_text_cols (decode_window (window)));
+  return make_number (window_box_text_cols (decode_any_window (window)));
 }
 
 DEFUN ("window-hscroll", Fwindow_hscroll, Swindow_hscroll, 0, 1, 0,
@@ -489,7 +503,7 @@ To get the edges of the actual text area, use `window-inside-edges'.  */)
      (window)
      Lisp_Object window;
 {
-  register struct window *w = decode_window (window);
+  register struct window *w = decode_any_window (window);
 
   return Fcons (make_number (WINDOW_LEFT_EDGE_COL (w)),
         Fcons (make_number (WINDOW_TOP_EDGE_LINE (w)),
@@ -509,7 +523,7 @@ To get the edges of the actual text area, use `window-inside-pixel-edges'.  */)
      (window)
      Lisp_Object window;
 {
-  register struct window *w = decode_window (window);
+  register struct window *w = decode_any_window (window);
 
   return Fcons (make_number (WINDOW_LEFT_EDGE_X (w)),
         Fcons (make_number (WINDOW_TOP_EDGE_Y (w)),
@@ -528,7 +542,7 @@ display margins, fringes, header line, and/or mode line.  */)
      (window)
      Lisp_Object window;
 {
-  register struct window *w = decode_window (window);
+  register struct window *w = decode_any_window (window);
 
   return list4 (make_number (WINDOW_BOX_LEFT_EDGE_COL (w)
                             + WINDOW_LEFT_MARGIN_COLS (w)
@@ -552,7 +566,7 @@ display margins, fringes, header line, and/or mode line.  */)
      (window)
      Lisp_Object window;
 {
-  register struct window *w = decode_window (window);
+  register struct window *w = decode_any_window (window);
 
   return list4 (make_number (WINDOW_BOX_LEFT_EDGE_X (w)
                             + WINDOW_LEFT_MARGIN_WIDTH (w)
@@ -632,8 +646,7 @@ coordinates_in_window (w, x, y)
      scroll bars.  */
 
   if (WINDOW_WANTS_MODELINE_P (w)
-      && *y >= bottom_y - CURRENT_MODE_LINE_HEIGHT (w)
-      && *y < bottom_y)
+      && *y >= bottom_y - CURRENT_MODE_LINE_HEIGHT (w))
     {
       part = ON_MODE_LINE;
 
@@ -662,7 +675,6 @@ coordinates_in_window (w, x, y)
     }
 
   if (WINDOW_WANTS_HEADER_LINE_P (w)
-      && *y >= top_y
       && *y < top_y + CURRENT_HEADER_LINE_HEIGHT (w))
     {
       part = ON_HEADER_LINE;
@@ -708,7 +720,7 @@ coordinates_in_window (w, x, y)
              ? (*x >= left_x + WINDOW_LEFT_FRINGE_WIDTH (w))
              : (*x < left_x + lmargin_width)))
        {
-         *x -= x0;
+         *x -= left_x;
          if (WINDOW_HAS_FRINGES_OUTSIDE_MARGINS (w))
            *x -= WINDOW_LEFT_FRINGE_WIDTH (w);
          *y -= top_y;
@@ -773,7 +785,7 @@ If they are in the windows's left or right marginal areas, `left-margin'\n\
   int x, y;
   Lisp_Object lx, ly;
 
-  CHECK_LIVE_WINDOW (window);
+  CHECK_WINDOW (window);
   w = XWINDOW (window);
   f = XFRAME (w->frame);
   CHECK_CONS (coordinates);
@@ -1831,6 +1843,7 @@ enum window_loop
   DELETE_BUFFER_WINDOWS,       /* Arg is buffer */
   GET_LARGEST_WINDOW,
   UNSHOW_BUFFER,               /* Arg is buffer */
+  REDISPLAY_BUFFER_WINDOWS,    /* Arg is buffer */
   CHECK_ALL_WINDOWS
 };
 
@@ -2029,6 +2042,14 @@ window_loop (type, obj, mini, frames)
              }
            break;
 
+         case REDISPLAY_BUFFER_WINDOWS:
+           if (EQ (w->buffer, obj))
+             {
+               mark_window_display_accurate (window, 0);
+               best_window = window;
+             }
+           break;
+
            /* Check for a window that has a killed buffer.  */
          case CHECK_ALL_WINDOWS:
            if (! NILP (w->buffer)
@@ -2538,6 +2559,118 @@ adjust_window_margins (w)
   return 1;
 }
 
+/* Calculate new sizes for windows in the list FORWARD when the window 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.
+   The minimum size a window can have is MIN_SIZE.
+   If we are shrinking fixed windows, RESIZE_FIXED_P is non-zero.
+   If we are shrinking columns, WIDTH_P is non-zero, otherwise we are
+   shrinking rows.
+
+   This function returns an allocated array of new sizes that the caller
+   must free.  The size -1 means the window is fixed and RESIZE_FIXED_P
+   is zero.  Array index 0 refers to the first window in FORWARD, 1 to
+   the second, and so on.
+
+   This function tries to keep windows at least at the minimum size
+   and resize other windows before it resizes any window to zero (i.e.
+   delete that window).
+
+   Windows are resized proportional to their size, so bigger windows
+   shrink more than smaller windows.  */
+static int *
+shrink_windows (total, size, nchildren, shrinkable,
+                min_size, resize_fixed_p, forward, width_p)
+     int total, size, nchildren, shrinkable, min_size;
+     int resize_fixed_p, width_p;
+     Lisp_Object forward;
+{
+  int available_resize = 0;
+  int *new_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);
+
+  for (i = 0, child = forward; !NILP (child); child = c->next, ++i)
+    {
+      int child_size;
+
+      c = XWINDOW (child);
+      child_size = width_p ? XINT (c->total_cols) : XINT (c->total_lines);
+
+      if (! resize_fixed_p && window_fixed_size_p (c, width_p, 0))
+        new_sizes[i] = -1;
+      else
+        {
+          new_sizes[i] = child_size;
+          if (child_size > min_size)
+            available_resize += child_size - min_size;
+        }
+    }
+  /* 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_size)
+              available_resize -= smallest - min_size;
+            available_resize += smallest;
+            --shrinkable;
+            total_removed += smallest;
+
+            /* 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_size)
+        {
+          int to_shrink = total_shrink*new_sizes[i]/total;
+          if (new_sizes[i] - to_shrink < min_size)
+            to_shrink = new_sizes[i] - min_size;
+          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)
+    {
+      for (i = 0; i < nchildren; ++i)
+        if (new_sizes[i] > min_size)
+          {
+            --new_sizes[i];
+            ++total_removed;
+
+            /* Out of for, just shrink one window at the time and
+               check again if we have enough space.  */
+            break;
+          }
+    }
+
+  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
@@ -2641,6 +2774,7 @@ size_window (window, size, width_p, nodelete_p)
       int fixed_size, each, extra, n;
       int resize_fixed_p, nfixed;
       int last_pos, first_pos, nchildren, total;
+      int *new_sizes = NULL;
 
       /* Determine the fixed-size portion of the this window, and the
         number of child windows.  */
@@ -2665,16 +2799,22 @@ size_window (window, size, width_p, nodelete_p)
         windows.  */
       resize_fixed_p = nfixed == nchildren || size < fixed_size;
 
-      /* Compute how many lines/columns to add to each child.  The
+      /* 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;
-      each = (size - total) / n;
-      extra = (size - total) - n * each;
+      if (size < total && n > 1)
+        new_sizes = shrink_windows (total, size, nchildren, n, min_size,
+                                    resize_fixed_p, *forward, width_p);
+      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 (child = *forward; !NILP (child); child = c->next)
+      for (n = 0, child = *forward; !NILP (child); child = c->next, ++n)
        {
          int new_size, old_size;
 
@@ -2692,7 +2832,7 @@ size_window (window, size, width_p, nodelete_p)
          /* If this child can be resized, do it.  */
          if (resize_fixed_p || !window_fixed_size_p (c, width_p, 0))
            {
-             new_size = old_size + each + extra;
+             new_size = new_sizes ? new_sizes[n] : old_size + each + extra;
              extra = 0;
            }
 
@@ -2703,9 +2843,11 @@ size_window (window, size, width_p, nodelete_p)
 
          /* 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_size;
+          last_pos += new_size;
        }
 
+      if (new_sizes) xfree (new_sizes);
+
       /* We should have covered the parent exactly with child windows.  */
       xassert (size == last_pos - first_pos);
 
@@ -2853,6 +2995,9 @@ set_window_buffer (window, buffer, run_hooks_p, keep_margins_p)
 
       /* This may call adjust_window_margins three times, so
         temporarily disable window margins.  */
+      Lisp_Object save_left = w->left_margin_cols;
+      Lisp_Object save_right = w->right_margin_cols;
+
       w->left_margin_cols = w->right_margin_cols = Qnil;
 
       Fset_window_fringes (window,
@@ -2863,6 +3008,9 @@ set_window_buffer (window, buffer, run_hooks_p, keep_margins_p)
                               b->scroll_bar_width,
                               b->vertical_scroll_bar_type, Qnil);
 
+      w->left_margin_cols = save_left;
+      w->right_margin_cols = save_right;
+
       Fset_window_margins (window,
                           b->left_margin_cols, b->right_margin_cols);
     }
@@ -3303,6 +3451,46 @@ displayed.  */)
   return display_buffer_1 (window);
 }
 
+
+DEFUN ("force-window-update", Fforce_window_update, Sforce_window_update,
+       0, 1, 0,
+       doc: /* Force redisplay of all windows.
+If optional arg OBJECT is a window, force redisplay of that window only.
+If OBJECT is a buffer or buffer name, force redisplay of all windows 
+displaying that buffer.  */)
+     (object)
+     Lisp_Object object;
+{
+  if (NILP (object))
+    {
+      windows_or_buffers_changed++;
+      return Qt;
+    }
+
+  if (WINDOWP (object))
+    {
+      mark_window_display_accurate (object, 0);
+      return Qt;
+    }
+    
+  if (STRINGP (object))
+    object = Fget_buffer (object);
+  if (BUFFERP (object) && !NILP (XBUFFER (object)->name))
+    {
+      /* Walk all windows looking for buffer, and force update
+        of each of those windows.  */
+
+      object = window_loop (REDISPLAY_BUFFER_WINDOWS, object, 0, Qvisible);
+      return NILP (object) ? Qnil : Qt;
+    }
+
+  /* If nothing suitable was found, just return.
+     We could signal an error, but this feature will typically be used
+     asynchronously in timers or process sentinels, so we don't.  */
+  return Qnil;
+}
+
+
 void
 temp_output_buffer_show (buf)
      register Lisp_Object buf;
@@ -5719,38 +5907,32 @@ usage: (save-window-excursion BODY ...)  */)
 DEFUN ("set-window-margins", Fset_window_margins, Sset_window_margins,
        2, 3, 0,
        doc: /* Set width of marginal areas of window WINDOW.
-If window is nil, set margins of the currently selected window.
-First parameter LEFT-WIDTH specifies the number of character
-cells to reserve for the left marginal area.  Second parameter
-RIGHT-WIDTH does the same for the right marginal area.
-A nil width parameter means no margin.  */)
+If WINDOW is nil, set margins of the currently selected window.
+Second arg LEFT-WIDTH specifies the number of character cells to
+reserve for the left marginal area.  Optional third arg RIGHT-WIDTH
+does the same for the right marginal area.  A nil width parameter
+means no margin.  */)
      (window, left, right)
      Lisp_Object window, left, right;
 {
   struct window *w = decode_window (window);
 
-  /* TODO: It doesn't make sense to use FLOATs here, since
-     the rest of the code assumes they are integers.
-     So don't allow floats!  ++KFS */
+  /* Translate negative or zero widths to nil.
+     Margins that are too wide have to be checked elsewhere.  */
 
   if (!NILP (left))
-    CHECK_NUMBER_OR_FLOAT (left);
-  if (!NILP (right))
-    CHECK_NUMBER_OR_FLOAT (right);
+    {
+      CHECK_NUMBER (left);
+      if (XINT (left) <= 0)
+       left = Qnil;
+    }
 
-  /* Check widths < 0 and translate a zero width to nil.
-     Margins that are too wide have to be checked elsewhere.  */
-  if ((INTEGERP (left) && XINT (left) < 0)
-      || (FLOATP (left) && XFLOAT_DATA (left) <= 0))
-     XSETFASTINT (left, 0);
-  if (INTEGERP (left) && XFASTINT (left) == 0)
-    left = Qnil;
-
-  if ((INTEGERP (right) && XINT (right) < 0)
-      || (FLOATP (right) && XFLOAT_DATA (right) <= 0))
-    XSETFASTINT (right, 0);
-  if (INTEGERP (right) && XFASTINT (right) == 0)
-    right = Qnil;
+  if (!NILP (right))
+    {
+      CHECK_NUMBER (right);
+      if (XINT (right) <= 0)
+       right = Qnil;
+    }
 
   if (!EQ (w->left_margin_cols, left)
       || !EQ (w->right_margin_cols, right))
@@ -5790,15 +5972,17 @@ as nil.  */)
 
 DEFUN ("set-window-fringes", Fset_window_fringes, Sset_window_fringes,
        2, 4, 0,
-       doc: /* Set width of fringes of window WINDOW.
-
-If window is nil, set fringes of the currently selected window.
-Second parameter LEFT-WIDTH specifies the number of pixels to reserve
-for the left fringe.  Third parameter RIGHT-WIDTH does the same for
-the right fringe.  Fourth parameter OUTSIDE-MARGINS non-nil specifies
-that fringes are drawn outside of the display margins; by default, fringes
-are drawn between display marginal areas and the text area.
-A nil width parameter means to use the frame's corresponding fringe width.  */)
+       doc: /* Set the fringe widths of window WINDOW.
+If WINDOW is nil, set the fringe widths of the currently selected
+window.
+Second arg LEFT-WIDTH specifies the number of pixels to reserve for
+the left fringe.  Optional third arg RIGHT-WIDTH specifies the right
+fringe width.  If a fringe width arg is nil, that means to use the
+frame's default fringe width.  Default fringe widths can be set with
+the command `set-fringe-style'.
+If optional fourth arg OUTSIDE-MARGINS is non-nil, draw the fringes
+outside of the display margins.  By default, fringes are drawn between
+display marginal areas and the text area.  */)
      (window, left, right, outside_margins)
      Lisp_Object window, left, right, outside_margins;
 {
@@ -5834,9 +6018,7 @@ DEFUN ("window-fringes", Fwindow_fringes, Swindow_fringes,
        0, 1, 0,
        doc: /* Get width of fringes of window WINDOW.
 If WINDOW is omitted or nil, use the currently selected window.
-Value is a list of the form (LEFT-WIDTH RIGHT-WIDTH OUTSIDE-MARGINS).
-If a window specific fringe width is not set, its width will be returned
-as nil.  */)
+Value is a list of the form (LEFT-WIDTH RIGHT-WIDTH OUTSIDE-MARGINS).  */)
      (window)
      Lisp_Object window;
 {
@@ -5861,7 +6043,8 @@ Second parameter WIDTH specifies the pixel width for the scroll bar;
 this is automatically adjusted to a multiple of the frame column width.
 Third parameter VERTICAL-TYPE specifies the type of the vertical scroll
 bar: left, right, or nil.
-A width of nil and type of t means to use the frame's corresponding value.  */)
+If WIDTH is nil, use the frame's scroll-bar width.
+If TYPE is t, use the frame's scroll-bar type.  */)
      (window, width, vertical_type, horizontal_type)
      Lisp_Object window, width, vertical_type, horizontal_type;
 {
@@ -5873,6 +6056,12 @@ A width of nil and type of t means to use the frame's corresponding value.  */)
   if (XINT (width) == 0)
     vertical_type = Qnil;
 
+  if (!(EQ (vertical_type, Qnil)
+       || EQ (vertical_type, Qleft) 
+       || EQ (vertical_type, Qright)
+       || EQ (vertical_type, Qt)))
+    error ("Invalid type of vertical scroll bar");
+
   if (!EQ (w->scroll_bar_width, width)
       || !EQ (w->vertical_scroll_bar_type, vertical_type))
     {
@@ -5896,7 +6085,9 @@ DEFUN ("window-scroll-bars", Fwindow_scroll_bars, Swindow_scroll_bars,
        0, 1, 0,
        doc: /* Get width and type of scroll bars of window WINDOW.
 If WINDOW is omitted or nil, use the currently selected window.
-Value is a list of the form (WIDTH COLS VERTICAL-TYPE HORIZONTAL-TYPE).  */)
+Value is a list of the form (WIDTH COLS VERTICAL-TYPE HORIZONTAL-TYPE).
+If WIDTH is nil or TYPE is t, the window is using the frame's corresponding
+value.  */)
      (window)
      Lisp_Object window;
 {
@@ -6202,7 +6393,7 @@ and scrolling positions.  */)
 void
 init_window_once ()
 {
-  struct frame *f = make_terminal_frame ();
+  struct frame *f = make_initial_frame ();
   XSETFRAME (selected_frame, f);
   Vterminal_frame = selected_frame;
   minibuf_window = f->minibuffer_window;
@@ -6310,6 +6501,11 @@ In the latter case, FUNCTION is called with BUFFER as the first argument,
 followed by OTHER-ARGS--it can display BUFFER in any way it likes.
 All this is done by the function found in `special-display-function'.
 
+If the specified frame parameters include (same-buffer . t), the
+buffer is displayed in the currently selected window.  Otherwise, if
+they include (same-frame . t), the buffer is displayed in a new window
+in the currently selected frame.
+
 If this variable appears \"not to work\", because you add a name to it
 but that buffer still appears in the selected window, look at the
 values of `same-window-buffer-names' and `same-window-regexps'.
@@ -6330,6 +6526,11 @@ In the latter case, FUNCTION is called with the buffer as first argument,
 followed by OTHER-ARGS--it can display the buffer in any way it likes.
 All this is done by the function found in `special-display-function'.
 
+If the specified frame parameters include (same-buffer . t), the
+buffer is displayed in the currently selected window.  Otherwise, if
+they include (same-frame . t), the buffer is displayed in a new window
+in the currently selected frame.
+
 If this variable appears \"not to work\", because you add a regexp to it
 but the matching buffers still appear in the selected window, look at the
 values of `same-window-buffer-names' and `same-window-regexps'.
@@ -6462,6 +6663,7 @@ This variable automatically becomes buffer-local when set.  */);
   defsubr (&Sspecial_display_p);
   defsubr (&Ssame_window_p);
   defsubr (&Sdisplay_buffer);
+  defsubr (&Sforce_window_update);
   defsubr (&Ssplit_window);
   defsubr (&Senlarge_window);
   defsubr (&Sshrink_window);
@@ -6510,3 +6712,6 @@ keys_of_window ()
   initial_define_key (global_map, Ctl('L'), "recenter");
   initial_define_key (meta_map, 'r', "move-to-window-line");
 }
+
+/* arch-tag: 90a9c576-0590-48f1-a5f1-6c96a0452d9f
+   (do not change this comment) */