Even more compile stuff.
[bpt/emacs.git] / src / xdisp.c
index 1ee9158..18ae7e8 100644 (file)
@@ -1,5 +1,5 @@
 /* Display generation from window structure and buffer text.
-   Copyright (C) 1985, 1986, 1987, 1988, 1993 Free Software Foundation, Inc.
+   Copyright (C) 1985, 86, 87, 88, 93, 94 Free Software Foundation, Inc.
 
 This file is part of GNU Emacs.
 
@@ -18,7 +18,7 @@ along with GNU Emacs; see the file COPYING.  If not, write to
 the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
 
 
-#include "config.h"
+#include <config.h>
 #include <stdio.h>
 /*#include <ctype.h>*/
 #undef NULL
@@ -33,6 +33,11 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
 #include "macros.h"
 #include "disptab.h"
 #include "termhooks.h"
+#include "intervals.h"
+
+#ifdef USE_X_TOOLKIT
+extern void set_frame_menubar ();
+#endif
 
 extern int interrupt_input;
 extern int command_loop_level;
@@ -89,9 +94,14 @@ Lisp_Object Voverlay_arrow_string;
 /* Values of those variables at last redisplay.  */
 static Lisp_Object last_arrow_position, last_arrow_string;
 
+Lisp_Object Qmenu_bar_update_hook;
+
 /* Nonzero if overlay arrow has been displayed once in this window.  */
 static int overlay_arrow_seen;
 
+/* Nonzero means highlight the region even in nonselected windows.  */
+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;
@@ -120,6 +130,8 @@ static void echo_area_display ();
 void mark_window_display_accurate ();
 static void redisplay_windows ();
 static void redisplay_window ();
+static void update_menu_bars ();
+static void update_menu_bar ();
 static void try_window ();
 static int try_window_id ();
 static struct position *display_text_line ();
@@ -128,6 +140,8 @@ static int display_mode_element ();
 static char *fmodetrunc ();
 static char *decode_mode_spec ();
 static int display_string ();
+static void display_menu_bar ();
+static int display_count_lines ();
 
 /* Prompt to display in front of the minibuffer contents */
 char *minibuf_prompt;
@@ -141,6 +155,9 @@ int minibuf_prompt_width;
    It overrides the minibuf_prompt as well as the buffer.  */
 char *echo_area_glyphs;
 
+/* This is the length of the message in echo_area_glyphs.  */
+int echo_area_glyphs_length;
+
 /* true iff we should redraw the mode lines on the next redisplay */
 int update_mode_lines;
 
@@ -167,19 +184,31 @@ int clip_changed;
    since last redisplay that finished */
 int windows_or_buffers_changed;
 
+/* Nonzero after display_mode_line if %l was used
+   and it displayed a line number.  */
+int line_number_displayed;
+
+/* Maximum buffer size for which to display line numbers.  */
+int line_number_display_limit;
 \f
-/* Specify m, a string, as a message in the minibuf.  If m is 0, clear out
-   any existing message, and let the minibuffer text show through.  */
+/* 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.
+   Do not pass text that is stored in a Lisp string.  */
+
 void
-message1 (m)
+message2 (m, len)
      char *m;
+     int len;
 {
   if (noninteractive)
     {
       if (noninteractive_need_newline)
        putc ('\n', stderr);
       noninteractive_need_newline = 0;
-      fprintf (stderr, "%s\n", m);
+      fwrite (m, len, 1, stderr);
+      if (cursor_in_echo_area == 0)
+       fprintf (stderr, "\n");
       fflush (stderr);
     }
   /* A null message buffer means that the frame hasn't really been
@@ -199,7 +228,10 @@ message1 (m)
 #endif
 
       if (m)
-       echo_area_glyphs = m;
+       {
+         echo_area_glyphs = m;
+         echo_area_glyphs_length = len;
+       }
       else
        echo_area_glyphs = previous_echo_glyphs = 0;
 
@@ -207,9 +239,32 @@ message1 (m)
       echo_area_display ();
       update_frame (XFRAME (XWINDOW (minibuf_window)->frame), 1, 1);
       do_pending_window_change ();
+      if (frame_up_to_date_hook != 0 && ! gc_in_progress)
+       (*frame_up_to_date_hook) (XFRAME (XWINDOW (minibuf_window)->frame));
     }
 }
 
+void
+message1 (m)
+     char *m;
+{
+  message2 (m, (m ? strlen (m) : 0));
+}
+
+/* Truncate what will be displayed in the echo area
+   the next time we display it--but don't redisplay it now.  */
+
+void
+truncate_echo_area (len)
+     int len;
+{
+  /* A null message buffer means that the frame hasn't really been
+     initialized yet.  Error messages get reported properly by
+     cmd_error, so this must be just an informative message; toss it.  */
+  if (!noninteractive && INTERACTIVE && FRAME_MESSAGE_BUF (selected_frame))
+    echo_area_glyphs_length = len;
+}
+
 /* Nonzero if FRAME_MESSAGE_BUF (selected_frame) is being used by print;
    zero if being used by message.  */
 int message_buf_print;
@@ -229,7 +284,8 @@ message (m, a1, a2, a3)
            putc ('\n', stderr);
          noninteractive_need_newline = 0;
          fprintf (stderr, m, a1, a2, a3);
-         fprintf (stderr, "\n");
+         if (cursor_in_echo_area == 0)
+           fprintf (stderr, "\n");
          fflush (stderr);
        }
     }
@@ -253,22 +309,21 @@ message (m, a1, a2, a3)
        {
          if (m)
            {
-             {
+             int len;
 #ifdef NO_ARG_ARRAY
-               int a[3];
-               a[0] = a1;
-               a[1] = a2;
-               a[2] = a3;
+             int a[3];
+             a[0] = a1;
+             a[1] = a2;
+             a[2] = a3;
 
-               doprnt (FRAME_MESSAGE_BUF (echo_frame),
-                       FRAME_WIDTH (echo_frame), m, 0, 3, a);
+             len = doprnt (FRAME_MESSAGE_BUF (echo_frame),
+                           FRAME_WIDTH (echo_frame), m, 0, 3, a);
 #else
-               doprnt (FRAME_MESSAGE_BUF (echo_frame),
-                       FRAME_WIDTH (echo_frame), m, 0, 3, &a1);
+             len = doprnt (FRAME_MESSAGE_BUF (echo_frame),
+                           FRAME_WIDTH (echo_frame), m, 0, 3, &a1);
 #endif /* NO_ARG_ARRAY */
-             }
 
-             message1 (FRAME_MESSAGE_BUF (echo_frame));
+             message2 (FRAME_MESSAGE_BUF (echo_frame), len);
            }
          else
            message1 (0);
@@ -297,7 +352,7 @@ echo_area_display ()
 
   if (frame_garbaged)
     {
-      Fredraw_display ();
+      redraw_garbaged_frames ();
       frame_garbaged = 0;
     }
 
@@ -307,7 +362,8 @@ echo_area_display ()
       get_display_line (f, vpos, 0);
       display_string (XWINDOW (minibuf_window), vpos,
                      echo_area_glyphs ? echo_area_glyphs : "",
-                     0, 0, 0, FRAME_WIDTH (f));
+                     echo_area_glyphs ? echo_area_glyphs_length : -1,
+                     0, 0, 0, 0, FRAME_WIDTH (f));
 
       /* If desired cursor location is on this line, put it at end of text */
       if (FRAME_CURSOR_Y (f) == vpos)
@@ -317,11 +373,12 @@ echo_area_display ()
       {
        int i;
 
-       for (i = vpos + 1; i < vpos + XWINDOW (minibuf_window)->height; i++)
+       for (i = vpos + 1;
+            i < vpos + XFASTINT (XWINDOW (minibuf_window)->height); i++)
          {
            get_display_line (f, i, 0);
            display_string (XWINDOW (minibuf_window), vpos,
-                           "", 0, 0, 0, FRAME_WIDTH (f));
+                           "", 0, 0, 0, 0, 0, FRAME_WIDTH (f));
          }
       }
     }
@@ -333,6 +390,84 @@ echo_area_display ()
 
   previous_echo_glyphs = echo_area_glyphs;
 }
