(window_scroll): When scrolling forward and point is
[bpt/emacs.git] / src / window.c
index 34b64ca..c5360a8 100644 (file)
@@ -1,6 +1,6 @@
 /* Window creation, deletion and examination for GNU Emacs.
    Does not include redisplay.
-   Copyright (C) 1985, 86, 87, 93, 94, 95, 96 Free Software Foundation, Inc.
+   Copyright (C) 1985,86,87,93,94,95,96,1997 Free Software Foundation, Inc.
 
 This file is part of GNU Emacs.
 
@@ -29,6 +29,7 @@ Boston, MA 02111-1307, USA.  */
 #include "termchar.h"
 #include "disptab.h"
 #include "keyboard.h"
+#include "blockinput.h"
 
 Lisp_Object Qwindowp, Qwindow_live_p;
 
@@ -111,10 +112,17 @@ static int sequence_number;
 /* Nonzero after init_window_once has finished.  */
 static int window_initialized;
 
+/* Hook to run when window config changes.  */
+Lisp_Object Qwindow_configuration_change_hook;
+Lisp_Object Vwindow_configuration_change_hook;
+
 /* Nonzero means scroll commands try to put point
    at the same screen height as previously.  */
 static int scroll_preserve_screen_position;
 
+/* Nonzero means we can split a frame even if it is "unsplittable".  */
+static int inhibit_frame_unsplittable;
+
 #define min(a, b) ((a) < (b) ? (a) : (b))
 
 extern int scroll_margin;
@@ -253,8 +261,17 @@ POS defaults to point; WINDOW, to the selected window.")
        return Qnil;
 
       /* If that info is not correct, calculate afresh */
+      /* BUG FIX for the 7th arg (TOHPOS).
+
+        '0' is harmless, however, ' - (1 << (BITS_PER_SHORT - 1))' is
+        more appropriate here.  In case of HSCROLL > 0, this can avoid
+        needless calculation done until (HPOS == 0).
+
+        We want to determine if the position POSINT is in HEIGHT or
+        not.  We don't have to do calculation until (HPOS == 0).  We
+        can stop it when VPOS goes beyond HEIGHT.  */
       posval = *compute_motion (top, 0, (hscroll ? 1 - hscroll : 0), 0,
-                               posint, height, 0,
+                               posint, height, - (1 << (BITS_PER_SHORT - 1)),
                                window_internal_width (w) - 1,
                                hscroll, 0, w);
 
@@ -440,7 +457,7 @@ If they are on the border between WINDOW and its right sibling,\n\
       return Qnil;
 
     case 1:                    /* In text part of window. */
-      return Fcons (x, y);
+      return Fcons (make_number (x), make_number (y));
 
     case 2:                    /* In mode line of window. */
       return Qmode_line;
@@ -776,6 +793,18 @@ DEFUN ("delete-window", Fdelete_window, Sdelete_window, 0, 1, "",
   "Remove WINDOW from the display.  Default is selected window.")
   (window)
      register Lisp_Object window;
+{
+  delete_window (window);
+
+  if (! NILP (Vwindow_configuration_change_hook)
+      && ! NILP (Vrun_hooks))
+    call1 (Vrun_hooks, Qwindow_configuration_change_hook);
+
+  return Qnil;
+}
+
+delete_window (window)
+     register Lisp_Object window;
 {
   register Lisp_Object tem, parent, sib;
   register struct window *p;
@@ -794,7 +823,7 @@ DEFUN ("delete-window", Fdelete_window, Sdelete_window, 0, 1, "",
   if (NILP (p->buffer)
       && NILP (p->hchild)
       && NILP (p->vchild))
-    return Qnil;
+    return;
 
   parent = p->parent;
   if (NILP (parent))
@@ -901,8 +930,6 @@ DEFUN ("delete-window", Fdelete_window, Sdelete_window, 0, 1, "",
 
   /* Mark this window as deleted.  */
   p->buffer = p->hchild = p->vchild = Qnil;
-
-  return Qnil;
 }
 \f
 
@@ -1573,7 +1600,7 @@ value is reasonable when this function is called.")
 
       Fset_marker (w->start, make_number (pos.bufpos), w->buffer);
       w->start_at_line_beg = ((pos.bufpos == BEGV
-                              || FETCH_CHAR (pos.bufpos - 1) == '\n') ? Qt
+                              || FETCH_BYTE (pos.bufpos - 1) == '\n') ? Qt
                              : Qnil);
       /* We need to do this, so that the window-scroll-functions
         get called.  */
@@ -1708,9 +1735,11 @@ set_window_height (window, height, nodelete)
 
   if (!nodelete
       && ! NILP (w->parent)
-      && height < window_min_height)
+      && (MINI_WINDOW_P (w)
+         ? height < 1
+         : height < window_min_height))
     {
-      Fdelete_window (window);
+      delete_window (window);
       return;
     }
 
