]> code.delx.au - gnu-emacs/blobdiff - src/xdisp.c
(prepare_menu_bars): Clear size-change flag before running
[gnu-emacs] / src / xdisp.c
index e58a1cdd0693a49fbc268b12433db0d30b8c136b..3e25cdd35db1bae32de16ed6a5605c450084f0f7 100644 (file)
@@ -50,7 +50,7 @@ int noninteractive_need_newline;
 
 /* Nonzero means print newline to message log before next message.  */
 
-int message_log_need_newline;
+static int message_log_need_newline;
 
 #define min(a, b) ((a) < (b) ? (a) : (b))
 #define max(a, b) ((a) > (b) ? (a) : (b))
@@ -107,6 +107,11 @@ Lisp_Object Vframe_title_format;
 /* Like mode-line-format, but for the titlebar on an iconified frame.  */
 Lisp_Object Vicon_title_format;
 
+/* List of functions to call when a window's size changes.  These
+   functions get one arg, a frame on which one or more windows' sizes
+   have changed.  */
+static Lisp_Object Vwindow_size_change_functions;
+
 /* Values of those variables at last redisplay.  */
 static Lisp_Object last_arrow_position, last_arrow_string;
 
@@ -120,7 +125,7 @@ static int highlight_nonselected_windows;
 
 /* If cursor motion alone moves point off frame,
    Try scrolling this many lines up or down if that will bring it back.  */
-int scroll_step;
+static int scroll_step;
 
 /* Nonzero if try_window_id has made blank lines at window bottom
  since the last redisplay that paused */
@@ -138,11 +143,12 @@ int buffer_shared;
 static int cursor_vpos;
 static int cursor_hpos;
 
-int debug_end_pos;
+static int debug_end_pos;
 
 /* Nonzero means display mode line highlighted */
 int mode_line_inverse_video;
 
+static int message_log_check_duplicate ();
 static void echo_area_display ();
 void mark_window_display_accurate ();
 static void redisplay_windows ();
@@ -206,12 +212,20 @@ int windows_or_buffers_changed;
 int line_number_displayed;
 
 /* Maximum buffer size for which to display line numbers.  */
-int line_number_display_limit;
+static int line_number_display_limit;
 
 /* Number of lines to keep in the message log buffer.
    t means infinite.  nil means don't log at all.  */
 Lisp_Object Vmessage_log_max;
 \f
+void
+message_log_maybe_newline ()
+{
+  if (message_log_need_newline)
+    message_dolog ("", 0, 1);
+}
+
+
 /* Add a string to the message log, optionally terminated with a newline.
    This function calls low-level routines in order to bypass text property
    hooks, etc. which might not be safe to run.  */
@@ -227,7 +241,8 @@ message_dolog (m, len, nlflag)
       int oldpoint, oldbegv, oldzv;
 
       oldbuf = current_buffer;
-      Fset_buffer (Fget_buffer_create (build_string (" *Messages*")));
+      Fset_buffer (Fget_buffer_create (build_string ("*Messages*")));
+      current_buffer->undo_list = Qt;
       oldpoint = PT;
       oldbegv = BEGV;
       oldzv = ZV;
@@ -241,24 +256,100 @@ message_dolog (m, len, nlflag)
       if (len)
        insert_1 (m, len, 1, 0);
       if (nlflag)
-       insert_1 ("\n", 1, 1, 0);
-      if (NATNUMP (Vmessage_log_max))
        {
-         int pos = scan_buffer ('\n', PT, 0,
-                                -XFASTINT (Vmessage_log_max) - 1, 0, 1);
-         oldpoint -= min (pos, oldpoint) - BEG;
-         oldbegv -= min (pos, oldbegv) - BEG;
-         oldzv -= min (pos, oldzv) - BEG;
-         del_range_1 (BEG, pos, 0);
+         int this_bol, prev_bol, dup;
+         insert_1 ("\n", 1, 1, 0);
+
+         this_bol = scan_buffer ('\n', Z, 0, -2, 0, 0);
+         if (this_bol > BEG)
+           {
+             prev_bol = scan_buffer ('\n', this_bol, 0, -2, 0, 0);
+             dup = message_log_check_duplicate (prev_bol, this_bol);
+             if (dup)
+               {
+                 if (oldpoint > prev_bol)
+                   oldpoint -= min (this_bol, oldpoint) - prev_bol;
+                 if (oldbegv > prev_bol)
+                   oldbegv -= min (this_bol, oldbegv) - prev_bol;
+                 if (oldzv > prev_bol)
+                   oldzv -= min (this_bol, oldzv) - prev_bol;
+                 del_range_1 (prev_bol, this_bol, 0);
+                 if (dup > 1)
+                   {
+                     char dupstr[40];
+                     int duplen;
+
+                     /* If you change this format, don't forget to also
+                        change message_log_check_duplicate.  */
+                     sprintf (dupstr, " [%d times]", dup);
+                     duplen = strlen (dupstr);
+                     TEMP_SET_PT (Z-1);
+                     if (oldpoint == Z)
+                       oldpoint += duplen;
+                     if (oldzv == Z)
+                       oldzv += duplen;
+                     insert_1 (dupstr, duplen, 1, 0);
+                   }
+               }
+           }
+
+         if (NATNUMP (Vmessage_log_max))
+           {
+             int pos = scan_buffer ('\n', Z, 0,
+                                    -XFASTINT (Vmessage_log_max) - 1, 0, 0);
+             oldpoint -= min (pos, oldpoint) - BEG;
+             oldbegv -= min (pos, oldbegv) - BEG;
+             oldzv -= min (pos, oldzv) - BEG;
+             del_range_1 (BEG, pos, 0);
+           }
        }
       BEGV = oldbegv;
       ZV = oldzv;
       TEMP_SET_PT (oldpoint);
       set_buffer_internal (oldbuf);