+
+#ifdef HAVE_X_WINDOWS
+/* I'm trying this out because I saw Unimpress use it, but it's
+   possible that this may mess adversely with some window managers.  -jla
+
+   Wouldn't it be nice to use something like mode-line-format to
+   describe frame titles?  -JimB  */
+
+/* Change the title of the frame to the name of the buffer displayed
+   in the currently selected window.  Don't do this for minibuffer frames,
+   and don't do it when there's only one non-minibuffer frame.  */
+static void
+x_consider_frame_title (frame)
+     Lisp_Object frame;
+{
+  FRAME_PTR f = XFRAME (frame);
+
+  if (FRAME_X_P (f) && ! FRAME_MINIBUF_ONLY_P (f))
+    {
+      Lisp_Object title;
+
+      title = Qnil;
+      if (! EQ (Fnext_frame (frame, Qnil), frame))
+       title = XBUFFER (XWINDOW (f->selected_window)->buffer)->name;
+
+      x_implicitly_set_name (f, title, Qnil);
+    }
+}
+#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.  */
+
+void
+prepare_menu_bars ()
+{
+  register struct window *w = XWINDOW (selected_window);
+  int all_windows;
+
+  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 ();
+
+  if (frame_garbaged)
+    {
+      redraw_garbaged_frames ();
+      frame_garbaged = 0;
+    }
+
+  all_windows = (update_mode_lines || buffer_shared > 1
+                || clip_changed || windows_or_buffers_changed);
+
+  /* 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.
    This is the main external entry point for redisplay.
@@ -341,14 +476,19 @@ echo_area_display ()
    message is no longer requested, we clear the echo area
    or bring back the minibuffer if that is in use.
 
-   Everyone would like to have a hook here to call eval,
-   but that cannot be done safely without a lot of changes elsewhere.
-   This can be called from signal handlers; with alarms set up;
+   Do not call eval from within this function.
+   Calls to eval after the call to echo_area_display would confuse
+   the display_line mechanism and would cause a crash.
+   Calls to eval before that point will work most of the time,
+   but can still lose, because  this function
+   can be called from signal handlers; with alarms set up;
    or with synchronous processes running.
-   See the function `echo' in keyboard.c.
+
    See Fcall_process; if you called it from here, it could be
    entered recursively.  */
 
+static int do_verify_charstarts;
+
 void
 redisplay ()
 {
@@ -368,11 +508,10 @@ redisplay ()
      to know if their frames are visible.
      See the comment in frame.h for FRAME_SAMPLE_VISIBILITY.  */
   {
-    Lisp_Object tail;
-    FRAME_PTR f;
+    Lisp_Object tail, frame;
 
-    FOR_EACH_FRAME (tail, f)
-      FRAME_SAMPLE_VISIBILITY (f);
+    FOR_EACH_FRAME (tail, frame)
+      FRAME_SAMPLE_VISIBILITY (XFRAME (frame));
   }
 
   /* Notice any pending interrupt request to change frame size.  */
@@ -380,20 +519,10 @@ redisplay ()
 
   if (frame_garbaged)
     {
-      Fredraw_display ();
+      redraw_garbaged_frames ();
       frame_garbaged = 0;
     }
 
-  /* Normally the message* functions will have already displayed and
-     updated the echo area, but the frame may have been trashed, or
-     the update may have been preempted, so display the echo area
-     again here.  */
-  if (echo_area_glyphs || previous_echo_glyphs)
-    {
-      echo_area_display ();
-      must_finish = 1;
-    }
-
   if (clip_changed || windows_or_buffers_changed)
     update_mode_lines++;
 
@@ -416,6 +545,25 @@ redisplay ()
       || ! EQ (Voverlay_arrow_string, last_arrow_string))
     all_windows = 1, clip_changed = 1;
 
+  /* Normally the message* functions will have already displayed and
+     updated the echo area, but the frame may have been trashed, or
+     the update may have been preempted, so display the echo area
+     again here.  */
+  if (echo_area_glyphs || previous_echo_glyphs)
+    {
+      echo_area_display ();
+      must_finish = 1;
+    }
+
+  /* If showing region, and mark has changed, must redisplay whole window.  */
+  if (((!NILP (Vtransient_mark_mode)
+       && !NILP (XBUFFER (w->buffer)->mark_active))
+       != !NILP (w->region_showing))
+      || (!NILP (w->region_showing)
+         && !EQ (w->region_showing,
+                 Fmarker_position (XBUFFER (w->buffer)->mark))))
+    this_line_bufpos = -1;
+
   tlbufpos = this_line_bufpos;
   tlendpos = this_line_endpos;
   if (!all_windows && tlbufpos > 0 && NILP (w->update_mode_line)
@@ -425,8 +573,8 @@ redisplay ()
       && current_buffer == XBUFFER (w->buffer)
       && NILP (w->force_start)
       /* Point must be on the line that we have info recorded about */
-      && point >= tlbufpos
-      && point <= Z - tlendpos
+      && PT >= tlbufpos
+      && PT <= Z - tlendpos
       /* All text outside that line, including its final newline,
         must be unchanged */
       && (XFASTINT (w->last_modified) >= MODIFF
@@ -459,6 +607,30 @@ redisplay ()
          if (cursor_vpos >= 0 && this_line_bufpos
              && this_line_endpos == tlendpos)
            {
+             /* If this is not the window's last line,
+                we must adjust the charstarts of the lines below.  */
+             if (this_line_vpos + 1
+                 < XFASTINT (w->top) + window_internal_height (w))
+               {
+                 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)
+                   /* This line ends at end of (accessible part of) buffer.
+                      There is no newline to count.  */
+                   adjust = Z - tlendpos - charstart_next_line[left];
+                 else
+                   /* This line ends in a newline.
+                      Must take account of the newline and the rest of the
+                      text that follows.  */
+                   adjust = Z - tlendpos + 1 - charstart_next_line[left];
+
+                 adjust_window_charstarts (w, this_line_vpos, adjust);
+               }
+
              if (XFASTINT (w->width) != FRAME_WIDTH (XFRAME (WINDOW_FRAME (w))))
                preserve_other_columns (w);
              goto update;
@@ -466,7 +638,7 @@ redisplay ()
          else
            goto cancel;
        }
-      else if (point == XFASTINT (w->last_point))
+      else if (PT == XFASTINT (w->last_point))
        {
          if (!must_finish)
            {
@@ -475,14 +647,17 @@ redisplay ()
            }
          goto update;
        }
-      else
+      /* If highlighting the region, we can't just move the cursor.  */
+      else if (! (!NILP (Vtransient_mark_mode)
+                 && !NILP (current_buffer->mark_active))
+              && NILP (w->region_showing))
        {
          pos = *compute_motion (tlbufpos, 0,
                                 XINT (w->hscroll) ? 1 - XINT (w->hscroll) : 0,
-                                point, 2, - (1 << (SHORTBITS - 1)),
+                                PT, 2, - (1 << (SHORTBITS - 1)),
                                 window_internal_width (w) - 1,
                                 XINT (w->hscroll),
-                                pos_tab_offset (w, tlbufpos));
+                                pos_tab_offset (w, tlbufpos), w);
          if (pos.vpos < 1)
            {
              FRAME_CURSOR_X (selected_frame)
@@ -503,15 +678,22 @@ redisplay ()
 
   if (all_windows)
     {
-      Lisp_Object tail;
-      FRAME_PTR f;
+      Lisp_Object tail, frame;
+
+#ifdef HAVE_X_WINDOWS
+      /* Since we're doing a thorough redisplay, we might as well
+        recompute all our display faces.  */
+      clear_face_vector ();
+#endif
 
       /* Recompute # windows showing selected buffer.
         This will be incremented each time such a window is displayed.  */
       buffer_shared = 0;
 
-      FOR_EACH_FRAME (tail, f)
+      FOR_EACH_FRAME (tail, frame)
        {
+         FRAME_PTR f = XFRAME (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)
@@ -519,6 +701,11 @@ redisplay ()
 
          if (FRAME_VISIBLE_P (f))
            redisplay_windows (FRAME_ROOT_WINDOW (f));
+#ifdef HAVE_X_WINDOWS
+         else if (FRAME_ICONIFIED_P (f)
+                  && ! MINI_WINDOW_P (XWINDOW (f->selected_window)))
+           x_consider_frame_title (frame);
+#endif
 
          /* Any scroll bars which redisplay_windows should have nuked
             should now go away.  */
@@ -560,7 +747,11 @@ update:
            {
              pause |= update_frame (f, 0, 0);
              if (!pause)
-               mark_window_display_accurate (f->root_window, 1);
+               {
+                 mark_window_display_accurate (f->root_window, 1);
+                 if (frame_up_to_date_hook != 0)
+                   (*frame_up_to_date_hook) (f);
+               }
            }
        }
     }
@@ -576,8 +767,8 @@ update:
         above call to update_frame would not have caught it.  Catch
         it here.  */
       {
-       FRAME_PTR mini_frame =
-         XFRAME (WINDOW_FRAME (XWINDOW (minibuf_window)));
+       FRAME_PTR mini_frame
+         XFRAME (WINDOW_FRAME (XWINDOW (minibuf_window)));
        
        if (mini_frame != selected_frame)
          pause |= update_frame (mini_frame, 0, 0);
@@ -625,9 +816,13 @@ update:
        {
          w->update_mode_line = Qnil;
          XFASTINT (w->last_modified) = BUF_MODIFF (b);
-         w->window_end_valid = Qt;
+         w->window_end_valid = w->buffer;
          last_arrow_position = Voverlay_arrow_position;
          last_arrow_string = Voverlay_arrow_string;
+         if (do_verify_charstarts)
+           verify_charstarts (w);
+         if (frame_up_to_date_hook != 0)
+           (*frame_up_to_date_hook) (selected_frame);
        }
       update_mode_lines = 0;
       windows_or_buffers_changed = 0;
@@ -681,11 +876,21 @@ mark_window_display_accurate (window, flag)
       w = XWINDOW (window);
 
       if (!NILP (w->buffer))
-       XFASTINT (w->last_modified)
-         = !flag ? 0
-           : XBUFFER (w->buffer) == current_buffer
-             ? MODIFF : BUF_MODIFF (XBUFFER (w->buffer));
-      w->window_end_valid = Qt;
+       {
+         XFASTINT (w->last_modified)
+           = !flag ? 0
+             : XBUFFER (w->buffer) == current_buffer
+               ? MODIFF : BUF_MODIFF (XBUFFER (w->buffer));
+
+         /* Record if we are showing a region, so can make sure to
+            update it fully at next redisplay.  */
+         w->region_showing = (!NILP (Vtransient_mark_mode)
+                              && !NILP (XBUFFER (w->buffer)->mark_active)
+                              ? Fmarker_position (XBUFFER (w->buffer)->mark)
+                              : Qnil);
+       }
+
+      w->window_end_valid = w->buffer;
       w->update_mode_line = Qnil;
 
       if (!NILP (w->vchild))
@@ -707,8 +912,60 @@ mark_window_display_accurate (window, flag)
     }
 }
 \f
+/* Update the menu bar item list for frame F.
+   This has to be done before we start to fill in any display lines,
+   because it can call eval.  */
+
+static void
+update_menu_bar (f)
+     FRAME_PTR f;
+{
+  struct buffer *old = current_buffer;
+  Lisp_Object window;
+  register struct window *w;
+  window = FRAME_SELECTED_WINDOW (f);
+  w = XWINDOW (window);
+  
+  if (update_mode_lines)
+    w->update_mode_line = Qt;
+
+  if (
+#ifdef USE_X_TOOLKIT
+      FRAME_EXTERNAL_MENU_BAR (f) 
+#else
+      FRAME_MENU_BAR_LINES (f) > 0
+#endif
+      )
+    {
+      /* If the user has switched buffers or windows, we need to
+        recompute to reflect the new bindings.  But we'll
+        recompute when update_mode_lines is set too; that means
+        that people can use force-mode-line-update to request
+        that the menu bar be recomputed.  The adverse effect on
+        the rest of the redisplay algorithm is about the same as
+        windows_or_buffers_changed anyway.  */
+      if (windows_or_buffers_changed
+         || !NILP (w->update_mode_line)
+         || (XFASTINT (w->last_modified) < MODIFF
+             && (XFASTINT (w->last_modified)
+                 <= XBUFFER (w->buffer)->save_modified)))
+       {
+         struct buffer *prev = current_buffer;
+         call1 (Vrun_hooks, Qmenu_bar_update_hook);
+         current_buffer = XBUFFER (w->buffer);
+         FRAME_MENU_BAR_ITEMS (f) = menu_bar_items (FRAME_MENU_BAR_ITEMS (f));
+         current_buffer = prev;
+#ifdef USE_X_TOOLKIT
+         set_frame_menubar (f, 0);
+#endif /* USE_X_TOOLKIT */
+       }
+    }
+}
+\f
 int do_id = 1;
 
+/* Redisplay WINDOW and its subwindows and siblings.  */
+
 static void
 redisplay_windows (window)
      Lisp_Object window;
@@ -717,6 +974,8 @@ redisplay_windows (window)
     redisplay_window (window, 0);
 }
 
+/* Redisplay window WINDOW and its subwindows.  */
+
 static void
 redisplay_window (window, just_this_one)
      Lisp_Object window;
@@ -725,13 +984,13 @@ redisplay_window (window, just_this_one)
   register struct window *w = XWINDOW (window);
   FRAME_PTR f = XFRAME (WINDOW_FRAME (w));
   int height;
-  register int lpoint = point;
+  register int lpoint = PT;
   struct buffer *old = current_buffer;
   register int width = window_internal_width (w) - 1;
   register int startp;
   register int hscroll = XINT (w->hscroll);
   struct position pos;
-  int opoint = point;
+  int opoint = PT;
   int tem;
   int window_needs_modeline;
 
@@ -772,7 +1031,7 @@ redisplay_window (window, just_this_one)
          for (i = 0; i < height; i++)
            {
              get_display_line (f, vpos + i, 0);
-             display_string (w, vpos + i, "", 0, 0, 0, width);
+             display_string (w, vpos + i, "", 0, 0, 0, 1, 0, width);
            }
          
          goto finish_scroll_bars;
@@ -785,7 +1044,7 @@ redisplay_window (window, just_this_one)
   /* Otherwise set up data on this window; select its buffer and point value */
 
   current_buffer = XBUFFER (w->buffer);
-  opoint = point;
+  opoint = PT;
 
   /* Count number of windows showing the selected buffer.  */
 
@@ -799,15 +1058,15 @@ redisplay_window (window, just_this_one)
   if (!EQ (window, selected_window))
     {
       SET_PT (marker_position (w->pointm));
-      if (point < BEGV)
+      if (PT < BEGV)
        {
          SET_PT (BEGV);
-         Fset_marker (w->pointm, make_number (point), Qnil);
+         Fset_marker (w->pointm, make_number (PT), Qnil);
        }
-      else if (point > (ZV - 1))
+      else if (PT > (ZV - 1))
        {
          SET_PT (ZV);
-         Fset_marker (w->pointm, make_number (point), Qnil);
+         Fset_marker (w->pointm, make_number (PT), Qnil);
        }
     }
 
@@ -818,9 +1077,11 @@ redisplay_window (window, just_this_one)
   startp = marker_position (w->start);
 
   /* Handle case where place to start displaying has been specified,
-     unless the specified location is outside the visible range.  */
+     unless the specified location is outside the accessible range.  */
   if (!NILP (w->force_start))
     {
+      /* Forget any recorded base line for line number display.  */
+      w->base_line_number = Qnil;
       w->update_mode_line = Qt;
       w->force_start = Qnil;
       XFASTINT (w->last_modified) = 0;
@@ -829,6 +1090,7 @@ redisplay_window (window, just_this_one)
       try_window (window, startp);
       if (cursor_vpos < 0)
        {
+         /* ??? What should happen here if highlighting a region?  */
          /* If point does not appear, move point so it does appear */
          pos = *compute_motion (startp, 0,
                                ((EQ (window, minibuf_window) && startp == 1)
@@ -837,13 +1099,14 @@ redisplay_window (window, just_this_one)
                                (hscroll ? 1 - hscroll : 0),
                                ZV, height / 2,
                                - (1 << (SHORTBITS - 1)),
-                               width, hscroll, pos_tab_offset (w, startp));
+                               width, hscroll, pos_tab_offset (w, startp), w);
          SET_PT (pos.bufpos);
-         if (w != XWINDOW (FRAME_SELECTED_WINDOW (f)))
-           Fset_marker (w->pointm, make_number (point), Qnil);
+         if (w != XWINDOW (selected_window))
+           Fset_marker (w->pointm, make_number (PT), Qnil);
          else
            {
-             lpoint = point;
+             if (current_buffer == old)
+               lpoint = PT;
              FRAME_CURSOR_X (f) = max (0, pos.hpos) + XFASTINT (w->left);
              FRAME_CURSOR_Y (f) = pos.vpos + XFASTINT (w->top);
            }
@@ -862,13 +1125,16 @@ redisplay_window (window, just_this_one)
      in redisplay handles the same cases.  */
 
   if (XFASTINT (w->last_modified) >= MODIFF
-      && point >= startp && !clip_changed
+      && PT >= startp && !clip_changed
       && (just_this_one || XFASTINT (w->width) == FRAME_WIDTH (f))
+      /* Can't use this case if highlighting a region.  */
+      && !(!NILP (Vtransient_mark_mode) && !NILP (current_buffer->mark_active))
+      && NILP (w->region_showing)
       && !EQ (window, minibuf_window))
     {
       pos = *compute_motion (startp, 0, (hscroll ? 1 - hscroll : 0),
-                           point, height + 1, 10000, width, hscroll,
-                           pos_tab_offset (w, startp));
+                           PT, height + 1, 10000, width, hscroll,
+                           pos_tab_offset (w, startp), w);
 
       if (pos.vpos < height)
        {
@@ -900,12 +1166,18 @@ redisplay_window (window, just_this_one)
       goto recenter;
     }
   else if (just_this_one && !MINI_WINDOW_P (w)
-          && point >= startp
+          && PT >= startp
           && XFASTINT (w->last_modified)
+          /* or else vmotion on first line won't work.  */
+          && ! NILP (w->start_at_line_beg)
           && ! EQ (w->window_end_valid, Qnil)
           && do_id && !clip_changed
           && !blank_end_of_window
           && XFASTINT (w->width) == FRAME_WIDTH (f)
+          /* Can't use this case if highlighting a region.  */
+          && !(!NILP (Vtransient_mark_mode)
+               && !NILP (current_buffer->mark_active))
+          && NILP (w->region_showing)
           && EQ (last_arrow_position, Voverlay_arrow_position)
           && EQ (last_arrow_string, Voverlay_arrow_string)
           && (tem = try_window_id (FRAME_SELECTED_WINDOW (f)))
@@ -927,7 +1199,12 @@ redisplay_window (window, just_this_one)
       /* If point has not moved off frame, accept the results */
       try_window (window, startp);
       if (cursor_vpos >= 0)
-       goto done;
+       {
+         if (!just_this_one || clip_changed || beg_unchanged < startp)
+           /* Forget any recorded base line for line number display.  */
+           w->base_line_number = Qnil;
+         goto done;
+       }
       else
        cancel_my_columns (w);
     }
@@ -939,7 +1216,7 @@ redisplay_window (window, just_this_one)
 
   if (scroll_step && !clip_changed)
     {
-      if (point > startp)
+      if (PT > startp)
        {
          pos = *vmotion (Z - XFASTINT (w->window_end_pos),
                          scroll_step, width, hscroll, window);
@@ -947,14 +1224,19 @@ redisplay_window (window, just_this_one)
            goto scroll_fail;
        }
 
-      pos = *vmotion (startp, point < startp ? - scroll_step : scroll_step,
+      pos = *vmotion (startp, PT < startp ? - scroll_step : scroll_step,
                      width, hscroll, window);
 
-      if (point >= pos.bufpos)
+      if (PT >= pos.bufpos)
        {
          try_window (window, pos.bufpos);
          if (cursor_vpos >= 0)
-           goto done;
+           {
+             if (!just_this_one || clip_changed || beg_unchanged < startp)
+               /* Forget any recorded base line for line number display.  */
+               w->base_line_number = Qnil;
+             goto done;
+           }
          else
            cancel_my_columns (w);
        }
@@ -964,7 +1246,10 @@ redisplay_window (window, just_this_one)
   /* Finally, just choose place to start which centers point */
 
 recenter:
-  pos = *vmotion (point, - height / 2, width, hscroll, window);
+  /* Forget any previously recorded base line for line number display.  */
+  w->base_line_number = Qnil;
+
+  pos = *vmotion (PT, - (height / 2), width, hscroll, window);
   try_window (window, pos.bufpos);
 
   startp = marker_position (w->start);
@@ -972,12 +1257,29 @@ recenter:
     (startp == BEGV || FETCH_CHAR (startp - 1) == '\n') ? Qt : Qnil;
 
 done:
-  /* If window not full width, must redo its mode line
-     if the window to its side is being redone */
   if ((!NILP (w->update_mode_line)
-       || (!just_this_one && width < FRAME_WIDTH (f) - 1))
+       /* 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)
+       || INTEGERP (w->base_line_pos))
       && height != XFASTINT (w->height))
     display_mode_line (w);
+  if (! line_number_displayed
+      && ! BUFFERP (w->base_line_pos))
+    {
+      w->base_line_pos = Qnil;
+      w->base_line_number = Qnil;
+    }
+
+  /* When we reach a frame's selected window, redo the frame's menu bar.  */
+  if (!NILP (w->update_mode_line)
+#ifdef USE_X_TOOLKIT
+      && FRAME_EXTERNAL_MENU_BAR (f) 
+#else
+      && FRAME_MENU_BAR_LINES (f) > 0
+#endif
+      && EQ (FRAME_SELECTED_WINDOW (f), window))
+    display_menu_bar (w);
 
  finish_scroll_bars:
   if (FRAME_HAS_VERTICAL_SCROLL_BARS (f))
@@ -985,21 +1287,29 @@ done:
       int start, end, whole;
 
       /* Calculate the start and end positions for the current window.
+        At some point, it would be nice to choose between scrollbars
+        which reflect the whole buffer size, with special markers
+        indicating narrowing, and scrollbars which reflect only the
+        visible region.
+
         Note that minibuffers sometimes aren't displaying any text.  */
       if (! MINI_WINDOW_P (w)
          || (w == XWINDOW (minibuf_window) && ! echo_area_glyphs))
        {
-         start = startp;
+         whole = ZV - BEGV;
+         start = startp - 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);
-         whole = Z - BEG;
+         end = (Z - XINT (w->window_end_pos)) - BEGV;
+
+         if (end < start) end = start;
+         if (whole < (end - start)) whole = end - start;
        }
       else
        start = end = whole = 0;
 
       /* Indicate what this scroll bar ought to be displaying now.  */
-      (*set_vertical_scroll_bar_hook) (w, end - start, whole, start - 1);
+      (*set_vertical_scroll_bar_hook) (w, end - start, whole, start);
 
       /* Note that we actually used the scroll bar attached to this window,
         so it shouldn't be deleted at the end of redisplay.  */
@@ -1041,7 +1351,13 @@ try_window (window, pos)
       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');
+         = vpos - (val.vpos && (FETCH_CHAR (val.bufpos - 1) != '\n'
+#ifdef USE_TEXT_PROPERTIES
+                                || ! NILP (Fget_char_property (val.bufpos-1,
+                                                               Qinvisible,
+                                                               window))
+#endif
+                                ));
       pos = val.bufpos;
     }
 
@@ -1086,6 +1402,10 @@ try_window_id (window)
   register int i, tem;
   int last_text_vpos = 0;
   int stop_vpos;
+  int selective
+    = XTYPE (current_buffer->selective_display) == Lisp_Int
+      ? XINT (current_buffer->selective_display)
+       : !NILP (current_buffer->selective_display) ? -1 : 0;
 
   struct position val, bp, ep, xp, pp;
   int scroll_amount = 0;
@@ -1097,16 +1417,16 @@ try_window_id (window)
   if (Z - GPT < end_unchanged)
     end_unchanged = Z - GPT;
 
-  if (beg_unchanged + 1 < start)
+  if (beg_unchanged + BEG < start)
     return 0;                  /* Give up if changes go above top of window */
 
   /* Find position before which nothing is changed.  */
   bp = *compute_motion (start, 0, lmargin,
-                       beg_unchanged + 1, height + 1, 0, width, hscroll,
-                       pos_tab_offset (w, start));
+                       min (ZV, beg_unchanged + BEG), height + 1, 0,
+                       width, hscroll, pos_tab_offset (w, start), w);
   if (bp.vpos >= height)
     {
-      if (point < bp.bufpos && !bp.contin)
+      if (PT < bp.bufpos && !bp.contin)
        {
          /* All changes are below the frame, and point is on the frame.
             We don't need to change the frame at all.
@@ -1114,7 +1434,7 @@ try_window_id (window)
             any change in buffer size.  */
          bp = *compute_motion (start, 0, lmargin,
                                Z, height, 0,
-                               width, hscroll, pos_tab_offset (w, start));
+                               width, hscroll, pos_tab_offset (w, start), w);
          XFASTINT (w->window_end_vpos) = height;
          XFASTINT (w->window_end_pos) = Z - bp.bufpos;
          return 1;
@@ -1138,9 +1458,7 @@ try_window_id (window)
   if ((bp.contin && bp.bufpos - 1 == beg_unchanged && vpos > 0)
       ||
       /* Likewise if we have to worry about selective display.  */
-      (XTYPE (current_buffer->selective_display) == Lisp_Int
-       && XINT (current_buffer->selective_display) > 0
-       && bp.bufpos - 1 == beg_unchanged && vpos > 0))
+      (selective > 0 && bp.bufpos - 1 == beg_unchanged && vpos > 0))
     {
       bp = *vmotion (bp.bufpos, -1, width, hscroll, window);
       --vpos;
@@ -1157,17 +1475,14 @@ try_window_id (window)
 
   /* Find first visible newline after which no more is changed.  */
   tem = find_next_newline (Z - max (end_unchanged, Z - ZV), 1);
-  if (XTYPE (current_buffer->selective_display) == Lisp_Int
-      && XINT (current_buffer->selective_display) > 0)
-    while (tem < ZV - 1
-          && (position_indentation (tem)
-              >= XINT (current_buffer->selective_display)))
+  if (selective > 0)
+    while (tem < ZV - 1 && (indented_beyond_p (tem, selective)))
       tem = find_next_newline (tem, 1);
 
   /* Compute the cursor position after that newline.  */
   ep = *compute_motion (pos, vpos, val.hpos, tem,
                        height, - (1 << (SHORTBITS - 1)),
-                       width, hscroll, pos_tab_offset (w, bp.bufpos));
+                       width, hscroll, pos_tab_offset (w, bp.bufpos), w);
 
   /* If changes reach past the text available on the frame,
      just display rest of frame.  */
@@ -1198,7 +1513,7 @@ try_window_id (window)
       epto = pos_tab_offset (w, ep.bufpos);
       xp = *compute_motion (ep.bufpos, ep.vpos, ep.hpos,
                            Z - XFASTINT (w->window_end_pos),
-                           10000, 0, width, hscroll, epto);
+                           10000, 0, width, hscroll, epto, w);
       scroll_amount = xp.vpos - XFASTINT (w->window_end_vpos);
 
       /* Is everything on frame below the changes whitespace?
@@ -1215,21 +1530,22 @@ try_window_id (window)
       XFASTINT (w->window_end_vpos) += scroll_amount;
 
       /* Before doing any scrolling, verify that point will be on frame. */
-      if (point > ep.bufpos && !(point <= xp.bufpos && xp.bufpos < height))
+      if (PT > ep.bufpos && !(PT <= xp.bufpos && xp.bufpos < height))
        {
-         if (point <= xp.bufpos)
+         if (PT <= xp.bufpos)
            {
              pp = *compute_motion (ep.bufpos, ep.vpos, ep.hpos,
-                                   point, height, - (1 << (SHORTBITS - 1)),
-                                   width, hscroll, epto);
+                                   PT, height, - (1 << (SHORTBITS - 1)),
+                                   width, hscroll, epto, w);
            }
          else
            {
              pp = *compute_motion (xp.bufpos, xp.vpos, xp.hpos,
-                                   point, height, - (1 << (SHORTBITS - 1)),
-                                   width, hscroll, pos_tab_offset (w, xp.bufpos));
+                                   PT, height, - (1 << (SHORTBITS - 1)),
+                                   width, hscroll,
+                                   pos_tab_offset (w, xp.bufpos), w);
            }
-         if (pp.bufpos < point || pp.vpos == height)
+         if (pp.bufpos < PT || pp.vpos == height)
            return 0;
          cursor_vpos = pp.vpos + top;
          cursor_hpos = pp.hpos + XFASTINT (w->left);
@@ -1250,7 +1566,14 @@ try_window_id (window)
          blank_end_of_window = 1;
        }
       else if (!scroll_amount)