@@ -1774,7 +1803,7 @@ set_window_width (window, width, nodelete)
 
   if (!nodelete && width < window_min_width && !NILP (w->parent))
     {
-      Fdelete_window (window);
+      delete_window (window);
       return;
     }
 
@@ -1865,6 +1894,9 @@ BUFFER can be a buffer or buffer name.")
 
   if (EQ (window, selected_window))
     XBUFFER (w->buffer)->last_selected_window = window;
+  if (INTEGERP (XBUFFER (buffer)->display_count))
+    XSETINT (XBUFFER (buffer)->display_count,
+            XINT (XBUFFER (buffer)->display_count) + 1);
 
   XSETFASTINT (w->window_end_pos, 0);
   w->window_end_valid = Qnil;
@@ -1898,6 +1930,10 @@ BUFFER can be a buffer or buffer name.")
     run_hook_with_args_2 (Qwindow_scroll_functions, window,
                          Fmarker_position (w->start));
 
+  if (! NILP (Vwindow_configuration_change_hook)
+      && ! NILP (Vrun_hooks))
+    call1 (Vrun_hooks, Qwindow_configuration_change_hook);
+
   unbind_to (count, Qnil);
 
   return Qnil;
@@ -2057,7 +2093,7 @@ See `same-window-buffer-names' and `same-window-regexps'.")
 }
 
 DEFUN ("display-buffer", Fdisplay_buffer, Sdisplay_buffer, 1, 2,
-       "bDisplay buffer: \nP",
+     "BDisplay buffer: \nP",   /* Use B so the default is (other-buffer).  */
   "Make BUFFER appear in some window but don't select it.\n\
 BUFFER can be a buffer or a buffer name.\n\
 If BUFFER is shown already in some window, just use that one,\n\
@@ -2186,13 +2222,20 @@ buffer names are handled.")
              && window_height (window) >= window_min_height << 1)
            window = Fsplit_window (window, Qnil, Qnil);
          /* If Fget_lru_window returned nil, try other approaches.  */
+
          /* Try visible frames first.  */
+         if (NILP (window))
+           window = Fget_buffer_window (buffer, Qvisible);
          if (NILP (window))
            window = Fget_largest_window (Qvisible);
          /* If that didn't work, try iconified frames.  */
+         if (NILP (window))
+           window = Fget_buffer_window (buffer, make_number (0));
          if (NILP (window))
            window = Fget_largest_window (make_number (0));
          /* Try invisible frames.  */
+         if (NILP (window))
+           window = Fget_buffer_window (buffer, Qt);
          if (NILP (window))
            window = Fget_largest_window (Qt);
          /* As a last resort, make a new frame.  */
@@ -2207,15 +2250,19 @@ buffer names are handled.")
            other = lower = XWINDOW (window)->next, upper = window;
          if (!NILP (other)
              /* Check that OTHER and WINDOW are vertically arrayed.  */
-             && XWINDOW (other)->top != XWINDOW (window)->top
-             && XWINDOW (other)->height > XWINDOW (window)->height)
+             && !EQ (XWINDOW (other)->top, XWINDOW (window)->top)
+             && (XFASTINT (XWINDOW (other)->height)
+                 > XFASTINT (XWINDOW (window)->height)))
            {
-             int total = XWINDOW (other)->height + XWINDOW (window)->height;
+             int total = (XFASTINT (XWINDOW (other)->height)
+                          + XFASTINT (XWINDOW (window)->height));
              Lisp_Object old_selected_window;
              old_selected_window = selected_window;
 
              selected_window = upper;
-             change_window_height (total / 2 - XWINDOW (upper)->height, 0);
+             change_window_height ((total / 2
+                                    - XFASTINT (XWINDOW (upper)->height)),
+                                   0);
              selected_window = old_selected_window;
            }
        }
@@ -2235,6 +2282,8 @@ temp_output_buffer_show (buf)
   register Lisp_Object window;
   register struct window *w;
 
+  XBUFFER (buf)->directory = current_buffer->directory;
+
   Fset_buffer (buf);
   BUF_SAVE_MODIFF (XBUFFER (buf)) = MODIFF;
   BEGV = BEG;
@@ -2353,8 +2402,6 @@ and put SIZE columns in the first of the pair.")
 
   if (MINI_WINDOW_P (o))
     error ("Attempt to split minibuffer window");