+      message_log_need_newline = !nlflag;
     }
 }
 
 
+/* We are at the end of the buffer after just having inserted a newline.
+   (Note: We depend on the fact we won't be crossing the gap.)
+   Check to see if the most recent message looks a lot like the previous one.
+   Return 0 if different, 1 if the new one should just replace it, or a
+   value N > 1 if we should also append " [N times]".  */
+
+static int
+message_log_check_duplicate (prev_bol, this_bol)
+     int prev_bol, this_bol;
+{
+  int i;
+  int len = Z - 1 - this_bol;
+  int seen_dots = 0;
+  unsigned char *p1 = BUF_CHAR_ADDRESS (current_buffer, prev_bol);
+  unsigned char *p2 = BUF_CHAR_ADDRESS (current_buffer, this_bol);
+
+  for (i = 0; i < len; i++)
+    {
+      if (i >= 3 && p1[i-3] == '.' && p1[i-2] == '.' && p1[i-1] == '.'
+         && p1[i] != '\n')
+       seen_dots = 1;
+      if (p1[i] != p2[i])
+       return seen_dots;
+    }
+  p1 += len;
+  if (*p1 == '\n')
+    return 2;
+  if (*p1++ == ' ' && *p1++ == '[')
+    {
+      int n = 0;
+      while (*p1 >= '0' && *p1 <= '9')
+       n = n * 10 + *p1++ - '0';
+      if (strncmp (p1, " times]\n", 8) == 0)
+       return n+1;
+    }
+  return 0;
+}
+
 /* Display an echo area message M with a specified length of LEN chars.
    The string may include null characters.  If m is 0, clear out any
    existing message, and let the minibuffer text show through.
@@ -270,9 +361,7 @@ message2 (m, len)
      int len;
 {
   /* First flush out any partial line written with print.  */
-  if (message_log_need_newline)
-    message_dolog ("", 0, 1);
-  message_log_need_newline = 0;
+  message_log_maybe_newline ();
   if (m)
     message_dolog (m, len, 1);
   message2_nolog (m, len);
@@ -428,6 +517,19 @@ message (m, a1, a2, a3)
     }
 }
 
+/* The non-logging version of that function.  */
+void
+message_nolog (m, a1, a2, a3)
+     char *m;
+     EMACS_INT a1, a2, a3;
+{
+  Lisp_Object old_log_max;
+  old_log_max = Vmessage_log_max;
+  Vmessage_log_max = Qnil;
+  message (m, a1, a2, a3);
+  Vmessage_log_max = old_log_max;
+}
+
 void
 update_echo_area ()
 {
@@ -465,6 +567,8 @@ echo_area_display ()
                      0, 0, 0, 0, FRAME_WIDTH (f));
 
       /* If desired cursor location is on this line, put it at end of text */
+      if (cursor_in_echo_area)
+       FRAME_CURSOR_Y (f) = vpos;
       if (FRAME_CURSOR_Y (f) == vpos)
        FRAME_CURSOR_X (f) = FRAME_DESIRED_GLYPHS (f)->used[vpos];
 
@@ -545,40 +649,55 @@ x_consider_frame_title (frame)
 #endif
 \f
 /* Prepare for redisplay by updating menu-bar item lists when appropriate.
-   This can't be done in `redisplay' itself because it can call eval.  */
+   This can call eval.  */
 
 void
 prepare_menu_bars ()
 {
   register struct window *w = XWINDOW (selected_window);
   int all_windows;
+  struct gcpro gcpro1, gcpro2;
 
-  if (noninteractive)
-    return;
-
-  /* Set the visible flags for all frames.
-     Do this before checking for resized or garbaged frames; they want
-     to know if their frames are visible.
-     See the comment in frame.h for FRAME_SAMPLE_VISIBILITY.  */
-  {
-    Lisp_Object tail, frame;
-
-    FOR_EACH_FRAME (tail, frame)
-      FRAME_SAMPLE_VISIBILITY (XFRAME (frame));
-  }
-
-  /* Notice any pending interrupt request to change frame size.  */
-  do_pending_window_change ();
+  all_windows = (update_mode_lines || buffer_shared > 1
+                || clip_changed || windows_or_buffers_changed);
 
-  if (frame_garbaged)
+  /* Update the menu bar item lists, if appropriate.
+     This has to be done before any actual redisplay
+     or generation of display lines.  */
+  if (all_windows)
     {
-      redraw_garbaged_frames ();
-      frame_garbaged = 0;
-    }
+      Lisp_Object tail, frame;
 
-  all_windows = (update_mode_lines || buffer_shared > 1
-                || clip_changed || windows_or_buffers_changed);
+      FOR_EACH_FRAME (tail, frame)
+       {
+         /* If a window on this frame changed size,
+            report that to the user and clear the size-change flag.  */
+         if (FRAME_WINDOW_SIZES_CHANGED (XFRAME (frame)))
+           {
+             Lisp_Object functions;
+             /* Clear flag first in case we get error below.  */
+             FRAME_WINDOW_SIZES_CHANGED (XFRAME (frame)) = 0;
+             functions = Vwindow_size_change_functions;
+             GCPRO2 (tail, functions);
+             while (CONSP (functions))
+               {
+                 call1 (XCONS (functions)->car, frame);
+                 functions = XCONS (functions)->cdr;
+               }
+             UNGCPRO;
+           }
+         GCPRO1 (tail);
+         update_menu_bar (XFRAME (frame));
+         UNGCPRO;
+       }
+    }
+  else
+    update_menu_bar (selected_frame);
 