-       {}
+       {
+         /* Even if we don't need to scroll, we must adjust the
+            charstarts of subsequent lines (that we won't redisplay)
+            according to the amount of text inserted or deleted.  */
+         int oldpos = FRAME_CURRENT_GLYPHS (f)->charstarts[ep.vpos + top][0];
+         int adjust = ep.bufpos - oldpos;
+         adjust_window_charstarts (w, ep.vpos + top - 1, adjust);
+       }
       else if (bp.bufpos == Z - end_unchanged)
        {
          /* If reprinting everything is nearly as fast as scrolling,
@@ -1267,9 +1590,26 @@ try_window_id (window)
             following line from being overwritten by scrolling
             and therefore having to be redrawn.  */
          tem = scroll_frame_lines (f, bp.vpos + top - scroll_amount,
-                                    top + height - max (0, scroll_amount),
-                                    scroll_amount);
-         if (!tem) stop_vpos = height;
+                                   top + height - max (0, scroll_amount),
+                                   scroll_amount, bp.bufpos);
+         if (!tem)
+           stop_vpos = height;
+         else
+           {
+             /* scroll_frame_lines did not properly adjust subsequent
+                lines' charstarts in the case where the text of the
+                screen line at bp.vpos has changed.
+                (This can happen in a deletion that ends in mid-line.)
+                To adjust properly, we need to make things constent at
+                the position ep.
+                So do a second adjust to make that happen.
+                Note that stop_vpos >= ep.vpos, so it is sufficient
+                to update the charstarts for lines at ep.vpos and below.  */
+             int oldstart
+               = FRAME_CURRENT_GLYPHS (f)->charstarts[ep.vpos + top][0];
+             adjust_window_charstarts (w, ep.vpos + top - 1,
+                                       ep.bufpos - oldstart);
+           }
        }
       else if (scroll_amount)
        {
@@ -1287,7 +1627,7 @@ try_window_id (window)
            return -2;
          tem = scroll_frame_lines (f, ep.vpos + top - scroll_amount,
                                     top + height - max (0, scroll_amount),
-                                    scroll_amount);
+                                    scroll_amount, ep.bufpos);
          if (!tem) stop_vpos = height;
        }
     }