-  else if (FRAME_NO_SPLIT_P (fo))
-    error ("Attempt to split unsplittable frame");
 
   check_min_window_sizes ();
 
@@ -2408,8 +2455,6 @@ and put SIZE columns in the first of the pair.")
   p->parent = o->parent;
   p->buffer = Qt;
 
-  Fset_window_buffer (new, o->buffer);
-
   /* Apportion the available frame space among the two new windows */
 
   if (!NILP (horflag))
@@ -2429,6 +2474,8 @@ and put SIZE columns in the first of the pair.")
       XSETFASTINT (p->top, XFASTINT (o->top) + size_int);
     }
 
+  Fset_window_buffer (new, o->buffer);
+
   return new;
 }
 \f
@@ -2440,6 +2487,10 @@ From program, optional second arg non-nil means grow sideways ARG columns.")
 {
   CHECK_NUMBER (arg, 0);
   change_window_height (XINT (arg), !NILP (side));
+
+  if (! NILP (Vwindow_configuration_change_hook))
+    call1 (Vrun_hooks, Qwindow_configuration_change_hook);
+
   return Qnil;
 }
 
@@ -2451,6 +2502,10 @@ From program, optional second arg non-nil means shrink sideways arg columns.")
 {
   CHECK_NUMBER (arg, 0);
   change_window_height (-XINT (arg), !NILP (side));
+
+  if (! NILP (Vwindow_configuration_change_hook))
+    call1 (Vrun_hooks, Qwindow_configuration_change_hook);
+
   return Qnil;
 }
 
@@ -2540,7 +2595,7 @@ change_window_height (delta, widthflag)
 
   if (*sizep + delta < MINSIZE (window))
     {
-      Fdelete_window (window);
+      delete_window (window);
       return;
     }
 
@@ -2555,7 +2610,7 @@ change_window_height (delta, widthflag)
     maximum += (*sizefun) (prev) - MINSIZE (prev);
 
   /* If we can get it all from them, do so.  */
-  if (delta < maximum)
+  if (delta <= maximum)
     {
       Lisp_Object first_unaffected;
       Lisp_Object first_affected;
@@ -2566,7 +2621,7 @@ change_window_height (delta, widthflag)
       /* Look at one sibling at a time,
         moving away from this window in both directions alternately,
         and take as much as we can get without deleting that sibling.  */
-      while (delta > 0)
+      while (delta != 0)
        {
          if (delta == 0)
            break;
@@ -2795,7 +2850,7 @@ window_scroll (window, n, whole, noerror)
              Fvertical_motion (make_number (original_vpos), window);
            }
          else
-           SET_PT (pos);
+           SET_PT (top_margin);
        }
       else if (n < 0)
        {
@@ -3055,7 +3110,7 @@ redraws with point in the center of the current window.")
 
   Fset_marker (w->start, make_number (pos.bufpos), w->buffer);
   w->start_at_line_beg = ((pos.bufpos == BEGV
-                          || FETCH_CHAR (pos.bufpos - 1) == '\n')
+                          || FETCH_BYTE (pos.bufpos - 1) == '\n')
                          ? Qt : Qnil);
   w->force_start = Qt;
 
@@ -3161,6 +3216,7 @@ by `current-window-configuration' (which see).")
   Lisp_Object new_current_buffer;
   Lisp_Object frame;
   FRAME_PTR f;
+  int old_point = -1;
 
   while (!WINDOW_CONFIGURATIONP (configuration))
     {
@@ -3174,6 +3230,11 @@ by `current-window-configuration' (which see).")
   new_current_buffer = data->current_buffer;
   if (NILP (XBUFFER (new_current_buffer)->name))
     new_current_buffer = Qnil;
+  else
+    {
+      if (XBUFFER (new_current_buffer) == current_buffer)
+       old_point = PT;
+    }
 
   frame = XWINDOW (SAVED_WINDOW_N (saved_windows, 0)->window)->frame;
   f = XFRAME (frame);
@@ -3194,6 +3255,10 @@ by `current-window-configuration' (which see).")
       int previous_frame_width =  FRAME_WIDTH  (f);
       int previous_frame_menu_bar_lines = FRAME_MENU_BAR_LINES (f);
 
+      /* The mouse highlighting code could get screwed up
+        if it runs during this.  */
+      BLOCK_INPUT;
+
       if (XFASTINT (data->frame_height) != previous_frame_height
          || XFASTINT (data->frame_width) != previous_frame_width)
        change_frame_size (f, data->frame_height, data->frame_width, 0, 0);
@@ -3325,6 +3390,8 @@ by `current-window-configuration' (which see).")
 
       FRAME_ROOT_WINDOW (f) = data->root_window;
       Fselect_window (data->current_window);