+  /* Update all frame titles based on their buffer names, etc.
+     We do this after the menu bars so that the frame will first
+     create its menu bar using the name `emacs' if no other name
+     has yet been specified.  */
 #ifdef HAVE_X_WINDOWS
   if (windows_or_buffers_changed)
     {
@@ -590,19 +709,6 @@ prepare_menu_bars ()
          x_consider_frame_title (frame);
     }
 #endif
-
-  /* Update the menu bar item lists, if appropriate.
-     This has to be done before any actual redisplay
-     or generation of display lines.  */
-  if (all_windows)
-    {
-      Lisp_Object tail, frame;
-
-      FOR_EACH_FRAME (tail, frame)
-       update_menu_bar (XFRAME (frame));
-    }
-  else
-    update_menu_bar (selected_frame);
 }
 \f
 /* Do a frame update, taking possible shortcuts into account.
@@ -638,7 +744,6 @@ redisplay ()
   int all_windows;
   register int tlbufpos, tlendpos;
   struct position pos;
-  extern int input_pending;
 
   if (noninteractive)
     return;
@@ -651,7 +756,13 @@ redisplay ()
     Lisp_Object tail, frame;
 
     FOR_EACH_FRAME (tail, frame)
-      FRAME_SAMPLE_VISIBILITY (XFRAME (frame));
+      {
+       FRAME_SAMPLE_VISIBILITY (XFRAME (frame));
+
+       /* Clear out all the display lines in which we will generate the
+          glyphs to display.  */
+       init_desired_glyphs (XFRAME (frame));
+      }
   }
 
   /* Notice any pending interrupt request to change frame size.  */
@@ -663,6 +774,8 @@ redisplay ()
       frame_garbaged = 0;
     }
 
+  prepare_menu_bars ();
+
   if (clip_changed || windows_or_buffers_changed
       || (!NILP (w->column_number_displayed)
          && XFASTINT (w->column_number_displayed) != current_column ()))
@@ -757,7 +870,6 @@ redisplay ()
                  int left = XFASTINT (w->left);
                  int *charstart_next_line
                    = FRAME_CURRENT_GLYPHS (XFRAME (WINDOW_FRAME (w)))->charstarts[this_line_vpos + 1];
-                 int i;
                  int adjust;
 
                  if (Z - tlendpos == ZV)
@@ -789,10 +901,12 @@ redisplay ()
            }
          goto update;
        }