@@ -1373,7 +1713,7 @@ try_window_id (window)
       /* Here is a case where display_text_line sets cursor_vpos wrong.
         Make it be fixed up, below.  */
       if (xp.bufpos == ZV
-         && xp.bufpos == point)
+         && xp.bufpos == PT)
        cursor_vpos = -1;
     }
 
@@ -1402,8 +1742,8 @@ try_window_id (window)
   /* If point was not in a line that was displayed, find it */
   if (cursor_vpos < 0)
     {
-      val = *compute_motion (start, 0, lmargin, point, 10000, 10000,
-                            width, hscroll, pos_tab_offset (w, start));
+      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 */
       if (val.vpos >= height)
        {
@@ -1422,7 +1762,7 @@ try_window_id (window)
     {
       val = *compute_motion (start, 0, lmargin, ZV,
                             height, - (1 << (SHORTBITS - 1)),
-                            width, hscroll, pos_tab_offset (w, start));
+                            width, hscroll, pos_tab_offset (w, start), w);
       if (val.vpos != XFASTINT (w->window_end_vpos))
        abort ();
       if (XFASTINT (w->window_end_pos)
@@ -1433,50 +1773,142 @@ try_window_id (window)
   return 1;
 }
 \f
-/* Copy glyphs from the vector FROM to the rope T.
-   But don't actually copy the parts that would come in before S.
-   Value is T, advanced past the copied data.  */
+/* Mark a section of BUF as modified, but only for the sake of redisplay.
+   This is useful for recording changes to overlays.
 
-GLYPH *
-copy_rope (t, s, from)
-     register GLYPH *t; /* Copy to here. */
-     register GLYPH *s; /* Starting point. */
-     Lisp_Object from;    /* Data to copy; known to be a vector.  */
+   We increment the buffer's modification timestamp and set the
+   redisplay caches (windows_or_buffers_changed, beg_unchanged, etc)
+   as if the region of text between START and END had been modified;
+   the redisplay code will check this against the windows' timestamps,
+   and redraw the appropriate area of the buffer.
+
+   However, if the buffer is unmodified, we bump the last-save
+   timestamp as well, so that incrementing the timestamp doesn't fool
+   Emacs into thinking that the buffer's text has been modified. 
+
+   Tweaking the timestamps shouldn't hurt the first-modification
+   timestamps recorded in the undo records; those values aren't
+   written until just before a real text modification is made, so they
+   will never catch the timestamp value just before this function gets
+   called.  */
+
+void
+redisplay_region (buf, start, end)
+     struct buffer *buf;
+     int start, end;
 {
-  register int n = XVECTOR (from)->size;
-  register Lisp_Object *f = XVECTOR (from)->contents;
+  if (start == end)
+    return;
 
-  while (n--)
+  if (start > end)
     {
-      if (t >= s) *t = *f;
-      ++t;
-      ++f;
+      int temp = start;
+      start = end; end = temp;
+    }
+
+  if (buf != current_buffer)
+    windows_or_buffers_changed = 1;
+  else
+    {
+      if (unchanged_modified == MODIFF)
+       {
+         beg_unchanged = start - BEG;
+         end_unchanged = Z - end;
+       }
+      else
+       {
+         if (Z - end < end_unchanged)
+           end_unchanged = Z - end;
+         if (start - BEG < beg_unchanged)
+           beg_unchanged = start - BEG;
+       }
     }
-  return t;
+
+  /* Increment the buffer's time stamp, but also increment the save
+     and autosave timestamps, so as not to screw up that timekeeping. */
+  if (BUF_MODIFF (buf) == buf->save_modified)
+    buf->save_modified++;
+  if (BUF_MODIFF (buf) == buf->auto_save_modified)
+    buf->auto_save_modified++;
+
+  BUF_MODIFF (buf) ++;
 }
 
-/* Similar but copy at most LEN glyphs.  */
+\f
+/* Copy LEN glyphs starting address FROM to the rope TO.
+   But don't actually copy the parts that would come in before S.
+   Value is TO, advanced past the copied data.
+   F is the frame we are displaying in.  */
 
-GLYPH *
-copy_part_of_rope (t, s, from, len)
-     register GLYPH *t; /* Copy to here. */
+static GLYPH *
+copy_part_of_rope (f, to, s, from, len, face)
+     FRAME_PTR f;
+     register GLYPH *to; /* Copy to here. */
      register GLYPH *s; /* Starting point. */
-     Lisp_Object from;    /* Data to copy; known to be a vector.  */
+     Lisp_Object *from;  /* Data to copy. */
      int len;
+     int face;         /* Face to apply to glyphs which don't specify one. */
 {
-  register int n = XVECTOR (from)->size;
-  register Lisp_Object *f = XVECTOR (from)->contents;
+  int n = len;
+  register Lisp_Object *fp = from;
+  /* These cache the results of the last call to compute_glyph_face.  */
+  int last_code = -1;
+  int last_merged = 0;
+
+#ifdef HAVE_X_WINDOWS
+  if (! FRAME_TERMCAP_P (f))
+    while (n--)
+      {
+       int glyph = (INTEGERP (*fp) ? XFASTINT (*fp) : 0);
+       int facecode;
+
+       if (FAST_GLYPH_FACE (glyph) == 0)
+         /* If GLYPH has no face code, use FACE.  */
+         facecode = face;
+       else if (FAST_GLYPH_FACE (glyph) == last_code)
+         /* If it's same as previous glyph, use same result.  */
+         facecode = last_merged;
+       else
+         {
+           /* Merge this glyph's face and remember the result.  */
+           last_code = FAST_GLYPH_FACE (glyph);
+           last_merged = facecode = compute_glyph_face (f, last_code, face);
+         }
+
+       if (to >= s)
+         *to = FAST_MAKE_GLYPH (FAST_GLYPH_CHAR (glyph), facecode);
+       ++to;
+       ++fp;
+      }
+  else
+#endif
+    while (n--)
+      {
+       if (to >= s) *to = (INTEGERP (*fp) ? XFASTINT (*fp) : 0);
+       ++to;
+       ++fp;
+      }
+  return to;
+}
 
-  if (n > len)
-    n = len;
+/* Correct a glyph by replacing its specified user-level face code
+   with a displayable computed face code.  */
 
-  while (n--)
+static GLYPH
+fix_glyph (f, glyph, cface)
+     FRAME_PTR f;
+     GLYPH glyph;
+     int cface;
+{
+#ifdef HAVE_X_WINDOWS
+  if (! FRAME_TERMCAP_P (f))
     {
-      if (t >= s) *t = *f;
-      ++t;
-      ++f;
+      if (FAST_GLYPH_FACE (glyph) != 0)
+       cface = compute_glyph_face (f, FAST_GLYPH_FACE (glyph), cface);
+      glyph = FAST_MAKE_GLYPH (FAST_GLYPH_CHAR (glyph), cface);
     }
-  return t;
+#endif
+  return glyph;
 }
 \f
 /* Display one line of window w, starting at position START in W's buffer.
@@ -1510,8 +1942,10 @@ display_text_line (w, start, vpos, hpos, taboffset)
   register int pause;
   register unsigned char *p;
   GLYPH *endp;
-  register GLYPH *startp;
-  register GLYPH *p1prev;
+  register GLYPH *leftmargin;
+  register GLYPH *p1prev = 0;
+  register GLYPH *p1start;
+  int *charstart;
   FRAME_PTR f = XFRAME (w->frame);
   int tab_width = XINT (current_buffer->tab_width);
   int ctl_arrow = !NILP (current_buffer->ctl_arrow);
@@ -1520,68 +1954,206 @@ display_text_line (w, start, vpos, hpos, taboffset)
   int lastpos;
   int invis;
   int hscroll = XINT (w->hscroll);
-  int truncate = hscroll
-    || (truncate_partial_width_windows
-       && XFASTINT (w->width) < FRAME_WIDTH (f))
-    || !NILP (current_buffer->truncate_lines);
+  int truncate = (hscroll
+                 || (truncate_partial_width_windows
+                     && XFASTINT (w->width) < FRAME_WIDTH (f))
+                 || !NILP (current_buffer->truncate_lines));
+
+  /* 1 if we should highlight the region.  */
+  int highlight_region
+    = !NILP (Vtransient_mark_mode) && !NILP (current_buffer->mark_active);
+  int region_beg, region_end;
+
   int selective
     = XTYPE (current_buffer->selective_display) == Lisp_Int
       ? XINT (current_buffer->selective_display)
        : !NILP (current_buffer->selective_display) ? -1 : 0;
-#ifndef old
-  int selective_e = selective && !NILP (current_buffer->selective_display_ellipses);
-#endif
   register struct frame_glyphs *desired_glyphs = FRAME_DESIRED_GLYPHS (f);
   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.  */
   int selective_rlen
     = (selective && dp && XTYPE (DISP_INVIS_VECTOR (dp)) == Lisp_Vector
-       ? XVECTOR (DISP_INVIS_VECTOR (dp))->size : 0);
+       ? XVECTOR (DISP_INVIS_VECTOR (dp))->size
+       : selective && !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
+    = (dp && XTYPE (DISP_INVIS_VECTOR (dp)) == Lisp_Vector
+       ? XVECTOR (DISP_INVIS_VECTOR (dp))->contents
+       : default_invis_vector);
+
   GLYPH truncator = (dp == 0 || XTYPE (DISP_TRUNC_GLYPH (dp)) != Lisp_Int
-                   ? '$' : XINT (DISP_TRUNC_GLYPH (dp)));
+                    ? '$' : XINT (DISP_TRUNC_GLYPH (dp)));
   GLYPH continuer = (dp == 0 || XTYPE (DISP_CONTINUE_GLYPH (dp)) != Lisp_Int
-                   ? '\\' : XINT (DISP_CONTINUE_GLYPH (dp)));
+                    ? '\\' : XINT (DISP_CONTINUE_GLYPH (dp)));
+
+  /* The next buffer location at which the face should change, due
+     to overlays or text property changes.  */
+  int next_face_change;
+
+#ifdef USE_TEXT_PROPERTIES
+  /* The next location where the `invisible' property changes */
+  int next_invisible;
+#endif
+  
+  /* The face we're currently using.  */
+  int current_face = 0;
+  int i;
+
+  XFASTINT (default_invis_vector[2]) = '.';
+  default_invis_vector[0] = default_invis_vector[1] = default_invis_vector[2];
 
   hpos += XFASTINT (w->left);
   get_display_line (f, vpos, XFASTINT (w->left));