+      XBUFFER (XWINDOW (selected_window)->buffer)->last_selected_window
+       = selected_window;
 
       if (NILP (data->focus_frame)
          || (FRAMEP (data->focus_frame)
@@ -3347,6 +3414,8 @@ by `current-window-configuration' (which see).")
       if (previous_frame_menu_bar_lines != FRAME_MENU_BAR_LINES (f))
        x_set_menu_bar_lines (f, previous_frame_menu_bar_lines, 0);
 #endif
+
+      UNBLOCK_INPUT;
     }
 
   /* Restore the minimum heights recorded in the configuration.  */
@@ -3362,9 +3431,22 @@ by `current-window-configuration' (which see).")
     do_switch_frame (data->selected_frame, Qnil, 0);
 
   if (!NILP (new_current_buffer))
-    Fset_buffer (new_current_buffer);
+    {
+      Fset_buffer (new_current_buffer);
+
+      /* If the buffer that is current now is the same
+        that was current before setting the window configuration,
+        don't alter its PT.  */
+      if (old_point >= 0)
+       SET_PT (old_point);
+    }
 
   Vminibuf_scroll_window = data->minibuf_scroll_window;
+
+  if (! NILP (Vwindow_configuration_change_hook)
+      && ! NILP (Vrun_hooks))
+    call1 (Vrun_hooks, Qwindow_configuration_change_hook);
+
   return (Qnil);
 }
 
@@ -3442,7 +3524,8 @@ save_window_save (window, vector, i)
          if (EQ (window, selected_window))
            {
              p->pointm = Fmake_marker ();
-             Fset_marker (p->pointm, BUF_PT (XBUFFER (w->buffer)),
+             Fset_marker (p->pointm,
+                          make_number (BUF_PT (XBUFFER (w->buffer))),
                           w->buffer);
            }
          else
@@ -3571,6 +3654,10 @@ init_window_once ()
 
 syms_of_window ()
 {
+  staticpro (&Qwindow_configuration_change_hook);
+  Qwindow_configuration_change_hook
+    = intern ("window-configuration-change-hook");
+
   Qwindowp = intern ("windowp");
   staticpro (&Qwindowp);
 
@@ -3627,7 +3714,12 @@ There are two ways to use a list as an element:\n\
 In the first case, FRAME-PARAMETERS are used to create the frame.\n\
 In the latter case, FUNCTION is called with BUFFER as the first argument,\n\
 followed by OTHER-ARGS--it can display BUFFER in any way it likes.\n\
-All this is done by the function found in `special-display-function'.");
+All this is done by the function found in `special-display-function'.\n\
+\n\
+If this variable appears \"not to work\", because you add a name to it\n\
+but that buffer still appears in the selected window, look at the\n\
+values of `same-window-buffer-names' and `same-window-regexps'.\n\
+Those variables take precedence over this one.");
   Vspecial_display_buffer_names = Qnil;
 
   DEFVAR_LISP ("special-display-regexps", &Vspecial_display_regexps,
@@ -3642,7 +3734,12 @@ There are two ways to use a list as an element:\n\
 In the first case, FRAME-PARAMETERS are used to create the frame.\n\
 In the latter case, FUNCTION is called with the buffer as first argument,\n\
 followed by OTHER-ARGS--it can display the buffer in any way it likes.\n\
-All this is done by the function found in `special-display-function'.");
+All this is done by the function found in `special-display-function'.\n\
+\n\
+If this variable appears \"not to work\", because you add a regexp to it\n\
+but the matching buffers still appear in the selected window, look at the\n\
+values of `same-window-buffer-names' and `same-window-regexps'.\n\
+Those variables take precedence over this one.");
   Vspecial_display_regexps = Qnil;
 
   DEFVAR_LISP ("special-display-function", &Vspecial_display_function,
@@ -3710,6 +3807,12 @@ If there is only one window, it is split regardless of this value.");
     "*Nonzero means scroll commands move point to keep its screen line unchanged.");
   scroll_preserve_screen_position = 0;
 
+  DEFVAR_LISP ("window-configuration-change-hook",
+              &Vwindow_configuration_change_hook,
+    "Functions to call when window configuration changes.\n\
+The selected frae is the one whose configuration has changed.");
+  Vwindow_configuration_change_hook = Qnil;
+
   defsubr (&Sselected_window);
   defsubr (&Sminibuffer_window);
   defsubr (&Swindow_minibuffer_p);