-      /* If highlighting the region, we can't just move the cursor.  */
+      /* If highlighting the region, or if the cursor is in the echo area,
+        then we can't just move the cursor.  */
       else if (! (!NILP (Vtransient_mark_mode)
                  && !NILP (current_buffer->mark_active))
-              && NILP (w->region_showing))
+              && NILP (w->region_showing)
+              && !cursor_in_echo_area)
        {
          pos = *compute_motion (tlbufpos, 0,
                                 XINT (w->hscroll) ? 1 - XINT (w->hscroll) : 0,
@@ -841,19 +955,22 @@ redisplay ()
       FOR_EACH_FRAME (tail, frame)
        {
          FRAME_PTR f = XFRAME (frame);
+         if (! FRAME_TERMCAP_P (f) || f == selected_frame)
+           {
 
-         /* Mark all the scroll bars to be removed; we'll redeem the ones
-            we want when we redisplay their windows.  */
-         if (condemn_scroll_bars_hook)
-           (*condemn_scroll_bars_hook) (f);
+             /* Mark all the scroll bars to be removed; we'll redeem the ones
+                we want when we redisplay their windows.  */
+             if (condemn_scroll_bars_hook)
+               (*condemn_scroll_bars_hook) (f);
 
-         if (FRAME_VISIBLE_P (f))
-           redisplay_windows (FRAME_ROOT_WINDOW (f));
+             if (FRAME_VISIBLE_P (f))
+               redisplay_windows (FRAME_ROOT_WINDOW (f));
 
-         /* Any scroll bars which redisplay_windows should have nuked
-            should now go away.  */
-         if (judge_scroll_bars_hook)
-           (*judge_scroll_bars_hook) (f);
+             /* Any scroll bars which redisplay_windows should have nuked
+                should now go away.  */
+             if (judge_scroll_bars_hook)
+               (*judge_scroll_bars_hook) (f);
+           }
        }
     }
   else if (FRAME_VISIBLE_P (selected_frame))
@@ -886,7 +1003,9 @@ update:
            continue;
 
          f = XFRAME (XCONS (tail)->car);
-         if (FRAME_VISIBLE_P (f))
+
+         if ((! FRAME_TERMCAP_P (f) || f == selected_frame)
+             && FRAME_VISIBLE_P (f))
            {
              pause |= update_frame (f, 0, 0);
              if (!pause)
@@ -903,6 +1022,8 @@ update:
     {
       if (FRAME_VISIBLE_P (selected_frame))
        pause = update_frame (selected_frame, 0, 0);
+      else
+       pause = 0;
 
       /* We may have called echo_area_display at the top of this
         function.  If the echo area is on another frame, that may
@@ -1138,7 +1259,7 @@ redisplay_window (window, just_this_one)
   struct position pos;
   int opoint = PT;
   int tem;
-  int window_needs_modeline;
+  int update_mode_line;
 
   if (FRAME_HEIGHT (f) == 0) abort (); /* Some bug zeros some core */
 
@@ -1158,6 +1279,7 @@ redisplay_window (window, just_this_one)
     abort ();
   
   height = window_internal_height (w);
+  update_mode_line = (!NILP (w->update_mode_line) || update_mode_lines);
 
   if (MINI_WINDOW_P (w))
     {
@@ -1184,12 +1306,13 @@ redisplay_window (window, just_this_one)
        }
     }
 
-  if (update_mode_lines)
-    w->update_mode_line = Qt;
-
   /* Otherwise set up data on this window; select its buffer and point value */
 
-  set_buffer_temp (XBUFFER (w->buffer));
+  if (update_mode_line)
+    set_buffer_internal (XBUFFER (w->buffer));
+  else
+    set_buffer_temp (XBUFFER (w->buffer));
+
   opoint = PT;
 
   /* Count number of windows showing the selected buffer.
@@ -1259,7 +1382,14 @@ redisplay_window (window, just_this_one)
     {
       /* Forget any recorded base line for line number display.  */
       w->base_line_number = Qnil;
-      w->update_mode_line = Qt;
+      /* Redisplay the mode line.  Select the buffer properly for that.  */
+      if (!update_mode_line)
+       {
+         set_buffer_temp (old);
+         set_buffer_internal (XBUFFER (w->buffer));
+         update_mode_line = 1;
+         w->update_mode_line = Qt;
+       }
       w->force_start = Qnil;
       XSETFASTINT (w->last_modified, 0);
       if (startp < BEGV) startp = BEGV;
@@ -1311,6 +1441,10 @@ redisplay_window (window, just_this_one)
   if (XFASTINT (w->last_modified) >= MODIFF
       && PT >= startp && !clip_changed
       && (just_this_one || XFASTINT (w->width) == FRAME_WIDTH (f))
+      /* If force-mode-line-update was called, really redisplay;
+        that's how redisplay is forced after e.g. changing
+        buffer-invisibility-spec.  */
+      && NILP (w->update_mode_line)
       /* Can't use this case if highlighting a region.  */
       && !(!NILP (Vtransient_mark_mode) && !NILP (current_buffer->mark_active))
       && NILP (w->region_showing)
@@ -1320,7 +1454,7 @@ redisplay_window (window, just_this_one)
       && !EQ (window, minibuf_window))
     {
       pos = *compute_motion (startp, 0, (hscroll ? 1 - hscroll : 0),
-                           PT, height + 1, 10000, width, hscroll,
+                           PT, height0, width, hscroll,
                            pos_tab_offset (w, startp), w);
 
       if (pos.vpos < height)
@@ -1397,7 +1531,14 @@ redisplay_window (window, just_this_one)
     }
 
   XSETFASTINT (w->last_modified, 0);
-  w->update_mode_line = Qt;
+  /* Redisplay the mode line.  Select the buffer properly for that.  */
+  if (!update_mode_line)
+    {
+      set_buffer_temp (old);
+      set_buffer_internal (XBUFFER (w->buffer));
+      update_mode_line = 1;
+      w->update_mode_line = Qt;
+    }
 
   /* Try to scroll by specified few lines */
 
@@ -1411,7 +1552,8 @@ redisplay_window (window, just_this_one)
            goto scroll_fail;
        }
 
-      pos = *vmotion (startp, PT < startp ? - scroll_step : scroll_step,
+      pos = *vmotion (startp,
+                     (PT < startp ? - scroll_step : scroll_step),
                      width, hscroll, window);
 
       if (PT >= pos.bufpos)
@@ -1444,7 +1586,7 @@ recenter:
     = (startp == BEGV || FETCH_CHAR (startp - 1) == '\n') ? Qt : Qnil;
 
 done:
-  if ((!NILP (w->update_mode_line)
+  if ((update_mode_line
        /* If window not full width, must redo its mode line
          if the window to its side is being redone */
        || (!just_this_one && width < FRAME_WIDTH (f) - 1)
@@ -1461,7 +1603,7 @@ done:
     }
 
   /* When we reach a frame's selected window, redo the frame's menu bar.  */
-  if (!NILP (w->update_mode_line)
+  if (update_mode_line
 #ifdef USE_X_TOOLKIT
       && FRAME_EXTERNAL_MENU_BAR (f) 
 #else
@@ -1486,7 +1628,7 @@ done:
          || (w == XWINDOW (minibuf_window) && ! echo_area_glyphs))
        {
          whole = ZV - BEGV;
-         start = startp - BEGV;
+         start = marker_position (w->start) - BEGV;
          /* I don't think this is guaranteed to be right.  For the
             moment, we'll pretend it is.  */
          end = (Z - XINT (w->window_end_pos)) - BEGV;
@@ -1506,7 +1648,10 @@ done:
     }
 
   BUF_PT (current_buffer) = opoint;
-  set_buffer_temp (old);
+  if (update_mode_line)
+    set_buffer_internal (old);
+  else
+    set_buffer_temp (old);
   BUF_PT (current_buffer) = lpoint;
 }
 \f
@@ -1535,18 +1680,30 @@ try_window (window, pos)
     {
       val = *display_text_line (w, pos, vpos, val.hpos, tab_offset);
       tab_offset += width;
+      /* For the first line displayed, display_text_line
+        subtracts the prompt width from the tab offset.
+        But it does not affect the value of our variable tab_offset.
+        So we do the subtraction again,
+        for the sake of continuation lines of that first line.  */
+      if (MINI_WINDOW_P (w) && vpos == XFASTINT (w->top))
+       tab_offset -= minibuf_prompt_width;
+
       if (val.vpos) tab_offset = 0;
       vpos++;
       if (pos != val.bufpos)
-       last_text_vpos
-         /* Next line, unless prev line ended in end of buffer with no cr */
-         = vpos - (val.vpos && (FETCH_CHAR (val.bufpos - 1) != '\n'
+       {
+         int invis = 0;
 #ifdef USE_TEXT_PROPERTIES
-                                || ! NILP (Fget_char_property (val.bufpos-1,
-                                                               Qinvisible,
-                                                               window))
+         Lisp_Object invis_prop;
+         invis_prop = Fget_char_property (val.bufpos-1, Qinvisible, window);
+         invis = TEXT_PROP_MEANS_INVISIBLE (invis_prop);
 #endif
-                                ));
+
+         last_text_vpos
+           /* Next line, unless prev line ended in end of buffer with no cr */
+           = vpos - (val.vpos
+                     && (FETCH_CHAR (val.bufpos - 1) != '\n' || invis));
+       }
       pos = val.bufpos;
     }
 
@@ -1610,11 +1767,11 @@ try_window_id (window)
 
   /* Find position before which nothing is changed.  */
   bp = *compute_motion (start, 0, lmargin,
-                       min (ZV, beg_unchanged + BEG), height + 1, 0,
+                       min (ZV, beg_unchanged + BEG), height, 0,
                        width, hscroll, pos_tab_offset (w, start), w);
   if (bp.vpos >= height)
     {
-      if (PT < bp.bufpos && !bp.contin)
+      if (PT < bp.bufpos)
        {
          /* All changes are below the frame, and point is on the frame.
             We don't need to change the frame at all.
@@ -1625,7 +1782,7 @@ try_window_id (window)
                                width, hscroll, pos_tab_offset (w, start), w);
          XSETFASTINT (w->window_end_vpos, height);
          XSETFASTINT (w->window_end_pos, Z - bp.bufpos);
-         return 1;
+         goto findpoint;
        }
       return 0;
     }
@@ -1932,6 +2089,7 @@ try_window_id (window)
   /* If point was not in a line that was displayed, find it */
   if (cursor_vpos < 0)
     {
+    findpoint:
       val = *compute_motion (start, 0, lmargin, PT, 10000, 10000,
                             width, hscroll, pos_tab_offset (w, start), w);
       /* Admit failure if point is off frame now */
@@ -2156,6 +2314,8 @@ display_text_line (w, start, vpos, hpos, taboffset)
   struct position val;
   int lastpos;
   int invis;
+  int last_invis_skip = 0;
+  Lisp_Object last_invis_prop;
   int hscroll = XINT (w->hscroll);
   int truncate = (hscroll
                  || (truncate_partial_width_windows
@@ -2174,13 +2334,14 @@ display_text_line (w, start, vpos, hpos, taboffset)
   register struct Lisp_Vector *dp = window_display_table (w);
 
   Lisp_Object default_invis_vector[3];
-  /* Nonzero means display something where there are invisible lines.
-     The precise value is the number of glyphs to display.  */
+  /* Number of characters of ellipsis to display after an invisible line
+     if it calls for an ellipsis.
+     Note that this value can be nonzero regardless of whether
+     selective display is enabled--you must check that separately.  */
   int selective_rlen
-    = (selective && dp && VECTORP (DISP_INVIS_VECTOR (dp))
+    = (dp && VECTORP (DISP_INVIS_VECTOR (dp))
        ? XVECTOR (DISP_INVIS_VECTOR (dp))->size
-       : selective && !NILP (current_buffer->selective_display_ellipses)
-       ? 3 : 0);
+       : !NILP (current_buffer->selective_display_ellipses) ? 3 : 0);
   /* This is the sequence of Lisp objects to display
      when there are invisible lines.  */
   Lisp_Object *invis_vector_contents
@@ -2250,6 +2411,7 @@ display_text_line (w, start, vpos, hpos, taboffset)
                               w->width > 10 ? w->width - 4 : -1)
               - hpos);
          hpos += minibuf_prompt_width;
+         taboffset -= minibuf_prompt_width;
        }
       else
        minibuf_prompt_width = 0;
@@ -2353,12 +2515,13 @@ display_text_line (w, start, vpos, hpos, taboffset)
              if (XFASTINT (limit) > pos + 50)
                XSETFASTINT (limit, pos + 50);
              endpos = Fnext_single_property_change (position, Qinvisible,
-                                               Fcurrent_buffer (), limit);
+                                                    Fcurrent_buffer (),
+                                                    limit);
              if (INTEGERP (endpos))
                next_invisible = XINT (endpos);
              else
                next_invisible = end;
-             if (! NILP (prop))
+             if (TEXT_PROP_MEANS_INVISIBLE (prop))
                {
                  if (pos < PT && next_invisible >= PT)
                    {
@@ -2366,6 +2529,8 @@ display_text_line (w, start, vpos, hpos, taboffset)
                      cursor_hpos = p1 - leftmargin;
                    }
                  pos = next_invisible;
+                 last_invis_skip = pos;
+                 last_invis_prop = prop;
                }
            }
          if (pos >= end)
@@ -2401,8 +2566,15 @@ display_text_line (w, start, vpos, hpos, taboffset)
          p = &FETCH_CHAR (pos);
        }
       c = *p++;
-      if (c >= 040 && c < 0177
-         && (dp == 0 || !VECTORP (DISP_CHAR_VECTOR (dp, c))))
+      /* Let a display table override all standard display methods.  */
+      if (dp != 0 && VECTORP (DISP_CHAR_VECTOR (dp, c)))
+       {
+         p1 = copy_part_of_rope (f, p1, leftmargin,
+                                 XVECTOR (DISP_CHAR_VECTOR (dp, c))->contents,
+                                 XVECTOR (DISP_CHAR_VECTOR (dp, c))->size,
+                                 current_face);
+       }
+      else if (c >= 040 && c < 0177)
        {
          if (p1 >= leftmargin)
            *p1 = MAKE_GLYPH (f, c, current_face);
@@ -2411,6 +2583,9 @@ display_text_line (w, start, vpos, hpos, taboffset)
       else if (c == '\n')
        {
          invis = 0;
+         if (last_invis_skip == pos
+             && TEXT_PROP_MEANS_INVISIBLE_WITH_ELLIPSIS (last_invis_prop))
+           invis = 1;
          while (pos + 1 < end
                 && selective > 0
                 && indented_beyond_p (pos + 1, selective))
@@ -2478,13 +2653,6 @@ display_text_line (w, start, vpos, hpos, taboffset)
 #endif
          break;
        }
-      else if (dp != 0 && VECTORP (DISP_CHAR_VECTOR (dp, c)))
-       {
-         p1 = copy_part_of_rope (f, p1, leftmargin,
-                                 XVECTOR (DISP_CHAR_VECTOR (dp, c))->contents,
-                                 XVECTOR (DISP_CHAR_VECTOR (dp, c))->size,
-                                 current_face);
-       }
       else if (c < 0200 && ctl_arrow)
        {
          if (p1 >= leftmargin)
@@ -2639,8 +2807,12 @@ display_text_line (w, start, vpos, hpos, taboffset)
       cursor_hpos += XFASTINT (w->left);
       if (w == XWINDOW (FRAME_SELECTED_WINDOW (f)))
        {
-         FRAME_CURSOR_Y (f) = cursor_vpos;
-         FRAME_CURSOR_X (f) = cursor_hpos;
+         if (!(cursor_in_echo_area && FRAME_HAS_MINIBUF_P (f)
+               && EQ (FRAME_MINIBUF_WINDOW (f), minibuf_window)))
+           {
+             FRAME_CURSOR_Y (f) = cursor_vpos;
+             FRAME_CURSOR_X (f) = cursor_hpos;
+           }
 
          if (w == XWINDOW (selected_window))
            {
@@ -2776,10 +2948,10 @@ display_menu_bar (w)
                               XSTRING (string)->data,
                               XSTRING (string)->size,
                               hpos, 0, 0, hpos, maxendcol);
-      /* Put a gap of 3 spaces between items.  */
+      /* Put a space between items.  */
       if (hpos < maxendcol)
        {
-         int hpos1 = hpos + 3;
+         int hpos1 = hpos + 1;
          hpos = display_string (w, vpos, "", 0, hpos, 0, 0,
                                 min (hpos1, maxendcol), maxendcol);
        }
@@ -2814,8 +2986,17 @@ display_mode_line (w)
   w->column_number_displayed = Qnil;
 
   get_display_line (f, vpos, left);
+
+  /* Temporarily make frame F's kboard the current kboard
+     so that kboard-local variables in the mode_line_format
+     will get the right values.  */
+  push_frame_kboard (f);
+
   display_mode_element (w, vpos, left, 0, right, right,
                        current_buffer->mode_line_format);
+
+  pop_frame_kboard ();
+
   FRAME_DESIRED_GLYPHS (f)->bufp[vpos] = 0;
 
   /* Make the mode line inverse video if the entire line
@@ -2828,7 +3009,7 @@ display_mode_line (w)
       || XFASTINT (XWINDOW (w->parent)->width) == FRAME_WIDTH (f))
     FRAME_DESIRED_GLYPHS (f)->highlight[vpos] = mode_line_inverse_video;
 #ifdef HAVE_FACES
-  else if (! FRAME_TERMCAP_P (f))
+  else if (! FRAME_TERMCAP_P (f) && mode_line_inverse_video)
     {
       /* For a partial width window, explicitly set face of each glyph. */
       int i;
@@ -3086,6 +3267,74 @@ decode_mode_spec (w, c, maxwidth)
 
   switch (c)
     {
+    case '*':
+      if (!NILP (b->read_only))
+       return "%";
+      if (BUF_MODIFF (b) > BUF_SAVE_MODIFF (b))
+       return "*";
+      return "-";
+
+    case '+':
+      /* This differs from %* only for a modified read-only buffer.  */
+      if (BUF_MODIFF (b) > BUF_SAVE_MODIFF (b))
+       return "*";
+      if (!NILP (b->read_only))
+       return "%";
+      return "-";
+
+    case '&':
+      /* This differs from %* in ignoring read-only-ness.  */
+      if (BUF_MODIFF (b) > BUF_SAVE_MODIFF (b))
+       return "*";
+      return "-";
+
+    case '%':
+      return "%";
+
+    case '[': 
+      {
+       int i;
+       char *p;
+
+       if (command_loop_level > 5)
+         return "[[[... ";
+       p = decode_mode_spec_buf;
+       for (i = 0; i < command_loop_level; i++)
+         *p++ = '[';
+       *p = 0;
+       return decode_mode_spec_buf;
+      }
+
+    case ']': 
+      {
+       int i;
+       char *p;
+
+       if (command_loop_level > 5)
+         return " ...]]]";
+       p = decode_mode_spec_buf;
+       for (i = 0; i < command_loop_level; i++)
+         *p++ = ']';
+       *p = 0;
+       return decode_mode_spec_buf;
+      }
+
+    case '-':
+      {
+       register char *p;
+       register int i;
+       
+       if (maxwidth < sizeof (lots_of_dashes))
+         return lots_of_dashes;
+       else
+         {
+           for (p = decode_mode_spec_buf, i = maxwidth; i > 0; i--)
+             *p++ = '-';
+           *p = '\0';
+         }
+       return decode_mode_spec_buf;
+      }
+
     case 'b': 
       obj = b->name;
 #if 0
@@ -3099,6 +3348,18 @@ decode_mode_spec (w, c, maxwidth)
 #endif
       break;
 
+    case 'c':
+      {
+       int col = current_column ();
+       XSETFASTINT (w->column_number_displayed, col);
+       sprintf (decode_mode_spec_buf, "%d", col);
+       return decode_mode_spec_buf;
+      }
+
+    case 'F':
+      /* %F displays the frame name.  */
+      return (char *) XSTRING (selected_frame->name)->data;
+
     case 'f': 
       obj = b->filename;
 #if 0
@@ -3114,14 +3375,6 @@ decode_mode_spec (w, c, maxwidth)
 #endif
       break;
 
-    case 'c':
-      {
-       int col = current_column ();
-       XSETFASTINT (w->column_number_displayed, col);
-       sprintf (decode_mode_spec_buf, "%d", col);
-       return decode_mode_spec_buf;
-      }
-
     case 'l':
       {
        int startpos = marker_position (w->start);
@@ -3218,44 +3471,6 @@ decode_mode_spec (w, c, maxwidth)
        return " Narrow";
       break;
 
-    case '*':
-      if (!NILP (b->read_only))
-       return "%";
-      if (BUF_MODIFF (b) > BUF_SAVE_MODIFF (b))
-       return "*";
-      return "-";
-
-    case '+':
-      /* This differs from %* only for a modified read-only buffer.  */
-      if (BUF_MODIFF (b) > BUF_SAVE_MODIFF (b))
-       return "*";
-      if (!NILP (b->read_only))
-       return "%";
-      return "-";
-
-    case '&':
-      /* This differs from %* in ignoring read-only-ness.  */
-      if (BUF_MODIFF (b) > BUF_SAVE_MODIFF (b))
-       return "*";
-      return "-";
-
-    case 's':
-      /* status of process */
-      obj = Fget_buffer_process (w->buffer);
-      if (NILP (obj))
-       return "no process";
-#ifdef subprocesses
-      obj = Fsymbol_name (Fprocess_status (obj));
-#endif
-      break;
-
-    case 't':                  /* indicate TEXT or BINARY */
-#ifdef MODE_LINE_BINARY_TEXT
-      return MODE_LINE_BINARY_TEXT (b);
-#else
-      return "T";
-#endif
-
     case 'p':
       {
        int pos = marker_position (w->start);
@@ -3311,52 +3526,22 @@ decode_mode_spec (w, c, maxwidth)
          }
       }
 
-    case '%':
-      return "%";
-
-    case '[': 
-      {
-       int i;
-       char *p;
-
-       if (command_loop_level > 5)
-         return "[[[... ";
-       p = decode_mode_spec_buf;
-       for (i = 0; i < command_loop_level; i++)
-         *p++ = '[';
-       *p = 0;
-       return decode_mode_spec_buf;
-      }
-
-    case ']': 
-      {
-       int i;
-       char *p;
-
-       if (command_loop_level > 5)
-         return " ...]]]";
-       p = decode_mode_spec_buf;
-       for (i = 0; i < command_loop_level; i++)
-         *p++ = ']';
-       *p = 0;
-       return decode_mode_spec_buf;
-      }
+    case 's':
+      /* status of process */
+      obj = Fget_buffer_process (w->buffer);
+      if (NILP (obj))
+       return "no process";
+#ifdef subprocesses
+      obj = Fsymbol_name (Fprocess_status (obj));
+#endif
+      break;
 
-    case '-':
-      {
-       register char *p;
-       register int i;
-       
-       if (maxwidth < sizeof (lots_of_dashes))
-         return lots_of_dashes;
-       else
-         {
-           for (p = decode_mode_spec_buf, i = maxwidth; i > 0; i--)
-             *p++ = '-';
-           *p = '\0';
-         }
-       return decode_mode_spec_buf;
-      }
+    case 't':                  /* indicate TEXT or BINARY */
+#ifdef MODE_LINE_BINARY_TEXT
+      return MODE_LINE_BINARY_TEXT (b);
+#else
+      return "T";
+#endif
     }
 
   if (STRINGP (obj))
@@ -3595,8 +3780,14 @@ display_string (w, vpos, string, length, hpos, truncate,
       else if (c == 0)
        break;
 
-      if (c >= 040 && c < 0177
-         && (dp == 0 || !VECTORP (DISP_CHAR_VECTOR (dp, c))))
+      if (dp != 0 && VECTORP (DISP_CHAR_VECTOR (dp, c)))
+       {
+         p1 = copy_part_of_rope (f, p1, start,
+                                 XVECTOR (DISP_CHAR_VECTOR (dp, c))->contents,
+                                 XVECTOR (DISP_CHAR_VECTOR (dp, c))->size,
+                                 0);
+       }
+      else if (c >= 040 && c < 0177)
        {
          if (p1 >= start)
            *p1 = c;
@@ -3612,13 +3803,6 @@ display_string (w, vpos, string, length, hpos, truncate,
            }
          while ((p1 - start + hscroll - (hscroll > 0)) % tab_width);
        }
-      else if (dp != 0 && VECTORP (DISP_CHAR_VECTOR (dp, c)))
-       {
-         p1 = copy_part_of_rope (f, p1, start,
-                                 XVECTOR (DISP_CHAR_VECTOR (dp, c))->contents,
-                                 XVECTOR (DISP_CHAR_VECTOR (dp, c))->size,
-                                 0);
-       }
       else if (c < 0200 && ! NILP (buffer_defaults.ctl_arrow))
        {
          if (p1 >= start)
@@ -3672,6 +3856,86 @@ display_string (w, vpos, string, length, hpos, truncate,
   }
 }
 \f
+/* This is like a combination of memq and assq.
+   Return 1 if PROPVAL appears as an element of LIST
+   or as the car of an element of LIST.
+   If PROPVAL is a list, compare each element against LIST
+   in that way, and return 1 if any element of PROPVAL is found in LIST.
+   Otherwise return 0.
+   This function cannot quit.  */
+
+int
+invisible_p (propval, list)
+     register Lisp_Object propval;
+     Lisp_Object list;
+{
+  register Lisp_Object tail, proptail;
+  for (tail = list; CONSP (tail); tail = XCONS (tail)->cdr)
+    {
+      register Lisp_Object tem;
+      tem = XCONS (tail)->car;
+      if (EQ (propval, tem))
+       return 1;
+      if (CONSP (tem) && EQ (propval, XCONS (tem)->car))
+       return 1;
+    }
+  if (CONSP (propval))
+    for (proptail = propval; CONSP (proptail);
+        proptail = XCONS (proptail)->cdr)
+      {
+       Lisp_Object propelt;
+       propelt = XCONS (proptail)->car;
+       for (tail = list; CONSP (tail); tail = XCONS (tail)->cdr)
+         {
+           register Lisp_Object tem;
+           tem = XCONS (tail)->car;
+           if (EQ (propelt, tem))
+             return 1;
+           if (CONSP (tem) && EQ (propelt, XCONS (tem)->car))
+             return 1;
+         }
+      }
+  return 0;
+}
+
+/* Return 1 if PROPVAL appears as the car of an element of LIST
+   and the cdr of that element is non-nil.
+   If PROPVAL is a list, check each element of PROPVAL in that way,
+   and the first time some element is found,
+   return 1 if the cdr of that element is non-nil.
+   Otherwise return 0.
+   This function cannot quit.  */
+
+int
+invisible_ellipsis_p (propval, list)
+     register Lisp_Object propval;
+     Lisp_Object list;
+{
+  register Lisp_Object tail, proptail;
+  for (tail = list; CONSP (tail); tail = XCONS (tail)->cdr)
+    {
+      register Lisp_Object tem;
+      tem = XCONS (tail)->car;
+      if (CONSP (tem) && EQ (propval, XCONS (tem)->car))
+       return ! NILP (XCONS (tem)->cdr);
+    }
+  if (CONSP (propval))
+    for (proptail = propval; CONSP (proptail);
+        proptail = XCONS (proptail)->cdr)
+      {
+       Lisp_Object propelt;
+       propelt = XCONS (proptail)->car;
+       for (tail = list; CONSP (tail); tail = XCONS (tail)->cdr)
+         {
+           register Lisp_Object tem;
+           tem = XCONS (tail)->car;
+           if (CONSP (tem) && EQ (propelt, XCONS (tem)->car))
+             return ! NILP (XCONS (tem)->cdr);
+         }
+      }
+  return 0;
+}
+\f
 void
 syms_of_xdisp ()
 {
@@ -3753,6 +4017,14 @@ and is used only on frames for which no explicit name has been set\n\
 If nil, disable message logging.  If t, log messages but don't truncate\n\
 the buffer when it becomes large.");
   XSETFASTINT (Vmessage_log_max, 50);
+
+  DEFVAR_LISP ("window-size-change-functions", &Vwindow_size_change_functions,
+    "Functions called before redisplay, if window sizes have changed.\n\
+The value should be a list of functions that take one argument.\n\
+Just before redisplay, for each frame, if any of its windows have changed\n\
+size since the last redisplay, or have been split or deleted,\n\
+all the functions in the list are called, with the frame as argument.");
+  Vwindow_size_change_functions = Qnil;
 }
 
 /* initialize the window system */