-  if (tab_width <= 0 || tab_width > 20) tab_width = 8;
+  if (tab_width <= 0 || tab_width > 1000) tab_width = 8;
+
+  /* Show where to highlight the region.  */
+  if (highlight_region && XMARKER (current_buffer->mark)->buffer != 0
+      /* Maybe highlight only in selected window.  */
+      && (highlight_nonselected_windows
+         || w == XWINDOW (selected_window)))
+    {
+      region_beg = marker_position (current_buffer->mark);
+      if (PT < region_beg)
+       {
+         region_end = region_beg;
+         region_beg = PT;
+       }
+      else
+       region_end = PT;
+      w->region_showing = Qt;
+    }
+  else
+    region_beg = region_end = -1;
 
   if (MINI_WINDOW_P (w) && start == 1
       && vpos == XFASTINT (w->top))
     {
       if (minibuf_prompt)
-       hpos = display_string (w, vpos, minibuf_prompt, hpos,
+       {
+         minibuf_prompt_width
+           = (display_string (w, vpos, minibuf_prompt, -1, hpos,
                               (!truncate ? continuer : truncator),
-                              -1, -1);
-      minibuf_prompt_width = hpos;
+                              1, -1, -1)
+              - hpos);
+         hpos += minibuf_prompt_width;
+       }
+      else
+       minibuf_prompt_width = 0;
     }
 
   desired_glyphs->bufp[vpos] = pos;
   p1 = desired_glyphs->glyphs[vpos] + hpos;
+  p1start = p1;
+  charstart = desired_glyphs->charstarts[vpos] + hpos;
+  /* In case we don't ever write anything into it...  */
+  desired_glyphs->charstarts[vpos][XFASTINT (w->left)] = -1;
   end = ZV;
-  startp = desired_glyphs->glyphs[vpos] + XFASTINT (w->left);
-  endp = startp + width;
+  leftmargin = desired_glyphs->glyphs[vpos] + XFASTINT (w->left);
+  endp = leftmargin + width;
+
+  /* Arrange the overlays nicely for our purposes.  Usually, we call
+     display_text_line on only one line at a time, in which case this
+     can't really hurt too much, or we call it on lines which appear
+     one after another in the buffer, in which case all calls to
+     recenter_overlay_lists but the first will be pretty cheap.  */
+  recenter_overlay_lists (current_buffer, pos);
 
   /* Loop generating characters.
      Stop at end of buffer, before newline,
-     or if reach or pass continuation column.  */
-
+     if reach or pass continuation column,
+     or at face change.  */
   pause = pos;
-  while (p1 < endp)
+  next_face_change = pos;
+#ifdef USE_TEXT_PROPERTIES
+  next_invisible = pos;
+#endif
+  while (1)
     {
+      /* Record which glyph starts a character,
+        and the character position of that character.  */
+      if (p1 >= leftmargin)
+       charstart[p1 - p1start] = pos;
+
+      if (p1 >= endp)
+       break;
+
       p1prev = p1;
-      if (pos == pause)
+      if (pos >= pause)
        {
-         if (pos == end)
+         /* Did we hit the end of the visible region of the buffer?
+            Stop here.  */
+         if (pos >= end)
            break;
-         if (pos == point && cursor_vpos < 0)
+
+         /* Did we reach point?  Record the cursor location.  */
+         if (pos == PT && cursor_vpos < 0)
            {
              cursor_vpos = vpos;
-             cursor_hpos = p1 - startp;
+             cursor_hpos = p1 - leftmargin;
            }
 
+#ifdef USE_TEXT_PROPERTIES
+         /* if the `invisible' property is set to t, we can skip to
+            the next property change */
+         while (pos == next_invisible && pos < end)
+           {
+             Lisp_Object position, limit, endpos, prop, ww;
+             XFASTINT (position) = pos;
+             XSET (ww, Lisp_Window, w);
+             prop = Fget_char_property (position, Qinvisible, ww);
+             /* This is just an estimate to give reasonable
+                performance; nothing should go wrong if it is too small.  */
+             limit = Fnext_overlay_change (position);
+             if (XFASTINT (limit) > pos + 50)
+               XFASTINT (limit) = pos + 50;
+             endpos = Fnext_single_property_change (position, Qinvisible,
+                                               Fcurrent_buffer (), limit);
+             if (INTEGERP (endpos))
+               next_invisible = XINT (endpos);
+             else
+               next_invisible = end;
+             if (! NILP (prop))
+               {
+                 if (pos < PT && next_invisible >= PT)
+                   {
+                     cursor_vpos = vpos;
+                     cursor_hpos = p1 - leftmargin;
+                   }
+                 pos = next_invisible;
+               }
+           }
+         if (pos >= end)
+           break;
+#endif
+
+#ifdef HAVE_X_WINDOWS
+         /* Did we hit a face change?  Figure out what face we should
+            use now.  We also hit this the first time through the
+            loop, to see what face we should start with.  */
+         if (pos >= next_face_change && FRAME_X_P (f))
+           current_face = compute_char_face (f, w, pos,
+                                             region_beg, region_end,
+                                             &next_face_change, pos + 50, 0);
+#endif
+
          pause = end;
-         if (pos < point && point < pause)
-           pause = point;
+
+#ifdef USE_TEXT_PROPERTIES       
+         if (pos < next_invisible && next_invisible < pause)
+           pause = next_invisible;
+#endif
+         if (pos < next_face_change && next_face_change < pause)
+           pause = next_face_change;
+
+         /* Wouldn't you hate to read the next line to someone over
+             the phone?  */
+         if (pos < PT && PT < pause)
+           pause = PT;
          if (pos < GPT && GPT < pause)
            pause = GPT;
 
@@ -1591,42 +2163,48 @@ display_text_line (w, start, vpos, hpos, taboffset)
       if (c >= 040 && c < 0177
          && (dp == 0 || XTYPE (DISP_CHAR_VECTOR (dp, c)) != Lisp_Vector))
        {
-         if (p1 >= startp)
-           *p1 = c;
+         if (p1 >= leftmargin)
+           *p1 = MAKE_GLYPH (f, c, current_face);
          p1++;
        }
       else if (c == '\n')
        {
          invis = 0;
-         while (pos < end
+         while (pos + 1 < end
                 && selective > 0
-                && position_indentation (pos + 1) >= selective)
+                && indented_beyond_p (pos + 1, selective))
            {
              invis = 1;
              pos = find_next_newline (pos + 1, 1);
              if (FETCH_CHAR (pos - 1) == '\n')
                pos--;
            }
-         if (invis && selective_rlen > 0 && p1 >= startp)
+         if (invis && selective_rlen > 0 && p1 >= leftmargin)
            {
              p1 += selective_rlen;
-             if (p1 - startp > width)
+             if (p1 - leftmargin > width)
                p1 = endp;
-             copy_part_of_rope (p1prev, p1prev,
-                                XVECTOR (DISP_INVIS_VECTOR (dp))->contents,
-                                (p1 - p1prev));
+             copy_part_of_rope (f, p1prev, p1prev, invis_vector_contents,
+                                (p1 - p1prev), current_face);
            }
+#ifdef HAVE_X_WINDOWS
+         /* Draw the face of the newline character as extending all the 
+            way to the end of the frame line.  */
+         if (current_face)
+           while (p1 < endp)
+             *p1++ = FAST_MAKE_GLYPH (' ', current_face);
+#endif
          break;
        }
       else if (c == '\t')
        {
          do
            {
-             if (p1 >= startp && p1 < endp)
-               *p1 = SPACEGLYPH;
+             if (p1 >= leftmargin && p1 < endp)
+               *p1 = MAKE_GLYPH (f, ' ', current_face);
              p1++;
            }
-         while ((p1 - startp + taboffset + hscroll - (hscroll > 0))
+         while ((p1 - leftmargin + taboffset + hscroll - (hscroll > 0))
                 % tab_width);
        }
       else if (c == Ctl ('M') && selective == -1)
@@ -1637,44 +2215,81 @@ display_text_line (w, start, vpos, hpos, taboffset)
          if (selective_rlen > 0)
            {
              p1 += selective_rlen;
-             if (p1 - startp > width)
+             if (p1 - leftmargin > width)
                p1 = endp;
-             copy_part_of_rope (p1prev, p1prev,
-                                XVECTOR(DISP_INVIS_VECTOR (dp))->contents,
-                                (p1 - p1prev));
+             copy_part_of_rope (f, p1prev, p1prev, invis_vector_contents,
+                                (p1 - p1prev), current_face);
            }
+#ifdef HAVE_X_WINDOWS
+         /* Draw the face of the newline character as extending all the 
+            way to the end of the frame line.  */
+         if (current_face)
+           while (p1 < endp)
+             *p1++ = FAST_MAKE_GLYPH (' ', current_face);
+#endif
          break;
        }
       else if (dp != 0 && XTYPE (DISP_CHAR_VECTOR (dp, c)) == Lisp_Vector)
        {
-         p1 = copy_rope (p1, startp, 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 >= startp)
-           *p1 = (dp && XTYPE (DISP_CTRL_GLYPH (dp)) == Lisp_Int
-                  ? XINT (DISP_CTRL_GLYPH (dp)) : '^');
+         if (p1 >= leftmargin)
+           *p1 = fix_glyph (f, (dp && XTYPE (DISP_CTRL_GLYPH (dp)) == Lisp_Int
+                                ? XINT (DISP_CTRL_GLYPH (dp)) : '^'),
+                            current_face);
          p1++;
-         if (p1 >= startp && p1 < endp)
-           *p1 = c ^ 0100;
+         if (p1 >= leftmargin && p1 < endp)
+           *p1 = MAKE_GLYPH (f, c ^ 0100, current_face);
          p1++;
        }
       else
        {
-         if (p1 >= startp)
-           *p1 = (dp && XTYPE (DISP_ESCAPE_GLYPH (dp)) == Lisp_Int
-                  ? XINT (DISP_ESCAPE_GLYPH (dp)) : '\\');
+         if (p1 >= leftmargin)
+           *p1 = fix_glyph (f, (dp && XTYPE (DISP_ESCAPE_GLYPH (dp)) == Lisp_Int
+                                ? XINT (DISP_ESCAPE_GLYPH (dp)) : '\\'),
+                            current_face);
          p1++;
-         if (p1 >= startp && p1 < endp)
-           *p1 = (c >> 6) + '0';
+         if (p1 >= leftmargin && p1 < endp)
+           *p1 = MAKE_GLYPH (f, (c >> 6) + '0', current_face);
          p1++;
-         if (p1 >= startp && p1 < endp)
-           *p1 = (7 & (c >> 3)) + '0';
+         if (p1 >= leftmargin && p1 < endp)
+           *p1 = MAKE_GLYPH (f, (7 & (c >> 3)) + '0', current_face);
          p1++;
-         if (p1 >= startp && p1 < endp)
-           *p1 = (7 & c) + '0';
+         if (p1 >= leftmargin && p1 < endp)
+           *p1 = MAKE_GLYPH (f, (7 & c) + '0', current_face);
          p1++;
        }
+
+      /* Do nothing here for a char that's entirely off the left edge.  */
+      if (p1 >= leftmargin)
+       {
+         /* For all the glyphs occupied by this character, except for the
+            first, store -1 in charstarts.  */
+         if (p1 != p1prev)
+           {
+             int *p2x = &charstart[p1prev - p1start];
+             int *p2 = &charstart[p1 - p1start];
+
+             /* The window's left column should always
+                contain a character position.
+                And don't clobber anything to the left of that.  */
+             if (p1prev < leftmargin)
+               {
+                 p2x = charstart + (leftmargin - p1start);
+                 *p2x = pos;
+               }
+
+             /* This loop skips over the char p2x initially points to.  */
+             while (++p2x < p2)
+               *p2x = -1;
+           }
+       }
+
       pos++;
     }
 
@@ -1686,14 +2301,32 @@ display_text_line (w, start, vpos, hpos, taboffset)
 
   lastpos = pos;
 
+  /* Store 0 in this charstart line for the positions where
+     there is no character.  But do leave what was recorded
+     for the character that ended the line.  */
+  /* Add 1 in the endtest to compensate for the fact that ENDP was
+     made from WIDTH, which is 1 less than the window's actual
+     internal width.  */
+  i = p1 - p1start + 1;
+  if (p1 < leftmargin)
+    i += leftmargin - p1;
+  for (; i < endp - p1start + 1; i++)
+    charstart[i] = 0;
+
   /* Handle continuation in middle of a character */
   /* by backing up over it */
   if (p1 > endp)
     {
-      /* Start the next line with that same character */
-      pos--;
-      /* but at a negative hpos, to skip the columns output on this line.  */
-      val.hpos += p1prev - endp;
+      /* Don't back up if we never actually displayed any text.
+        This occurs when the minibuffer prompt takes up the whole line.  */
+      if (p1prev)
+       {
+         /* Start the next line with that same character */
+         pos--;
+         /* but at negative hpos, to skip the columns output on this line.  */
+         val.hpos += p1prev - endp;
+       }
+
       /* Keep in this line everything up to the continuation column.  */
       p1 = endp;
     }
@@ -1706,25 +2339,35 @@ display_text_line (w, start, vpos, hpos, taboffset)
   if (pos < ZV)
     {
       if (FETCH_CHAR (pos) == '\n')
-       /* If stopped due to a newline, start next line after it */
-       pos++;
+       {
+         /* If stopped due to a newline, start next line after it */
+         pos++;
+         /* Check again for hidden lines, in case the newline occurred exactly
+            at the right margin.  */
+         while (pos < ZV && selective > 0
+                && indented_beyond_p (pos, selective))
+           pos = find_next_newline (pos, 1);
+       }
       else
        /* Stopped due to right margin of window */
        {
          if (truncate)
            {
-             *p1++ = truncator;
+             *p1++ = fix_glyph (f, truncator, 0);
              /* Truncating => start next line after next newline,
                 and point is on this line if it is before the newline,
                 and skip none of first char of next line */
-             pos = find_next_newline (pos, 1);
+             do
+               pos = find_next_newline (pos, 1);
+             while (pos < ZV && selective > 0
+                    && indented_beyond_p (pos, selective));
              val.hpos = XINT (w->hscroll) ? 1 - XINT (w->hscroll) : 0;
 
              lastpos = pos - (FETCH_CHAR (pos - 1) == '\n');
            }
          else
            {
-             *p1++ = continuer;
+             *p1++ = fix_glyph (f, continuer, 0);
              val.vpos = 0;
              lastpos--;
            }
@@ -1734,10 +2377,10 @@ display_text_line (w, start, vpos, hpos, taboffset)
   /* If point is at eol or in invisible text at eol,
      record its frame location now.  */
 
-  if (start <= point && point <= lastpos && cursor_vpos < 0)
+  if (start <= PT && PT <= lastpos && cursor_vpos < 0)
     {
       cursor_vpos = vpos;
-      cursor_hpos = p1 - startp;
+      cursor_hpos = p1 - leftmargin;
     }
 
   if (cursor_vpos == vpos)
@@ -1773,15 +2416,15 @@ display_text_line (w, start, vpos, hpos, taboffset)
   /* If hscroll and line not empty, insert truncation-at-left marker */
   if (hscroll && lastpos != start)
     {
-      *startp = truncator;
-      if (p1 <= startp)
-       p1 = startp + 1;
+      *leftmargin = fix_glyph (f, truncator, 0);
+      if (p1 <= leftmargin)
+       p1 = leftmargin + 1;
     }
 
   if (XFASTINT (w->width) + XFASTINT (w->left) != FRAME_WIDTH (f))
     {
       endp++;
-      if (p1 < startp) p1 = startp;
+      if (p1 < leftmargin) p1 = leftmargin;
       while (p1 < endp) *p1++ = SPACEGLYPH;
 
       /* Don't draw vertical bars if we're using scroll bars.  They're
@@ -1807,14 +2450,17 @@ display_text_line (w, start, vpos, hpos, taboffset)
       unsigned char *p = XSTRING (Voverlay_arrow_string)->data;
       int i;
       int len = XSTRING (Voverlay_arrow_string)->size;
+      int arrow_end;
 
       if (len > width)
        len = width;
       for (i = 0; i < len; i++)
-       startp[i] = p[i];
-      if (desired_glyphs->used[vpos] <
-         (len + startp - desired_glyphs->glyphs[vpos]))
-       desired_glyphs->used[vpos] = len + startp - desired_glyphs->glyphs[vpos];
+       leftmargin[i] = p[i];
+
+      /* Bug in SunOS 4.1.1 compiler requires this intermediate variable.  */
+      arrow_end = (leftmargin - desired_glyphs->glyphs[vpos]) + len;
+      if (desired_glyphs->used[vpos] < arrow_end)
+       desired_glyphs->used[vpos] = arrow_end;
 
       overlay_arrow_seen = 1;
     }
@@ -1824,6 +2470,63 @@ display_text_line (w, start, vpos, hpos, taboffset)
   return &val_display_text_line;
 }
 \f
+/* Redisplay the menu bar in the frame for window W.  */
+
+static void
+display_menu_bar (w)
+     struct window *w;
+{
+  Lisp_Object items, tail;
+  register int vpos = 0;
+  register FRAME_PTR f = XFRAME (WINDOW_FRAME (w));
+  int maxendcol = FRAME_WIDTH (f);
+  int hpos = 0;
+  int i;
+
+#ifndef USE_X_TOOLKIT
+  if (FRAME_MENU_BAR_LINES (f) <= 0)
+    return;
+
+  get_display_line (f, vpos, 0);
+
+  items = FRAME_MENU_BAR_ITEMS (f);
+  for (i = 0; i < XVECTOR (items)->size; i += 3)
+    {
+      Lisp_Object pos, string;
+      string = XVECTOR (items)->contents[i + 1];
+      if (NILP (string))
+       break;
+
+      XFASTINT (XVECTOR (items)->contents[i + 2]) = hpos;
+
+      if (hpos < maxendcol)
+       hpos = display_string (XWINDOW (FRAME_ROOT_WINDOW (f)), vpos,
+                              XSTRING (string)->data,
+                              XSTRING (string)->size,
+                              hpos, 0, 0, hpos, maxendcol);
+      /* Put a gap of 3 spaces between items.  */
+      if (hpos < maxendcol)
+       {
+         int hpos1 = hpos + 3;
+         hpos = display_string (w, vpos, "", 0, hpos, 0, 0,
+                                min (hpos1, maxendcol), maxendcol);
+       }
+    }
+
+  FRAME_DESIRED_GLYPHS (f)->bufp[vpos] = 0;
+  FRAME_DESIRED_GLYPHS (f)->highlight[vpos] = mode_line_inverse_video;
+
+  /* Fill out the line with spaces.  */
+  if (maxendcol > hpos)
+    hpos = display_string (w, vpos, "", 0, hpos, 0, 0, maxendcol, maxendcol);
+
+  /* Clear the rest of the lines allocated to the menu bar.  */
+  vpos++;
+  while (vpos < FRAME_MENU_BAR_LINES (f))
+    get_display_line (f, vpos++, 0);
+#endif /* not USE_X_TOOLKIT */
+}
+\f
 /* Display the mode line for window w */
 
 static void
@@ -1835,6 +2538,8 @@ display_mode_line (w)
   register int right = XFASTINT (w->width) + left;
   register FRAME_PTR f = XFRAME (WINDOW_FRAME (w));
 
+  line_number_displayed = 0;
+
   get_display_line (f, vpos, left);
   display_mode_element (w, vpos, left, 0, right, right,
                        current_buffer->mode_line_format);
@@ -1849,25 +2554,20 @@ display_mode_line (w)
   if (XFASTINT (w->width) == FRAME_WIDTH (f)
       || XFASTINT (XWINDOW (w->parent)->width) == FRAME_WIDTH (f))
     FRAME_DESIRED_GLYPHS (f)->highlight[vpos] = mode_line_inverse_video;
+#ifdef HAVE_X_WINDOWS
+  else if (! FRAME_TERMCAP_P (f))
+    {
+      /* For a partial width window, explicitly set face of each glyph. */
+      int i;
+      GLYPH *ptr = FRAME_DESIRED_GLYPHS (f)->glyphs[vpos];
+      for (i = left; i < right; ++i)
+       ptr[i] = FAST_MAKE_GLYPH (FAST_GLYPH_CHAR (ptr[i]), 1);
+    }
+#endif
 
 #ifdef HAVE_X_WINDOWS
-  /* I'm trying this out because I saw Unimpress use it, but it's
-     possible that this may mess adversely with some window managers.  -jla
-
-     Wouldn't it be nice to use something like mode-line-format to
-     describe frame titles?  -JimB  */
-
-  /* Change the title of the frame to the name of the buffer displayed
-     in the currently selected window.  Don't do this for minibuffer frames,
-     and don't do it when there's only one non-minibuffer frame.  */
-  if (FRAME_X_P (f)
-      && ! FRAME_MINIBUF_ONLY_P (f)
-      && w == XWINDOW (f->selected_window))
-    x_implicitly_set_name (f, (EQ (Fnext_frame (WINDOW_FRAME (w), Qnil),
-                                  WINDOW_FRAME (w))
-                              ? Qnil
-                              : XBUFFER (w->buffer)->name),
-                          Qnil);
+  if (w == XWINDOW (f->selected_window))
+    x_consider_frame_title (WINDOW_FRAME (w));
 #endif
 }
 
@@ -1930,8 +2630,8 @@ display_mode_element (w, vpos, hpos, depth, minendcol, maxendcol, elt)
            if (this - 1 != last)
              {
                register int lim = --this - last + hpos;
-               hpos = display_string (w, vpos, last, hpos, 0, hpos,
-                                      min (lim, maxendcol));
+               hpos = display_string (w, vpos, last, -1, hpos, 0, 1,
+                                      hpos, min (lim, maxendcol));
              }
            else /* c == '%' */
              {
@@ -1959,7 +2659,8 @@ display_mode_element (w, vpos, hpos, depth, minendcol, maxendcol, elt)
                  hpos = display_string (w, vpos,
                                         decode_mode_spec (w, c,
                                                           maxendcol - hpos),
-                                        hpos, 0, spec_width, maxendcol);
+                                        -1,
+                                        hpos, 0, 1, spec_width, maxendcol);
              }
          }
       }
@@ -1980,7 +2681,8 @@ display_mode_element (w, vpos, hpos, depth, minendcol, maxendcol, elt)
               don't check for % within it.  */
            if (XTYPE (tem) == Lisp_String)
              hpos = display_string (w, vpos, XSTRING (tem)->data,
-                                    hpos, 0, minendcol, maxendcol);
+                                    XSTRING (tem)->size,
+                                    hpos, 0, 1, minendcol, maxendcol);
            /* Give up right away for nil or t.  */
            else if (!EQ (tem, elt))
              { elt = tem; goto tail_recurse; }
@@ -2069,13 +2771,13 @@ display_mode_element (w, vpos, hpos, depth, minendcol, maxendcol, elt)
 
     default:
     invalid:
-      return (display_string (w, vpos, "*invalid*", hpos, 0,
+      return (display_string (w, vpos, "*invalid*", -1, hpos, 0, 1,
                              minendcol, maxendcol));
     }
 
  end:
   if (minendcol > hpos)
-    hpos = display_string (w, vpos, "", hpos, 0, minendcol, -1);
+    hpos = display_string (w, vpos, "", 0, hpos, 0, 1, minendcol, maxendcol);
   return hpos;
 }
 \f
@@ -2090,10 +2792,11 @@ decode_mode_spec (w, c, maxwidth)
      register char c;
      register int maxwidth;
 {
-  Lisp_Object obj = Qnil;
+  Lisp_Object obj;
   FRAME_PTR f = XFRAME (WINDOW_FRAME (w));
   char *decode_mode_spec_buf = (char *) FRAME_TEMP_GLYPHS (f)->total_contents;
 
+  obj = Qnil;
   if (maxwidth > FRAME_WIDTH (f))
     maxwidth = FRAME_WIDTH (f);
 
@@ -2127,6 +2830,93 @@ decode_mode_spec (w, c, maxwidth)
 #endif
       break;
 
+    case 'l':
+      {
+       int startpos = marker_position (w->start);
+       int line, linepos, topline;
+       int nlines, junk;
+       Lisp_Object tem;
+       int height = XFASTINT (w->height);
+
+       /* If we decided that this buffer isn't suitable for line numbers, 
+          don't forget that too fast.  */
+       if (EQ (w->base_line_pos, w->buffer))
+         return "??";
+
+       /* If the buffer is very big, don't waste time.  */
+       if (ZV - BEGV > line_number_display_limit)
+         {
+           w->base_line_pos = Qnil;
+           w->base_line_number = Qnil;
+           return "??";
+         }
+
+       if (!NILP (w->base_line_number)
+           && !NILP (w->base_line_pos)
+           && XFASTINT (w->base_line_pos) <= marker_position (w->start))
+         {
+           line = XFASTINT (w->base_line_number);
+           linepos = XFASTINT (w->base_line_pos);
+         }
+       else
+         {
+           line = 1;
+           linepos = BEGV;
+         }
+
+       /* Count lines from base line to window start position.  */
+       nlines = display_count_lines (linepos, startpos, startpos, &junk);
+
+       topline = nlines + line;
+
+       /* Determine a new base line, if the old one is too close
+          or too far away, or if we did not have one.
+          "Too close" means it's plausible a scroll-down would
+          go back past it.  */
+       if (startpos == BEGV)
+         {
+           XFASTINT (w->base_line_number) = topline;
+           XFASTINT (w->base_line_pos) = BEGV;
+         }
+       else if (nlines < height + 25 || nlines > height * 3 + 50
+                || linepos == BEGV)
+         {
+           int limit = BEGV;
+           int position;
+           int distance = (height * 2 + 30) * 200;
+
+           if (startpos - distance > limit)
+             limit = startpos - distance;
+
+           nlines = display_count_lines (startpos, limit,
+                                         -(height * 2 + 30),
+                                         &position);
+           /* If we couldn't find the lines we wanted within 
+              200 chars per line,
+              give up on line numbers for this window.  */
+           if (position == startpos - distance)
+             {
+               w->base_line_pos = w->buffer;
+               w->base_line_number = Qnil;
+               return "??";
+             }
+
+           XFASTINT (w->base_line_number) = topline - nlines;
+           XFASTINT (w->base_line_pos) = position;
+         }
+
+       /* Now count lines from the start pos to point.  */
+       nlines = display_count_lines (startpos, PT, PT, &junk);
+
+       /* Record that we did display the line number.  */
+       line_number_displayed = 1;
+
+       /* Make the string to show.  */
+       sprintf (decode_mode_spec_buf, "%d", topline + nlines);
+       return decode_mode_spec_buf;
+      }
+      break;
+
     case 'm': 
       obj = current_buffer->mode_name;
       break;
@@ -2143,14 +2933,28 @@ decode_mode_spec (w, c, maxwidth)
        return "*";
       return "-";
 
+    case '+':
+      if (MODIFF > current_buffer->save_modified)
+       return "*";
+      return "-";
+
     case 's':
       /* status of process */
       obj = Fget_buffer_process (Fcurrent_buffer ());
       if (NILP (obj))
        return "no process";
+#ifdef subprocesses
       obj = Fsymbol_name (Fprocess_status (obj));
+#endif
       break;
 
+    case 't':                  /* indicate TEXT or BINARY */
+#ifdef MSDOS
+      return NILP (current_buffer->buffer_file_type) ? "T" : "B";
+#else /* not MSDOS */
+      return "T";
+#endif /* not MSDOS */
+
     case 'p':
       {
        int pos = marker_position (w->start);
@@ -2177,6 +2981,35 @@ decode_mode_spec (w, c, maxwidth)
          }
       }
 
+      /* Display percentage of size above the bottom of the screen.  */
+    case 'P':
+      {
+       int toppos = marker_position (w->start);
+       int botpos = Z - XFASTINT (w->window_end_pos);
+       int total = ZV - BEGV;
+
+       if (botpos >= ZV)
+         {
+           if (toppos <= BEGV)
+             return "All";
+           else
+             return "Bottom";
+         }
+       else
+         {
+           total = ((botpos - BEGV) * 100 + total - 1) / total;
+           /* We can't normally display a 3-digit number,
+              so get us a 2-digit number that is close.  */
+           if (total == 100)
+             total = 99;
+           if (toppos <= BEGV)
+             sprintf (decode_mode_spec_buf, "Top%2d%%", total);
+           else
+             sprintf (decode_mode_spec_buf, "%2d%%", total);
+           return decode_mode_spec_buf;
+         }
+      }
+
     case '%':
       return "%";
 
@@ -2230,10 +3063,42 @@ decode_mode_spec (w, c, maxwidth)
   else
     return "";
 }
+
+/* Count up to N lines starting from FROM.
+   But don't go beyond LIMIT.
+   Return the number of lines thus found (always positive).
+   Store the position after what was found into *POS_PTR.  */
+
+static int
+display_count_lines (from, limit, n, pos_ptr)
+     int from, limit, n;
+     int *pos_ptr;
+{
+  int oldbegv = BEGV;
+  int oldzv = ZV;
+  int shortage = 0;
+  
+  if (limit < from)
+    BEGV = limit;
+  else
+    ZV = limit;
+
+  *pos_ptr = scan_buffer ('\n', from, n, &shortage, 0);
+
+  ZV = oldzv;
+  BEGV = oldbegv;
+
+  if (n < 0)
+    /* When scanning backwards, scan_buffer stops *after* the last newline
+       it finds, but does count it.  Compensate for that.  */
+    return - n - shortage - (*pos_ptr != limit);
+  return n - shortage;
+}  
 \f
 /* Display STRING on one line of window W, starting at HPOS.
    Display at position VPOS.  Caller should have done get_display_line.
    If VPOS == -1, display it as the current frame's title.
+   LENGTH is the length of STRING, or -1 meaning STRING is null-terminated.
 
   TRUNCATE is GLYPH to display at end if truncated.  Zero for none.
 
@@ -2244,14 +3109,22 @@ decode_mode_spec (w, c, maxwidth)
   The right edge of W is an implicit maximum.
   If TRUNCATE is nonzero, the implicit maximum is one column before the edge.
 
-  Returns ending hpos */
+  OBEY_WINDOW_WIDTH says to put spaces or vertical bars
+  at the place where the current window ends in this line
+  and not display anything beyond there.  Otherwise, only MAXCOL
+  controls where to stop output.
+
+  Returns ending hpos.  */
 
 static int
-display_string (w, vpos, string, hpos, truncate, mincol, maxcol)
+display_string (w, vpos, string, length, hpos, truncate,
+               obey_window_width, mincol, maxcol)
      struct window *w;
      unsigned char *string;
+     int length;
      int vpos, hpos;
      GLYPH truncate;
+     int obey_window_width;
      int mincol, maxcol;
 {
   register int c;
@@ -2268,39 +3141,58 @@ display_string (w, vpos, string, hpos, truncate, mincol, maxcol)
   /* Use the standard display table, not the window's display table.
      We don't want the mode line in rot13.  */
   register struct Lisp_Vector *dp = 0;
+  int i;
 
   if (XTYPE (Vstandard_display_table) == Lisp_Vector
       && XVECTOR (Vstandard_display_table)->size == DISP_TABLE_SIZE)
     dp = XVECTOR (Vstandard_display_table);
 
-  if (tab_width <= 0 || tab_width > 20) tab_width = 8;
+  if (tab_width <= 0 || tab_width > 1000) tab_width = 8;
 
   p1 = p1start;
   start = desired_glyphs->glyphs[vpos] + XFASTINT (w->left);
-  end = start + window_width - (truncate != 0);
 
-  if ((window_width + XFASTINT (w->left)) != FRAME_WIDTH (f))
+  if (obey_window_width)
     {
-      if (FRAME_HAS_VERTICAL_SCROLL_BARS (f))
+      end = start + window_width - (truncate != 0);
+
+      if ((window_width + XFASTINT (w->left)) != FRAME_WIDTH (f))
        {
-         int i;
+         if (FRAME_HAS_VERTICAL_SCROLL_BARS (f))
+           {
+             int i;
 
-         for (i = 0; i < VERTICAL_SCROLL_BAR_WIDTH; i++)
-           *end-- = ' ';
+             for (i = 0; i < VERTICAL_SCROLL_BAR_WIDTH; i++)
+               *end-- = ' ';
+           }
+         else
+           *end-- = '|';
        }
-      else
-       *end-- = '|';
     }
 
-  if (maxcol >= 0 && end - desired_glyphs->glyphs[vpos] > maxcol)
+  if (! obey_window_width
+      || (maxcol >= 0 && end - desired_glyphs->glyphs[vpos] > maxcol))
     end = desired_glyphs->glyphs[vpos] + maxcol;
+
+  /* Store 0 in charstart for these columns.  */
+  for (i = (hpos >= 0 ? hpos : 0); i < end - p1start + hpos; i++)
+    desired_glyphs->charstarts[vpos][i] = 0;
+
   if (maxcol >= 0 && mincol > maxcol)
     mincol = maxcol;
 
   while (p1 < end)
     {
+      if (length == 0)
+       break;
       c = *string++;
-      if (!c) break;
+      /* Specified length.  */
+      if (length >= 0)
+       length--;
+      /* Unspecified length (null-terminated string).  */
+      else if (c == 0)
+       break;
+
       if (c >= 040 && c < 0177
          && (dp == 0 || XTYPE (DISP_CHAR_VECTOR (dp, c)) != Lisp_Vector))
        {
@@ -2319,12 +3211,18 @@ display_string (w, vpos, string, hpos, truncate, mincol, maxcol)
          while ((p1 - start + hscroll - (hscroll > 0)) % tab_width);
        }
       else if (dp != 0 && XTYPE (DISP_CHAR_VECTOR (dp, c)) == Lisp_Vector)
-        p1 = copy_rope (p1, start, 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)
-           *p1 = (dp && XTYPE (DISP_CTRL_GLYPH (dp)) == Lisp_Int
-                  ? XINT (DISP_CTRL_GLYPH (dp)) : '^');
+           *p1 = fix_glyph (f, (dp && XTYPE (DISP_CTRL_GLYPH (dp)) == Lisp_Int
+                                ? XINT (DISP_CTRL_GLYPH (dp)) : '^'),
+                            0);
          p1++;
          if (p1 >= start && p1 < end)
            *p1 = c ^ 0100;
@@ -2333,8 +3231,9 @@ display_string (w, vpos, string, hpos, truncate, mincol, maxcol)
       else
        {
          if (p1 >= start)
-           *p1 = (dp && XTYPE (DISP_ESCAPE_GLYPH (dp)) == Lisp_Int
-                  ? XINT (DISP_ESCAPE_GLYPH (dp)) : '\\');
+           *p1 = fix_glyph (f, (dp && XTYPE (DISP_ESCAPE_GLYPH (dp)) == Lisp_Int
+                                ? XINT (DISP_ESCAPE_GLYPH (dp)) : '\\'),
+                            0);
          p1++;
          if (p1 >= start && p1 < end)
            *p1 = (c >> 6) + '0';
@@ -2348,10 +3247,10 @@ display_string (w, vpos, string, hpos, truncate, mincol, maxcol)
        }
     }
 
-  if (c)
+  if (c && length > 0)
     {
       p1 = end;
-      if (truncate) *p1++ = truncate;
+      if (truncate) *p1++ = fix_glyph (f, truncate, 0);
     }
   else if (mincol >= 0)
     {
@@ -2374,6 +3273,9 @@ display_string (w, vpos, string, hpos, truncate, mincol, maxcol)
 void
 syms_of_xdisp ()
 {
+  staticpro (&Qmenu_bar_update_hook);
+  Qmenu_bar_update_hook = intern ("menu-bar-update-hook");
+
   staticpro (&last_arrow_position);
   staticpro (&last_arrow_string);
   last_arrow_position = Qnil;
@@ -2408,6 +3310,14 @@ If this is zero, point is always centered after it moves off frame.");
   DEFVAR_BOOL ("mode-line-inverse-video", &mode_line_inverse_video,
     "*Non-nil means use inverse video for the mode line.");
   mode_line_inverse_video = 1;
+
+  DEFVAR_INT ("line-number-display-limit", &line_number_display_limit,
+    "*Maximum buffer size for which line number should be displayed.");
+  line_number_display_limit = 1000000;
+
+  DEFVAR_BOOL ("highlight-nonselected-windows", &highlight_nonselected_windows,
+    "*Non-nil means highlight region even in nonselected windows.");
+  highlight_nonselected_windows = 1;
 }
 
 /* initialize the window system */