Index: src/Makefile.in.in =================================================================== RCS file: /usr/CVSroot/XEmacs/xemacs/src/Makefile.in.in,v retrieving revision 1.81.2.13 diff -u -r1.81.2.13 Makefile.in.in --- src/Makefile.in.in 1999/05/24 00:32:43 1.81.2.13 +++ src/Makefile.in.in 1999/07/11 18:37:24 @@ -176,7 +176,7 @@ event-stream.o extents.o faces.o\ fileio.o $(LOCK_OBJ) filemode.o floatfns.o fns.o font-lock.o\ frame.o general.o glyphs.o glyphs-eimage.o glyphs-widget.o\ - gui.o $(gui_objs) hash.o imgproc.o indent.o insdel.o intl.o\ + gui.o gutter.o $(gui_objs) hash.o imgproc.o indent.o insdel.o intl.o\ keymap.o $(RTC_patch_objs) line-number.o lread.o lstream.o\ macros.o marker.o md5.o minibuf.o objects.o opaque.o\ print.o process.o profile.o\ Index: src/buffer.h =================================================================== RCS file: /usr/CVSroot/XEmacs/xemacs/src/buffer.h,v retrieving revision 1.13.2.3 diff -u -r1.13.2.3 buffer.h --- src/buffer.h 1998/12/29 10:56:49 1.13.2.3 +++ src/buffer.h 1999/07/11 18:37:42 @@ -409,6 +409,9 @@ #define REAL_INC_CHARPTR(ptr) \ ((void) ((ptr) += REP_BYTES_BY_FIRST_BYTE (* (unsigned char *) (ptr)))) +#define REAL_INC_CHARBYTIND(ptr,pos) \ + (pos += REP_BYTES_BY_FIRST_BYTE (* (unsigned char *) (ptr))) + #define REAL_DEC_CHARPTR(ptr) do { \ (ptr)--; \ } while (!VALID_CHARPTR_P (ptr)) @@ -417,6 +420,11 @@ #define INC_CHARPTR(ptr) do { \ ASSERT_VALID_CHARPTR (ptr); \ REAL_INC_CHARPTR (ptr); \ +} while (0) + +#define INC_CHARBYTIND(ptr,pos) do { \ + ASSERT_VALID_CHARPTR (ptr); \ + REAL_INC_CHARBYTIND (ptr,pos); \ } while (0) #define DEC_CHARPTR(ptr) do { \ Index: src/console.h =================================================================== RCS file: /usr/CVSroot/XEmacs/xemacs/src/console.h,v retrieving revision 1.22.2.7 diff -u -r1.22.2.7 console.h --- src/console.h 1999/07/05 07:28:21 1.22.2.7 +++ src/console.h 1999/07/11 18:37:42 @@ -155,6 +155,7 @@ int duration); void (*frame_redraw_cursor_method) (struct frame *f); void (*set_final_cursor_coords_method) (struct frame *, int, int); + void (*bevel_area_method) (struct window *, face_index, int, int, int, int, int); /* color methods */ int (*initialize_color_instance_method) (struct Lisp_Color_Instance *, Index: src/device.h =================================================================== RCS file: /usr/CVSroot/XEmacs/xemacs/src/device.h,v retrieving revision 1.8.2.2 diff -u -r1.8.2.2 device.h --- src/device.h 1998/12/18 05:42:03 1.8.2.2 +++ src/device.h 1999/07/11 18:37:49 @@ -173,6 +173,7 @@ unsigned int modeline_changed :1; unsigned int point_changed :1; unsigned int size_changed :1; + unsigned int gutter_changed :1; unsigned int toolbar_changed :1; unsigned int windows_changed :1; unsigned int windows_structure_changed :1; @@ -349,6 +350,9 @@ #define MARK_DEVICE_TOOLBARS_CHANGED(d) \ ((void) (toolbar_changed = (d)->toolbar_changed = 1)) + +#define MARK_DEVICE_GUTTERS_CHANGED(d) \ + ((void) (gutter_changed = (d)->gutter_changed = 1)) #define MARK_DEVICE_SIZE_CHANGED(d) \ ((void) (size_changed = (d)->size_changed = 1)) Index: src/emacs.c =================================================================== RCS file: /usr/CVSroot/XEmacs/xemacs/src/emacs.c,v retrieving revision 1.82.2.20 diff -u -r1.82.2.20 emacs.c --- src/emacs.c 1999/07/05 07:28:22 1.82.2.20 +++ src/emacs.c 1999/07/11 18:38:07 @@ -943,6 +943,7 @@ #if defined (HAVE_MENUBARS) || defined (HAVE_SCROLLBARS) || defined (HAVE_DIALOGS) || defined (HAVE_TOOLBARS) syms_of_gui (); #endif + syms_of_gutter (); syms_of_indent (); syms_of_intl (); syms_of_keymap (); @@ -1165,6 +1166,7 @@ specifier_type_create (); specifier_type_create_image (); + specifier_type_create_gutter (); specifier_type_create_objects (); #ifdef HAVE_TOOLBARS specifier_type_create_toolbar (); @@ -1338,6 +1340,7 @@ #if defined (HAVE_MENUBARS) || defined (HAVE_SCROLLBARS) || defined (HAVE_DIALOGS) || defined (HAVE_TOOLBARS) vars_of_gui (); #endif + vars_of_gutter (); vars_of_indent (); vars_of_insdel (); vars_of_intl (); @@ -1490,6 +1493,7 @@ */ specifier_vars_of_glyphs (); + specifier_vars_of_gutter (); #ifdef HAVE_MENUBARS specifier_vars_of_menubar (); #endif Index: src/frame.c =================================================================== RCS file: /usr/CVSroot/XEmacs/xemacs/src/frame.c,v retrieving revision 1.37.2.5 diff -u -r1.37.2.5 frame.c --- src/frame.c 1999/06/22 18:02:56 1.37.2.5 +++ src/frame.c 1999/07/11 18:38:29 @@ -34,6 +34,7 @@ #include "faces.h" #include "frame.h" #include "glyphs.h" +#include "gutter.h" #include "menubar.h" #include "redisplay.h" #include "scrollbar.h" @@ -893,7 +894,10 @@ { #ifdef HAVE_TOOLBARS if (!EQ (f->last_nonminibuf_window, window)) - MARK_TOOLBAR_CHANGED; + { + MARK_TOOLBAR_CHANGED; + MARK_GUTTER_CHANGED; + } #endif f->last_nonminibuf_window = window; } @@ -1526,6 +1530,7 @@ #ifdef HAVE_TOOLBARS free_frame_toolbars (f); #endif + free_frame_gutters (f); /* This must be done before the window and window_mirror structures are freed. The scrollbar information is attached to them. */ Index: src/frame.h =================================================================== RCS file: /usr/CVSroot/XEmacs/xemacs/src/frame.h,v retrieving revision 1.18.2.2 diff -u -r1.18.2.2 frame.h --- src/frame.h 1998/12/18 05:42:06 1.18.2.2 +++ src/frame.h 1999/07/11 18:38:34 @@ -33,6 +33,7 @@ #include "device.h" #include "glyphs.h" +#include "redisplay.h" #define FRAME_TYPE_NAME(f) ((f)->framemeths->name) #define FRAME_TYPE(f) ((f)->framemeths->symbol) @@ -108,7 +109,12 @@ in redisplay_frame. */ unsigned int current_toolbar_size[4]; #endif + unsigned int current_gutter_size[4]; + /* Dynamic array of display lines for gutters */ + display_line_dynarr *current_display_lines; + display_line_dynarr *desired_display_lines; + /* A structure of auxiliary data specific to the device type. struct x_frame is used for X window frames; defined in console-x.h */ void *frame_data; @@ -160,6 +166,11 @@ unsigned int bottom_toolbar_was_visible :1; unsigned int left_toolbar_was_visible :1; unsigned int right_toolbar_was_visible :1; + /* gutter visibility */ + unsigned int top_gutter_was_visible :1; + unsigned int bottom_gutter_was_visible :1; + unsigned int left_gutter_was_visible :1; + unsigned int right_gutter_was_visible :1; /* redisplay flags */ unsigned int buffers_changed :1; @@ -175,6 +186,7 @@ unsigned int point_changed :1; unsigned int size_changed :1; unsigned int toolbar_changed :1; + unsigned int gutter_changed :1; unsigned int windows_changed :1; unsigned int windows_structure_changed :1; unsigned int window_face_cache_reset :1; /* used by expose handler */ @@ -340,6 +352,19 @@ } \ else \ toolbar_changed = 1; \ +} while (0) + +#define MARK_FRAME_GUTTERS_CHANGED(f) do { \ + struct frame *mftc_f = (f); \ + mftc_f->gutter_changed = 1; \ + mftc_f->modiff++; \ + if (!NILP (mftc_f->device)) \ + { \ + struct device *mftc_d = XDEVICE (mftc_f->device); \ + MARK_DEVICE_GUTTERS_CHANGED (mftc_d); \ + } \ + else \ + gutter_changed = 1; \ } while (0) #define MARK_FRAME_SIZE_CHANGED(f) do { \ Index: src/general.c =================================================================== RCS file: /usr/CVSroot/XEmacs/xemacs/src/general.c,v retrieving revision 1.13.2.2 diff -u -r1.13.2.2 general.c --- src/general.c 1998/12/18 05:42:07 1.13.2.2 +++ src/general.c 1999/07/11 18:38:34 @@ -89,6 +89,7 @@ Lisp_Object Qgeneric; Lisp_Object Qgeometry; Lisp_Object Qglobal; +Lisp_Object Qgutter; Lisp_Object Qheight; Lisp_Object Qhighlight; Lisp_Object Qicon; @@ -245,6 +246,7 @@ defsymbol (&Qgeneric, "generic"); defsymbol (&Qgeometry, "geometry"); defsymbol (&Qglobal, "global"); + defsymbol (&Qgutter, "gutter"); defsymbol (&Qheight, "height"); defsymbol (&Qhighlight, "highlight"); defsymbol (&Qicon, "icon"); Index: src/glyphs-x.c =================================================================== RCS file: /usr/CVSroot/XEmacs/xemacs/src/glyphs-x.c,v retrieving revision 1.49.2.12 diff -u -r1.49.2.12 glyphs-x.c --- src/glyphs-x.c 1999/07/05 07:28:24 1.49.2.12 +++ src/glyphs-x.c 1999/07/11 18:38:53 @@ -2453,7 +2453,7 @@ INITIALIZE_DEVICE_IIFORMAT (x, subwindow); IIFORMAT_HAS_DEVMETHOD (x, subwindow, instantiate); - +#ifdef LWLIB_USES_MOTIF /* button widget */ INITIALIZE_DEVICE_IIFORMAT (x, button); IIFORMAT_HAS_DEVMETHOD (x, button, property); @@ -2469,10 +2469,12 @@ /* text field */ INITIALIZE_DEVICE_IIFORMAT (x, edit_field); IIFORMAT_HAS_DEVMETHOD (x, edit_field, instantiate); +#if 0 /* XmVERSION > 1*/ /* combo box */ INITIALIZE_DEVICE_IIFORMAT (x, combo_box); IIFORMAT_HAS_DEVMETHOD (x, combo_box, instantiate); - +#endif +#endif INITIALIZE_IMAGE_INSTANTIATOR_FORMAT (cursor_font, "cursor-font"); IIFORMAT_VALID_CONSOLE (x, cursor_font); Index: src/indent.c =================================================================== RCS file: /usr/CVSroot/XEmacs/xemacs/src/indent.c,v retrieving revision 1.9.2.1 diff -u -r1.9.2.1 indent.c --- src/indent.c 1999/06/04 12:53:47 1.9.2.1 +++ src/indent.c 1999/07/11 18:39:02 @@ -193,6 +193,53 @@ } int +string_column_at_point (struct Lisp_String* s, Bufpos init_pos, int tab_width) +{ + int col; + int tab_seen; + int post_tab; + Bufpos pos = init_pos; + Emchar c; + + if (tab_width <= 0 || tab_width > 1000) tab_width = 8; + col = tab_seen = post_tab = 0; + + while (1) + { + if (pos <= 0) + break; + + pos--; + c = string_char (s, pos); + if (c == '\t') + { + if (tab_seen) + col = ((col + tab_width) / tab_width) * tab_width; + + post_tab += col; + col = 0; + tab_seen = 1; + } + else if (c == '\n') + break; + else +#ifdef MULE + col += XCHARSET_COLUMNS (CHAR_CHARSET (c)); +#else + col ++; +#endif /* MULE */ + } + + if (tab_seen) + { + col = ((col + tab_width) / tab_width) * tab_width; + col += post_tab; + } + + return col; +} + +int current_column (struct buffer *buf) { if (buf == last_known_column_buffer Index: src/redisplay-msw.c =================================================================== RCS file: /usr/CVSroot/XEmacs/xemacs/src/redisplay-msw.c,v retrieving revision 1.28.2.4 diff -u -r1.28.2.4 redisplay-msw.c --- src/redisplay-msw.c 1999/06/16 22:55:14 1.28.2.4 +++ src/redisplay-msw.c 1999/07/11 18:39:12 @@ -41,6 +41,7 @@ #include "faces.h" #include "frame.h" #include "glyphs-msw.h" +#include "gutter.h" #include "redisplay.h" #include "sysdep.h" #include "window.h" @@ -75,6 +76,7 @@ face_index findex, int cursor_start, int cursor_width, int cursor_height, int offset_bitmap); +void bevel_modeline (struct window *w, struct display_line *dl); typedef struct textual_run { @@ -965,6 +967,7 @@ redraw anyhow. */ MAYBE_FRAMEMETH (f, redraw_exposed_toolbars, (f, x, y, width, height)); #endif + redraw_exposed_gutters (f, x, y, width, height); if (!f->window_face_cache_reset) { @@ -977,38 +980,36 @@ /***************************************************************************** - mswindows_bevel_modeline + mswindows_bevel_area - Draw a 3d border around the modeline on window W. + Draw a 3d border around the specified area on window W. ****************************************************************************/ static void -mswindows_bevel_modeline (struct window *w, struct display_line *dl) +mswindows_bevel_area (struct window *w, face_index findex, int x, int y, + int width, int height, int shadow_thickness) { struct frame *f = XFRAME (w->frame); - Lisp_Object color; - int shadow_width = MODELINE_SHADOW_THICKNESS (w); - RECT rect = { WINDOW_MODELINE_LEFT (w), - dl->ypos - dl->ascent - shadow_width, - WINDOW_MODELINE_RIGHT (w), - dl->ypos + dl->descent + shadow_width}; UINT edge; - color = WINDOW_FACE_CACHEL_BACKGROUND (w, MODELINE_INDEX); - mswindows_update_dc (FRAME_MSWINDOWS_DC (f), Qnil, Qnil, color, Qnil); - - if (XINT (w->modeline_shadow_thickness) < 0) - shadow_width = -shadow_width; - - if (shadow_width < -1) + if (shadow_thickness < -1) edge = EDGE_SUNKEN; - else if (shadow_width < 0) + else if (shadow_thickness < 0) edge = BDR_SUNKENINNER; - else if (shadow_width == 1) + else if (shadow_thickness == 1) edge = BDR_RAISEDINNER; else edge = EDGE_RAISED; - - DrawEdge (FRAME_MSWINDOWS_DC (f), &rect, edge, BF_RECT); + + if (shadow_thickness < 0) + shadow_thickness = -shadow_thickness; + + { + RECT rect = { x, y, x + width, y + height }; + Lisp_Object color = WINDOW_FACE_CACHEL_BACKGROUND (w, findex); + mswindows_update_dc (FRAME_MSWINDOWS_DC (f), Qnil, Qnil, color, Qnil); + + DrawEdge (FRAME_MSWINDOWS_DC (f), &rect, edge, BF_RECT); + } } @@ -1110,18 +1111,14 @@ rb = Dynarr_atp (rba, start); if (!rb) - { /* Nothing to do so don't do anything. */ return; - } - else - { - findex = rb->findex; - xpos = rb->xpos; - width = 0; - if (rb->type == RUNE_CHAR) - charset = CHAR_CHARSET (rb->object.chr.ch); - } + + findex = rb->findex; + xpos = rb->xpos; + width = 0; + if (rb->type == RUNE_CHAR) + charset = CHAR_CHARSET (rb->object.chr.ch); if (end < 0) end = Dynarr_length (rba); @@ -1295,7 +1292,7 @@ && (f->clear || f->windows_structure_changed || w->shadow_thickness_changed)) - mswindows_bevel_modeline (w, dl); + bevel_modeline (w, dl); Dynarr_free (buf); } @@ -1500,4 +1497,5 @@ CONSOLE_HAS_METHOD (mswindows, output_end); CONSOLE_HAS_METHOD (mswindows, flash); CONSOLE_HAS_METHOD (mswindows, ring_bell); + CONSOLE_HAS_METHOD (mswindows, bevel_area); } Index: src/redisplay-output.c =================================================================== RCS file: /usr/CVSroot/XEmacs/xemacs/src/redisplay-output.c,v retrieving revision 1.11.2.5 diff -u -r1.11.2.5 redisplay-output.c --- src/redisplay-output.c 1999/01/13 03:50:57 1.11.2.5 +++ src/redisplay-output.c 1999/07/11 18:39:19 @@ -592,7 +592,7 @@ cdl->clip != ddl->clip))) { int x, y, width, height; - Lisp_Object face; + face_index findex; must_sync = 1; x = start_pixpos; @@ -601,23 +601,33 @@ height = ddl->ascent + ddl->descent - ddl->clip; if (x < ddl->bounds.left_in) - face = Vleft_margin_face; + { + findex = ddl->left_margin_findex ? + ddl->left_margin_findex + : get_builtin_face_cache_index (w, Vleft_margin_face); + } else if (x < ddl->bounds.right_in) - face = Vdefault_face; + { + /* no check here because DEFAULT_INDEX == 0 anyway */ + findex = ddl->default_findex; + } else if (x < ddl->bounds.right_out) - face = Vright_margin_face; + { + findex = ddl->right_margin_findex ? + ddl->right_margin_findex + : get_builtin_face_cache_index (w, Vright_margin_face); + } else - face = Qnil; + findex = (face_index) -1; - if (!NILP (face)) + if (findex != (face_index) -1) { Lisp_Object window; XSETWINDOW (window, w); /* Clear the empty area. */ - redisplay_clear_region (window, get_builtin_face_cache_index (w, face), - x, y, width, height); + redisplay_clear_region (window, findex, x, y, width, height); /* Mark that we should clear the border. This is necessary because italic fonts may leave @@ -1602,4 +1612,29 @@ #ifdef HAVE_SCROLLBARS update_window_scrollbars (w, NULL, !MINI_WINDOW_P (w), 0); #endif +} + +/***************************************************************************** + bevel_modeline + + Draw a 3d border around the modeline on window W. + ****************************************************************************/ +void +bevel_modeline (struct window *w, struct display_line *dl) +{ + struct frame *f = XFRAME (w->frame); + struct device *d = XDEVICE (f->device); + int x, y, width, height; + int shadow_thickness = MODELINE_SHADOW_THICKNESS (w); + + x = WINDOW_MODELINE_LEFT (w); + width = WINDOW_MODELINE_RIGHT (w) - x; + y = dl->ypos - dl->ascent - shadow_thickness; + height = dl->ascent + dl->descent + 2 * shadow_thickness; + + if (XINT (w->modeline_shadow_thickness) < 0) + shadow_thickness = - shadow_thickness; + + MAYBE_DEVMETH (d, bevel_area, + (w, MODELINE_INDEX, x, y, width, height, shadow_thickness)); } Index: src/redisplay-x.c =================================================================== RCS file: /usr/CVSroot/XEmacs/xemacs/src/redisplay-x.c,v retrieving revision 1.23.2.4 diff -u -r1.23.2.4 redisplay-x.c --- src/redisplay-x.c 1999/06/16 13:23:46 1.23.2.4 +++ src/redisplay-x.c 1999/07/11 18:39:35 @@ -40,6 +40,7 @@ #include "debug.h" #include "faces.h" #include "frame.h" +#include "gutter.h" #include "redisplay.h" #include "sysdep.h" #include "window.h" @@ -78,7 +79,7 @@ int xpos, face_index findex); static void x_clear_frame (struct frame *f); static void x_clear_frame_windows (Lisp_Object window); -static void x_bevel_modeline (struct window *w, struct display_line *dl); +void bevel_modeline (struct window *w, struct display_line *dl); /* Note: We do not use the Xmb*() functions and XFontSets. @@ -334,7 +335,7 @@ int elt = start; face_index findex; - int xpos, width; + int xpos, width = 0; Lisp_Object charset = Qunbound; /* Qnil is a valid charset when MULE is not defined */ @@ -342,19 +343,14 @@ rb = Dynarr_atp (rba, start); if (!rb) - { - /* Nothing to do so don't do anything. */ - return; - } - else - { - findex = rb->findex; - xpos = rb->xpos; - width = 0; - if (rb->type == RUNE_CHAR) - charset = CHAR_CHARSET (rb->object.chr.ch); - } + /* Nothing to do so don't do anything. */ + return; + findex = rb->findex; + xpos = rb->xpos; + if (rb->type == RUNE_CHAR) + charset = CHAR_CHARSET (rb->object.chr.ch); + if (end < 0) end = Dynarr_length (rba); Dynarr_reset (buf); @@ -522,38 +518,40 @@ && (f->clear || f->windows_structure_changed || w->shadow_thickness_changed)) - x_bevel_modeline (w, dl); + bevel_modeline (w, dl); Dynarr_free (buf); } /***************************************************************************** - x_bevel_modeline + x_bevel_area - Draw a 3d border around the modeline on window W. + Draw a shadows for the given area in the given face. ****************************************************************************/ static void -x_bevel_modeline (struct window *w, struct display_line *dl) +x_bevel_area (struct window *w, face_index findex, + int x, int y, int width, int height, + int shadow_thickness) { struct frame *f = XFRAME (w->frame); struct device *d = XDEVICE (f->device); + + EmacsFrame ef = (EmacsFrame) FRAME_X_TEXT_WIDGET (f); Display *dpy = DEVICE_X_DISPLAY (d); Window x_win = XtWindow (FRAME_X_TEXT_WIDGET (f)); - EmacsFrame ef = (EmacsFrame) FRAME_X_TEXT_WIDGET (f); - GC top_shadow_gc, bottom_shadow_gc, background_gc; Pixel top_shadow_pixel, bottom_shadow_pixel, background_pixel; - XColor tmp_color; Lisp_Object tmp_pixel; - int x, y, width, height; + XColor tmp_color; XGCValues gcv; - unsigned long mask; + GC top_shadow_gc, bottom_shadow_gc, background_gc; + int use_pixmap = 0; int flip_gcs = 0; - int shadow_thickness; + unsigned long mask; memset (&gcv, ~0, sizeof (XGCValues)); - tmp_pixel = WINDOW_FACE_CACHEL_BACKGROUND (w, MODELINE_INDEX); + tmp_pixel = WINDOW_FACE_CACHEL_BACKGROUND (w, findex); tmp_color = COLOR_INSTANCE_X_COLOR (XCOLOR_INSTANCE (tmp_pixel)); /* First, get the GC's. */ @@ -564,12 +562,14 @@ x_generate_shadow_pixels (f, &top_shadow_pixel, &bottom_shadow_pixel, background_pixel, ef->core.background_pixel); - tmp_pixel = WINDOW_FACE_CACHEL_FOREGROUND (w, MODELINE_INDEX); + tmp_pixel = WINDOW_FACE_CACHEL_FOREGROUND (w, findex); tmp_color = COLOR_INSTANCE_X_COLOR (XCOLOR_INSTANCE (tmp_pixel)); gcv.background = tmp_color.pixel; gcv.graphics_exposures = False; mask = GCForeground | GCBackground | GCGraphicsExposures; + /* If we can't distinguish one of the shadows (the color is the same as the + background), it's better to use a pixmap to generate a dithered gray. */ if (top_shadow_pixel == background_pixel || bottom_shadow_pixel == background_pixel) use_pixmap = 1; @@ -583,15 +583,16 @@ gray_width, gray_height, 1, 0, 1); } - tmp_pixel = WINDOW_FACE_CACHEL_BACKGROUND (w, MODELINE_INDEX); + tmp_pixel = WINDOW_FACE_CACHEL_BACKGROUND (w, findex); tmp_color = COLOR_INSTANCE_X_COLOR (XCOLOR_INSTANCE (tmp_pixel)); gcv.foreground = tmp_color.pixel; + /* this is needed because the GC draws with a pixmap here */ gcv.fill_style = FillOpaqueStippled; gcv.stipple = DEVICE_X_GRAY_PIXMAP (d); top_shadow_gc = gc_cache_lookup (DEVICE_X_GC_CACHE (d), &gcv, (mask | GCStipple | GCFillStyle)); - tmp_pixel = WINDOW_FACE_CACHEL_FOREGROUND (w, MODELINE_INDEX); + tmp_pixel = WINDOW_FACE_CACHEL_FOREGROUND (w, findex); tmp_color = COLOR_INSTANCE_X_COLOR (XCOLOR_INSTANCE (tmp_pixel)); bottom_shadow_pixel = tmp_color.pixel; @@ -617,23 +618,23 @@ gcv.foreground = background_pixel; background_gc = gc_cache_lookup (DEVICE_X_GC_CACHE (d), &gcv, mask); - if (XINT (w->modeline_shadow_thickness) < 0) + /* possibly revert the GC's in case the shadow thickness is < 0. + This will give a depressed look to the divider */ + if (shadow_thickness < 0) { GC temp; temp = top_shadow_gc; top_shadow_gc = bottom_shadow_gc; bottom_shadow_gc = temp; - } - - shadow_thickness = MODELINE_SHADOW_THICKNESS (w); - x = WINDOW_MODELINE_LEFT (w); - width = WINDOW_MODELINE_RIGHT (w) - x; - y = dl->ypos - dl->ascent - shadow_thickness; - height = dl->ascent + dl->descent + 2 * shadow_thickness; + /* better avoid a Bad Address XLib error ;-) */ + shadow_thickness = - shadow_thickness; + } - x_output_shadows (f, x, y, width, height, top_shadow_gc, bottom_shadow_gc, + /* Draw the shadows around the divider line */ + x_output_shadows (f, x, y, width, height, + top_shadow_gc, bottom_shadow_gc, background_gc, shadow_thickness); } @@ -1397,17 +1398,13 @@ struct frame *f = XFRAME (w->frame); struct device *d = XDEVICE (f->device); - EmacsFrame ef = (EmacsFrame) FRAME_X_TEXT_WIDGET (f); Display *dpy = DEVICE_X_DISPLAY (d); Window x_win = XtWindow (FRAME_X_TEXT_WIDGET (f)); - Pixel top_shadow_pixel, bottom_shadow_pixel, background_pixel; Lisp_Object tmp_pixel; XColor tmp_color; XGCValues gcv; - GC top_shadow_gc, bottom_shadow_gc, background_gc; + GC background_gc; - int use_pixmap = 0; - int flip_gcs = 0; unsigned long mask; int x, y1, y2, width, shadow_thickness, spacing, line_width; face_index div_face = get_builtin_face_cache_index (w, Vvertical_divider_face); @@ -1426,83 +1423,12 @@ tmp_color = COLOR_INSTANCE_X_COLOR (XCOLOR_INSTANCE (tmp_pixel)); /* First, get the GC's. */ - top_shadow_pixel = tmp_color.pixel; - bottom_shadow_pixel = tmp_color.pixel; - background_pixel = tmp_color.pixel; - - x_generate_shadow_pixels (f, &top_shadow_pixel, &bottom_shadow_pixel, - background_pixel, ef->core.background_pixel); - - tmp_pixel = WINDOW_FACE_CACHEL_FOREGROUND (w, div_face); - tmp_color = COLOR_INSTANCE_X_COLOR (XCOLOR_INSTANCE (tmp_pixel)); gcv.background = tmp_color.pixel; + gcv.foreground = tmp_color.pixel; gcv.graphics_exposures = False; mask = GCForeground | GCBackground | GCGraphicsExposures; - - /* If we can't distinguish one of the shadows (the color is the same as the - background), it's better to use a pixmap to generate a dithered gray. */ - if (top_shadow_pixel == background_pixel || - bottom_shadow_pixel == background_pixel) - use_pixmap = 1; - - if (use_pixmap) - { - if (DEVICE_X_GRAY_PIXMAP (d) == None) - { - DEVICE_X_GRAY_PIXMAP (d) = - XCreatePixmapFromBitmapData (dpy, x_win, (char *) gray_bits, - gray_width, gray_height, 1, 0, 1); - } - - tmp_pixel = WINDOW_FACE_CACHEL_BACKGROUND (w, div_face); - tmp_color = COLOR_INSTANCE_X_COLOR (XCOLOR_INSTANCE (tmp_pixel)); - gcv.foreground = tmp_color.pixel; - /* this is needed because the GC draws with a pixmap here */ - gcv.fill_style = FillOpaqueStippled; - gcv.stipple = DEVICE_X_GRAY_PIXMAP (d); - top_shadow_gc = gc_cache_lookup (DEVICE_X_GC_CACHE (d), &gcv, - (mask | GCStipple | GCFillStyle)); - - tmp_pixel = WINDOW_FACE_CACHEL_FOREGROUND (w, div_face); - tmp_color = COLOR_INSTANCE_X_COLOR (XCOLOR_INSTANCE (tmp_pixel)); - bottom_shadow_pixel = tmp_color.pixel; - - flip_gcs = (bottom_shadow_pixel == - WhitePixelOfScreen (DefaultScreenOfDisplay (dpy))); - } - else - { - gcv.foreground = top_shadow_pixel; - top_shadow_gc = gc_cache_lookup (DEVICE_X_GC_CACHE (d), &gcv, mask); - } - - gcv.foreground = bottom_shadow_pixel; - bottom_shadow_gc = gc_cache_lookup (DEVICE_X_GC_CACHE (d), &gcv, mask); - - if (use_pixmap && flip_gcs) - { - GC tmp_gc = bottom_shadow_gc; - bottom_shadow_gc = top_shadow_gc; - top_shadow_gc = tmp_gc; - } - - gcv.foreground = background_pixel; background_gc = gc_cache_lookup (DEVICE_X_GC_CACHE (d), &gcv, mask); - /* possibly revert the GC's in case the shadow thickness is < 0. - This will give a depressed look to the divider */ - if (shadow_thickness < 0) - { - GC temp; - - temp = top_shadow_gc; - top_shadow_gc = bottom_shadow_gc; - bottom_shadow_gc = temp; - - /* better avoid a Bad Address XLib error ;-) */ - shadow_thickness = - shadow_thickness; - } - /* Clear the divider area first. This needs to be done when a window split occurs. */ if (clear) @@ -1514,10 +1440,9 @@ line_width, y2 - y1); /* Draw the shadows around the divider line */ - x_output_shadows (f, x + spacing, y1, - width - 2 * spacing, y2 - y1, - top_shadow_gc, bottom_shadow_gc, - background_gc, shadow_thickness); + x_bevel_area (w, div_face, x + spacing, y1, + width - 2 * spacing, y2 - y1, + shadow_thickness); } /***************************************************************************** @@ -1979,6 +1904,7 @@ redraw anyhow. */ MAYBE_FRAMEMETH (f, redraw_exposed_toolbars, (f, x, y, width, height)); #endif + redraw_exposed_gutters (f, x, y, width, height); if (!f->window_face_cache_reset) { @@ -2274,4 +2200,5 @@ CONSOLE_HAS_METHOD (x, output_end); CONSOLE_HAS_METHOD (x, flash); CONSOLE_HAS_METHOD (x, ring_bell); + CONSOLE_HAS_METHOD (x, bevel_area); } Index: src/redisplay.c =================================================================== RCS file: /usr/CVSroot/XEmacs/xemacs/src/redisplay.c,v retrieving revision 1.55.2.7 diff -u -r1.55.2.7 redisplay.c --- src/redisplay.c 1999/05/12 14:36:15 1.55.2.7 +++ src/redisplay.c 1999/07/11 18:40:13 @@ -51,6 +51,7 @@ #include "faces.h" #include "frame.h" #include "glyphs.h" +#include "gutter.h" #include "insdel.h" #include "menubar.h" #include "objects.h" @@ -271,6 +272,9 @@ static void update_line_start_cache (struct window *w, Bufpos from, Bufpos to, Bufpos point, int no_regen); static int point_visible (struct window *w, Bufpos point, int type); +extern Bytind bi_find_next_emchar_in_string (struct Lisp_String* str, Emchar target, + Bytind st, EMACS_INT count); +extern int string_column_at_point (struct Lisp_String* s, Bufpos init_pos, int tab_width); /* This used to be 10 but 30 seems to give much better performance. */ #define INIT_MAX_PREEMPTS 30 @@ -401,6 +405,10 @@ int toolbar_changed; int toolbar_changed_set; +/* non-nil if any gutter has changed */ +int gutter_changed; +int gutter_changed_set; + /* non-nil if any window has changed since the last time redisplay completed */ int windows_changed; @@ -4178,90 +4186,918 @@ /***************************************************************************/ -/* */ -/* window-regeneration routines */ -/* */ +/* */ +/* displayable string routines */ +/* */ /***************************************************************************/ -/* For a given window and starting position in the buffer it contains, - ensure that the TYPE display lines accurately represent the - presentation of the window. We pass the buffer instead of getting - it from the window since redisplay_window may have temporarily - changed it to the echo area buffer. */ +/* Given a position for a string in a window, ensure that the given + display line DL accurately represents the text on a line starting + at the given position. -static void -regenerate_window (struct window *w, Bufpos start_pos, Bufpos point, int type) + Yes, this is duplicating the code of create_text_block, but it + looked just too hard to change create_text_block to handle strings + *and* buffers. We already make a distinction between the two + elsewhere in the code so I think unifying them would require a + complete MULE rewrite. Besides, the other distinction is that these + functions cover text that the user *cannot edit* so we can remove + everything to do with cursors, minibuffers etc. Eventually the + modeline routines should be modified to use this code as it copes + with many more types of display situation. */ + +static Bufpos +create_string_text_block (struct window *w, Lisp_Object disp_string, + struct display_line *dl, + Bufpos start_pos, + prop_block_dynarr **prop, + face_index default_face) { struct frame *f = XFRAME (w->frame); + /* Note that a lot of the buffer controlled stuff has been left in + because you might well want to make use of it (selective display + etc), its just the buffer text that we do not use. */ struct buffer *b = XBUFFER (w->buffer); - int ypos = WINDOW_TEXT_TOP (w); - int yend; /* set farther down */ + struct device *d = XDEVICE (f->device); + struct Lisp_String* s = XSTRING (disp_string); - prop_block_dynarr *prop; - layout_bounds bounds; - display_line_dynarr *dla; - int need_modeline; + /* we're working with these a lot so precalculate them */ + Bytecount slen = XSTRING_LENGTH (disp_string); + Bytecount bi_string_zv = slen - 1; + Bytind bi_start_pos = charcount_to_bytecount (string_data (s), start_pos); - /* The lines had better exist by this point. */ - if (!(dla = window_display_lines (w, type))) - abort (); - Dynarr_reset (dla); - w->max_line_len = 0; + pos_data data; - /* Normally these get updated in redisplay_window but it is possible - for this function to get called from some other points where that - update may not have occurred. This acts as a safety check. */ - if (!Dynarr_length (w->face_cachels)) - reset_face_cachels (w); - if (!Dynarr_length (w->glyph_cachels)) - reset_glyph_cachels (w); + int truncate_win = window_truncation_on (w); + int end_glyph_width; - Fset_marker (w->start[type], make_int (start_pos), w->buffer); - Fset_marker (w->pointm[type], make_int (point), w->buffer); - w->last_point_x[type] = -1; - w->last_point_y[type] = -1; + /* we're going to ditch selective display for static text, its an + FSF thing and invisble extents are the way to go + here. Implementing it also relies on a number of buffer-specific + functions that we don't have the luxury of being able to use + here. */ - /* Make sure a modeline is in the structs if needed. */ - need_modeline = ensure_modeline_generated (w, type); + /* The variable ctl-arrow allows the user to specify what characters + can actually be displayed and which octal should be used for. + #### This variable should probably have some rethought done to + it. - /* Wait until here to set this so that the structs have a modeline - generated in the case where one didn't exist. */ - yend = WINDOW_TEXT_BOTTOM (w); + #### It would also be really nice if you could specify that + the characters come out in hex instead of in octal. Mule + does that by adding a ctl-hexa variable similar to ctl-arrow, + but that's bogus -- we need a more general solution. I + think you need to extend the concept of display tables + into a more general conversion mechanism. Ideally you + could specify a Lisp function that converts characters, + but this violates the Second Golden Rule and besides would + make things way way way way slow. - bounds = calculate_display_line_boundaries (w, 0); + So instead, we extend the display-table concept, which was + historically limited to 256-byte vectors, to one of the + following: - /* 97/3/14 jhod: stuff added here to support pre-prompts (used for input systems) */ - if (MINI_WINDOW_P (w) - && (!NILP (Vminibuf_prompt) || !NILP (Vminibuf_preprompt)) - && !echo_area_active (f) - && start_pos == BUF_BEGV (b)) - { - struct prop_block pb; - Lisp_Object string; - prop = Dynarr_new (prop_block); + a) A 256-entry vector, for backward compatibility; + b) char-table, mapping characters to values; + c) range-table, mapping ranges of characters to values; + d) a list of the above. - string = concat2(Vminibuf_preprompt, Vminibuf_prompt); - pb.type = PROP_MINIBUF_PROMPT; - pb.data.p_string.str = XSTRING_DATA(string); - pb.data.p_string.len = XSTRING_LENGTH(string); - Dynarr_add (prop, pb); + The (d) option allows you to specify multiple display tables + instead of just one. Each display table can specify conversions + for some characters and leave others unchanged. The way the + character gets displayed is determined by the first display table + with a binding for that character. This way, you could call a + function `enable-hex-display' that adds a hex display-table to + the list of display tables for the current buffer. + + #### ...not yet implemented... Also, we extend the concept of + "mapping" to include a printf-like spec. Thus you can make all + extended characters show up as hex with a display table like + this: + + #s(range-table data ((256 524288) (format "%x"))) + + Since more than one display table is possible, you have + great flexibility in mapping ranges of characters. */ + Emchar printable_min = (CHAR_OR_CHAR_INTP (b->ctl_arrow) + ? XCHAR_OR_CHAR_INT (b->ctl_arrow) + : ((EQ (b->ctl_arrow, Qt) || EQ (b->ctl_arrow, Qnil)) + ? 255 : 160)); + + Lisp_Object face_dt, window_dt; + + /* The text display block for this display line. */ + struct display_block *db = get_display_block_from_line (dl, TEXT); + + /* The first time through the main loop we need to force the glyph + data to be updated. */ + int initial = 1; + + /* Apparently the new extent_fragment_update returns an end position + equal to the position passed in if there are no more runs to be + displayed. */ + int no_more_frags = 0; + + dl->used_prop_data = 0; + dl->num_chars = 0; + + /* set up faces to use for clearing areas, used by + output_display_line */ + dl->default_findex = default_face; + if (default_face) + { + dl->left_margin_findex = default_face; + dl->right_margin_findex = default_face; } else - prop = 0; + { + dl->left_margin_findex = + get_builtin_face_cache_index (w, Vleft_margin_face); + dl->right_margin_findex = + get_builtin_face_cache_index (w, Vright_margin_face); + } - while (ypos < yend) + xzero (data); + data.ef = extent_fragment_new (disp_string, f); + + /* These values are used by all of the rune addition routines. We add + them to this structure for ease of passing. */ + data.d = d; + XSETWINDOW (data.window, w); + data.db = db; + data.dl = dl; + + data.bi_bufpos = bi_start_pos; + data.pixpos = dl->bounds.left_in; + data.last_charset = Qunbound; + data.last_findex = default_face; + data.result_str = Qnil; + + /* Set the right boundary adjusting it to take into account any end + glyph. Save the width of the end glyph for later use. */ + data.max_pixpos = dl->bounds.right_in; + if (truncate_win) + end_glyph_width = GLYPH_CACHEL_WIDTH (w, TRUN_GLYPH_INDEX); + else + end_glyph_width = GLYPH_CACHEL_WIDTH (w, CONT_GLYPH_INDEX); + data.max_pixpos -= end_glyph_width; + + data.cursor_type = NO_CURSOR; + data.cursor_x = -1; + + data.start_col = 0; + /* I don't think we want this, string areas should not scroll with + the window + data.start_col = w->hscroll; + data.bi_start_col_enabled = (w->hscroll ? bi_start_pos : 0); + */ + data.bi_start_col_enabled = 0; + data.hscroll_glyph_width_adjust = 0; + + /* We regenerate the line from the very beginning. */ + Dynarr_reset (db->runes); + + /* Why is this less than or equal and not just less than? If the + starting position is already equal to the maximum we can't add + anything else, right? Wrong. We might still have a newline to + add. A newline can use the room allocated for an end glyph since + if we add it we know we aren't going to be adding any end + glyph. */ + + /* #### Chuck -- I think this condition should be while (1). + Otherwise if (e.g.) there is one begin-glyph and one end-glyph + and the begin-glyph ends exactly at the end of the window, the + end-glyph and text might not be displayed. while (1) ensures + that the loop terminates only when either (a) there is + propagation data or (b) the end-of-line or end-of-buffer is hit. + + #### Also I think you need to ensure that the operation + "add begin glyphs; add end glyphs; add text" is atomic and + can't get interrupted in the middle. If you run off the end + of the line during that operation, then you keep accumulating + propagation data until you're done. Otherwise, if the (e.g.) + there's a begin glyph at a particular position and attempting + to display that glyph results in window-end being hit and + propagation data being generated, then the character at that + position won't be displayed. + + #### See also the comment after the end of this loop, below. + */ + while (data.pixpos <= data.max_pixpos) { - struct display_line dl; - struct display_line *dlp; - int local; + /* #### This check probably should not be necessary. */ + if (data.bi_bufpos > bi_string_zv) + { + /* #### urk! More of this lossage! */ + data.bi_bufpos--; + goto done; + } - if (Dynarr_length (dla) < Dynarr_largest (dla)) + /* Check for face changes. */ + if (initial || (!no_more_frags && data.bi_bufpos == data.ef->end)) { - dlp = Dynarr_atp (dla, Dynarr_length (dla)); - local = 0; + /* Now compute the face and begin/end-glyph information. */ + data.findex = + /* Remember that the extent-fragment routines deal in Bytind's. */ + extent_fragment_update (w, data.ef, data.bi_bufpos); + /* This is somewhat cheesy but the alternative is to + propagate default_face into extent_fragment_update. */ + if (data.findex == DEFAULT_INDEX) + data.findex = default_face; + + get_display_tables (w, data.findex, &face_dt, &window_dt); + + if (data.bi_bufpos == data.ef->end) + no_more_frags = 1; } - else + initial = 0; + + /* Determine what is next to be displayed. We first handle any + glyphs returned by glyphs_at_bufpos. If there are no glyphs to + display then we determine what to do based on the character at the + current buffer position. */ + + /* If the current position is covered by an invisible extent, do + nothing (except maybe add some ellipses). + + #### The behavior of begin and end-glyphs at the edge of an + invisible extent should be investigated further. This is + fairly low priority though. */ + if (data.ef->invisible) { + /* #### Chuck, perhaps you could look at this code? I don't + really know what I'm doing. */ + if (*prop) + { + Dynarr_free (*prop); + *prop = 0; + } + + /* The extent fragment code only sets this when we should + really display the ellipses. It makes sure the ellipses + don't get displayed more than once in a row. */ + if (data.ef->invisible_ellipses) + { + struct glyph_block gb; + + data.ef->invisible_ellipses_already_displayed = 1; + data.ef->invisible_ellipses = 0; + gb.extent = Qnil; + gb.glyph = Vinvisible_text_glyph; + *prop = add_glyph_rune (&data, &gb, BEGIN_GLYPHS, 0, + GLYPH_CACHEL (w, INVIS_GLYPH_INDEX)); + /* Perhaps they shouldn't propagate if the very next thing + is to display a newline (for compatibility with + selective-display-ellipses)? Maybe that's too + abstruse. */ + if (*prop) + goto done; + } + + /* #### What if we we're dealing with a display table? */ + if (data.start_col) + data.start_col--; + + if (data.bi_bufpos == bi_string_zv) + goto done; + else + INC_CHARBYTIND (string_data (s), data.bi_bufpos); + } + + /* If there is propagation data, then it represents the current + buffer position being displayed. Add them and advance the + position counter. This might also add the minibuffer + prompt. */ + else if (*prop) + { + dl->used_prop_data = 1; + *prop = add_propagation_runes (prop, &data); + + if (*prop) + goto done; /* gee, a really narrow window */ + else if (data.bi_bufpos == bi_string_zv) + goto done; + else if (data.bi_bufpos < 0) + /* #### urk urk urk! Aborts are not very fun! Fix this please! */ + data.bi_bufpos = 0; + else + INC_CHARBYTIND (string_data (s), data.bi_bufpos); + } + + /* If there are end glyphs, add them to the line. These are + the end glyphs for the previous run of text. We add them + here rather than doing them at the end of handling the + previous run so that glyphs at the beginning and end of + a line are handled correctly. */ + else if (Dynarr_length (data.ef->end_glyphs) > 0) + { + *prop = add_glyph_runes (&data, END_GLYPHS); + if (*prop) + goto done; + } + + /* If there are begin glyphs, add them to the line. */ + else if (Dynarr_length (data.ef->begin_glyphs) > 0) + { + *prop = add_glyph_runes (&data, BEGIN_GLYPHS); + if (*prop) + goto done; + } + + /* If at end-of-buffer, we've already processed begin and + end-glyphs at this point and there's no text to process, + so we're done. */ + else if (data.bi_bufpos == bi_string_zv) + goto done; + + else + { + Lisp_Object entry = Qnil; + /* Get the character at the current buffer position. */ + data.ch = string_char (s, data.bi_bufpos); + if (!NILP (face_dt) || !NILP (window_dt)) + entry = display_table_entry (data.ch, face_dt, window_dt); + + /* If there is a display table entry for it, hand it off to + add_disp_table_entry_runes and let it worry about it. */ + if (!NILP (entry) && !EQ (entry, make_char (data.ch))) + { + *prop = add_disp_table_entry_runes (&data, entry); + + if (*prop) + goto done; + } + + /* Check if we have hit a newline character. If so, add a marker + to the line and end this loop. */ + else if (data.ch == '\n') + { + /* We aren't going to be adding an end glyph so give its + space back in order to make sure that the cursor can + fit. */ + data.max_pixpos += end_glyph_width; + goto done; + } + + /* If the current character is considered to be printable, then + just add it. */ + else if (data.ch >= printable_min) + { + *prop = add_emchar_rune (&data); + if (*prop) + goto done; + } + + /* If the current character is a tab, determine the next tab + starting position and add a blank rune which extends from the + current pixel position to that starting position. */ + else if (data.ch == '\t') + { + int tab_start_pixpos = data.pixpos; + int next_tab_start; + int char_tab_width; + int prop_width = 0; + + if (data.start_col > 1) + tab_start_pixpos -= (space_width (w) * (data.start_col - 1)); + + next_tab_start = + next_tab_position (w, tab_start_pixpos, + dl->bounds.left_in + + data.hscroll_glyph_width_adjust); + if (next_tab_start > data.max_pixpos) + { + prop_width = next_tab_start - data.max_pixpos; + next_tab_start = data.max_pixpos; + } + data.blank_width = next_tab_start - data.pixpos; + char_tab_width = + (next_tab_start - tab_start_pixpos) / space_width (w); + + *prop = add_blank_rune (&data, w, char_tab_width); + + /* add_blank_rune is only supposed to be called with + sizes guaranteed to fit in the available space. */ + assert (!(*prop)); + + if (prop_width) + { + struct prop_block pb; + *prop = Dynarr_new (prop_block); + + pb.type = PROP_BLANK; + pb.data.p_blank.width = prop_width; + pb.data.p_blank.findex = data.findex; + Dynarr_add (*prop, pb); + + goto done; + } + } + + /* If character is a control character, pass it off to + add_control_char_runes. + + The is_*() routines have undefined results on + arguments outside of the range [-1, 255]. (This + often bites people who carelessly use `char' instead + of `unsigned char'.) + */ + else if (data.ch < 0x100 && iscntrl ((Bufbyte) data.ch)) + { + *prop = add_control_char_runes (&data, b); + + if (*prop) + goto done; + } + + /* If the character is above the ASCII range and we have not + already handled it, then print it as an octal number. */ + else if (data.ch >= 0200) + { + *prop = add_octal_runes (&data); + + if (*prop) + goto done; + } + + /* Assume the current character is considered to be printable, + then just add it. */ + else + { + *prop = add_emchar_rune (&data); + if (*prop) + goto done; + } + + INC_CHARBYTIND (string_data (s), data.bi_bufpos); + } + } + +done: + + /* Determine the starting point of the next line if we did not hit the + end of the buffer. */ + if (data.bi_bufpos < bi_string_zv) + { + /* #### This check is not correct. If the line terminated + due to a begin-glyph or end-glyph hitting window-end, then + data.ch will not point to the character at data.bi_bufpos. If + you make the two changes mentioned at the top of this loop, + you should be able to say '(if (*prop))'. That should also + make it possible to eliminate the data.bi_bufpos < BI_BUF_ZV (b) + check. */ + + /* The common case is that the line ended because we hit a newline. + In that case, the next character is just the next buffer + position. */ + if (data.ch == '\n') + { + INC_CHARBYTIND (string_data (s), data.bi_bufpos); + } + + /* Otherwise we have a buffer line which cannot fit on one display + line. */ + else + { + struct glyph_block gb; + struct glyph_cachel *cachel; + + /* If the line is to be truncated then we actually have to look + for the next newline. We also add the end-of-line glyph which + we know will fit because we adjusted the right border before + we starting laying out the line. */ + data.max_pixpos += end_glyph_width; + data.findex = default_face; + gb.extent = Qnil; + + if (truncate_win) + { + Bytind bi_pos; + + /* Now find the start of the next line. */ + bi_pos = bi_find_next_emchar_in_string (s, '\n', data.bi_bufpos, 1); + + data.cursor_type = NO_CURSOR; + data.bi_bufpos = bi_pos; + gb.glyph = Vtruncation_glyph; + cachel = GLYPH_CACHEL (w, TRUN_GLYPH_INDEX); + } + else + { + /* The cursor can never be on the continuation glyph. */ + data.cursor_type = NO_CURSOR; + + /* data.bi_bufpos is already at the start of the next line. */ + + gb.glyph = Vcontinuation_glyph; + cachel = GLYPH_CACHEL (w, CONT_GLYPH_INDEX); + } + + add_glyph_rune (&data, &gb, BEGIN_GLYPHS, 1, cachel); + + if (truncate_win && data.bi_bufpos == bi_string_zv) + { + CONST Bufbyte* endb = charptr_n_addr (string_data (s), bi_string_zv); + DEC_CHARPTR (endb); + if (charptr_emchar (endb) != '\n') + { + /* #### Damn this losing shit. */ + data.bi_bufpos++; + } + } + } + } + else if (data.bi_bufpos == bi_string_zv) + { + /* We need to add a marker to the end of the line since there is no + newline character in order for the cursor to get drawn. We label + it as a newline so that it gets handled correctly by the + whitespace routines below. */ + + data.ch = '\n'; + data.blank_width = DEVMETH (d, eol_cursor_width, ()); + data.findex = default_face; + data.start_col = 0; + data.bi_start_col_enabled = 0; + + data.max_pixpos += data.blank_width; + add_emchar_rune (&data); + data.max_pixpos -= data.blank_width; + + /* #### urk! Chuck, this shit is bad news. Going around + manipulating invalid positions is guaranteed to result in + trouble sooner or later. */ + data.bi_bufpos = bi_string_zv + 1; + } + + /* Calculate left whitespace boundary. */ + { + int elt = 0; + + /* Whitespace past a newline is considered right whitespace. */ + while (elt < Dynarr_length (db->runes)) + { + struct rune *rb = Dynarr_atp (db->runes, elt); + + if ((rb->type == RUNE_CHAR && rb->object.chr.ch == ' ') + || rb->type == RUNE_BLANK) + { + dl->bounds.left_white += rb->width; + elt++; + } + else + elt = Dynarr_length (db->runes); + } + } + + /* Calculate right whitespace boundary. */ + { + int elt = Dynarr_length (db->runes) - 1; + int done = 0; + + while (!done && elt >= 0) + { + struct rune *rb = Dynarr_atp (db->runes, elt); + + if (!(rb->type == RUNE_CHAR && rb->object.chr.ch < 0x100 + && isspace (rb->object.chr.ch)) + && !rb->type == RUNE_BLANK) + { + dl->bounds.right_white = rb->xpos + rb->width; + done = 1; + } + + elt--; + + } + + /* The line is blank so everything is considered to be right + whitespace. */ + if (!done) + dl->bounds.right_white = dl->bounds.left_in; + } + + /* Set the display blocks bounds. */ + db->start_pos = dl->bounds.left_in; + if (Dynarr_length (db->runes)) + { + struct rune *rb = Dynarr_atp (db->runes, Dynarr_length (db->runes) - 1); + + db->end_pos = rb->xpos + rb->width; + } + else + db->end_pos = dl->bounds.right_white; + + /* update line height parameters */ + if (!data.new_ascent && !data.new_descent) + { + /* We've got a blank line so initialize these values from the default + face. */ + default_face_font_info (data.window, &data.new_ascent, + &data.new_descent, 0, 0, 0); + } + + if (data.max_pixmap_height) + { + int height = data.new_ascent + data.new_descent; + int pix_ascent, pix_descent; + + pix_descent = data.max_pixmap_height * data.new_descent / height; + pix_ascent = data.max_pixmap_height - pix_descent; + + data.new_ascent = max (data.new_ascent, pix_ascent); + data.new_descent = max (data.new_descent, pix_descent); + } + + dl->ascent = data.new_ascent; + dl->descent = data.new_descent; + + { + unsigned short ascent = (unsigned short) XINT (w->minimum_line_ascent); + + if (dl->ascent < ascent) + dl->ascent = ascent; + } + { + unsigned short descent = (unsigned short) XINT (w->minimum_line_descent); + + if (dl->descent < descent) + dl->descent = descent; + } + + dl->cursor_elt = data.cursor_x; + /* #### lossage lossage lossage! Fix this shit! */ + if (data.bi_bufpos > bi_string_zv) + dl->end_bufpos = buffer_or_string_bytind_to_bufpos (disp_string, bi_string_zv); + else + dl->end_bufpos = buffer_or_string_bytind_to_bufpos (disp_string, data.bi_bufpos) - 1; + if (truncate_win) + data.dl->num_chars = + string_column_at_point (s, dl->end_bufpos, XINT (b->tab_width)); + else + /* This doesn't correctly take into account tabs and control + characters but if the window isn't being truncated then this + value isn't going to end up being used anyhow. */ + data.dl->num_chars = dl->end_bufpos - dl->bufpos; + + /* #### handle horizontally scrolled line with text none of which + was actually laid out. */ + + /* #### handle any remainder of overlay arrow */ + + if (*prop == ADD_FAILED) + *prop = NULL; + + if (truncate_win && *prop) + { + Dynarr_free (*prop); + *prop = NULL; + } + + extent_fragment_delete (data.ef); + + /* #### If we started at EOB, then make sure we return a value past + it so that regenerate_window will exit properly. This is bogus. + The main loop should get fixed so that it isn't necessary to call + this function if we are already at EOB. */ + + if (data.bi_bufpos == bi_string_zv && bi_start_pos == bi_string_zv) + return bytecount_to_charcount (string_data (s), data.bi_bufpos) + 1; /* Yuck! */ + else + return bytecount_to_charcount (string_data (s), data.bi_bufpos); +} + +/* Given a display line and a starting position, ensure that the + contents of the display line accurately represent the visual + representation of the buffer contents starting from the given + position when displayed in the given window. The display line ends + when the contents of the line reach the right boundary of the given + window. + + This is very similar to generate_display_line but with the same + limitations as create_string_text_block. I have taken the liberty + of fixing the bytind stuff though.*/ + +static Bufpos +generate_string_display_line (struct window *w, Lisp_Object disp_string, + struct display_line *dl, + Bufpos start_pos, + prop_block_dynarr **prop, + face_index default_face) +{ + Bufpos ret_bufpos; + + /* you must set bounds before calling this. */ + + /* Reset what this line is using. */ + if (dl->display_blocks) + Dynarr_reset (dl->display_blocks); + if (dl->left_glyphs) + { + Dynarr_free (dl->left_glyphs); + dl->left_glyphs = 0; + } + if (dl->right_glyphs) + { + Dynarr_free (dl->right_glyphs); + dl->right_glyphs = 0; + } + + /* We aren't generating a modeline at the moment. */ + dl->modeline = 0; + + /* Create a display block for the text region of the line. */ + ret_bufpos = create_string_text_block (w, disp_string, dl, start_pos, + prop, default_face); + dl->bufpos = start_pos; + if (dl->end_bufpos < dl->bufpos) + dl->end_bufpos = dl->bufpos; + + /* If there are left glyphs associated with any character in the + text block, then create a display block to handle them. */ + if (dl->left_glyphs != NULL && Dynarr_length (dl->left_glyphs)) + create_left_glyph_block (w, dl, 0); + + /* If there are right glyphs associated with any character in the + text block, then create a display block to handle them. */ + if (dl->right_glyphs != NULL && Dynarr_length (dl->right_glyphs)) + create_right_glyph_block (w, dl); + + return ret_bufpos; +} + +/* This is ripped off from regenerate_window. All we want to do is + loop through elements in the string creating display lines until we + have covered the provided area. Simple really. */ +void +generate_displayable_area (struct window *w, Lisp_Object disp_string, + int xpos, int ypos, int width, int height, + display_line_dynarr* dla, + Bufpos start_pos, + face_index default_face) +{ + int yend = ypos + height; + + prop_block_dynarr *prop = 0; + layout_bounds bounds; + assert (dla); + + Dynarr_reset (dla); + /* if there's nothing to do then do nothing. code after this assumes + there is something to do. */ + if (NILP (disp_string)) + return; + + bounds.left_out = xpos; + bounds.right_out = xpos + width; + /* The inner boundaries mark where the glyph margins are located. */ + bounds.left_in = bounds.left_out + window_left_margin_width (w); + bounds.right_in = bounds.right_out - window_right_margin_width (w); + /* We cannot fully calculate the whitespace boundaries as they + depend on the contents of the line being displayed. */ + bounds.left_white = bounds.left_in; + bounds.right_white = bounds.right_in; + + while (ypos < yend) + { + struct display_line dl; + struct display_line *dlp; + int local; + + if (Dynarr_length (dla) < Dynarr_largest (dla)) + { + dlp = Dynarr_atp (dla, Dynarr_length (dla)); + local = 0; + } + else + { + + xzero (dl); + dlp = &dl; + local = 1; + } + + dlp->bounds = bounds; + dlp->offset = 0; + start_pos = generate_string_display_line (w, disp_string, dlp, start_pos, + &prop, default_face); + dlp->ypos = ypos + dlp->ascent; + ypos = dlp->ypos + dlp->descent; + + if (ypos > yend) + { + int visible_height = dlp->ascent + dlp->descent; + + dlp->clip = (ypos - yend); + visible_height -= dlp->clip; + + if (visible_height < VERTICAL_CLIP (w, 1)) + { + if (local) + free_display_line (dlp); + break; + } + } + else + dlp->clip = 0; + + Dynarr_add (dla, *dlp); + + /* #### This type of check needs to be done down in the + generate_display_line call. */ + if (start_pos >= XSTRING_CHAR_LENGTH (disp_string)) + break; + } + + if (prop) + Dynarr_free (prop); +} + + +/***************************************************************************/ +/* */ +/* window-regeneration routines */ +/* */ +/***************************************************************************/ + +/* For a given window and starting position in the buffer it contains, + ensure that the TYPE display lines accurately represent the + presentation of the window. We pass the buffer instead of getting + it from the window since redisplay_window may have temporarily + changed it to the echo area buffer. */ + +static void +regenerate_window (struct window *w, Bufpos start_pos, Bufpos point, int type) +{ + struct frame *f = XFRAME (w->frame); + struct buffer *b = XBUFFER (w->buffer); + int ypos = WINDOW_TEXT_TOP (w); + int yend; /* set farther down */ + + prop_block_dynarr *prop; + layout_bounds bounds; + display_line_dynarr *dla; + int need_modeline; + + /* The lines had better exist by this point. */ + if (!(dla = window_display_lines (w, type))) + abort (); + Dynarr_reset (dla); + w->max_line_len = 0; + + /* Normally these get updated in redisplay_window but it is possible + for this function to get called from some other points where that + update may not have occurred. This acts as a safety check. */ + if (!Dynarr_length (w->face_cachels)) + reset_face_cachels (w); + if (!Dynarr_length (w->glyph_cachels)) + reset_glyph_cachels (w); + + Fset_marker (w->start[type], make_int (start_pos), w->buffer); + Fset_marker (w->pointm[type], make_int (point), w->buffer); + w->last_point_x[type] = -1; + w->last_point_y[type] = -1; + + /* Make sure a modeline is in the structs if needed. */ + need_modeline = ensure_modeline_generated (w, type); + + /* Wait until here to set this so that the structs have a modeline + generated in the case where one didn't exist. */ + yend = WINDOW_TEXT_BOTTOM (w); + + bounds = calculate_display_line_boundaries (w, 0); + + /* 97/3/14 jhod: stuff added here to support pre-prompts (used for input systems) */ + if (MINI_WINDOW_P (w) + && (!NILP (Vminibuf_prompt) || !NILP (Vminibuf_preprompt)) + && !echo_area_active (f) + && start_pos == BUF_BEGV (b)) + { + struct prop_block pb; + Lisp_Object string; + prop = Dynarr_new (prop_block); + + string = concat2(Vminibuf_preprompt, Vminibuf_prompt); + pb.type = PROP_MINIBUF_PROMPT; + pb.data.p_string.str = XSTRING_DATA(string); + pb.data.p_string.len = XSTRING_LENGTH(string); + Dynarr_add (prop, pb); + } + else + prop = 0; + + while (ypos < yend) + { + struct display_line dl; + struct display_line *dlp; + int local; + + if (Dynarr_length (dla) < Dynarr_largest (dla)) + { + dlp = Dynarr_atp (dla, Dynarr_length (dla)); + local = 0; + } + else + { + xzero (dl); dlp = &dl; local = 1; @@ -5391,6 +6227,7 @@ being handled. */ update_frame_menubars (f); #endif /* HAVE_MENUBARS */ + update_frame_gutters (f); /* widgets are similar to menus in that they can call lisp to determine activation etc. Therefore update them before we get into redisplay. This is primarily for connected widgets such as @@ -5474,6 +6311,7 @@ f->modeline_changed = 0; f->point_changed = 0; f->toolbar_changed = 0; + f->gutter_changed = 0; f->windows_changed = 0; f->windows_structure_changed = 0; f->window_face_cache_reset = 0; @@ -5532,7 +6370,8 @@ f->faces_changed || f->frame_changed || f->menubar_changed || f->modeline_changed || f->point_changed || f->size_changed || f->toolbar_changed || f->windows_changed || f->size_slipped || - f->windows_structure_changed || f->glyphs_changed || f->subwindows_changed) + f->windows_structure_changed || f->glyphs_changed || + f->subwindows_changed || f->gutter_changed) { preempted = redisplay_frame (f, 0); } @@ -5566,7 +6405,7 @@ f->faces_changed || f->frame_changed || f->menubar_changed || f->modeline_changed || f->point_changed || f->size_changed || f->toolbar_changed || f->windows_changed || - f->windows_structure_changed || + f->windows_structure_changed || f->gutter_changed || f->glyphs_changed || f->subwindows_changed) { preempted = redisplay_frame (f, 0); @@ -5594,6 +6433,7 @@ d->modeline_changed = 0; d->point_changed = 0; d->toolbar_changed = 0; + d->gutter_changed = 0; d->windows_changed = 0; d->windows_structure_changed = 0; @@ -5635,8 +6475,8 @@ !menubar_changed && !modeline_changed && !point_changed && !size_changed && !toolbar_changed && !windows_changed && !glyphs_changed && !subwindows_changed && - !windows_structure_changed && !disable_preemption && - preemption_count < max_preempts) + !gutter_changed && !windows_structure_changed && + !disable_preemption && preemption_count < max_preempts) goto done; DEVICE_LOOP_NO_BREAK (devcons, concons) @@ -5648,7 +6488,7 @@ d->faces_changed || d->frame_changed || d->icon_changed || d->menubar_changed || d->modeline_changed || d->point_changed || d->size_changed || d->toolbar_changed || d->windows_changed || - d->windows_structure_changed || + d->windows_structure_changed || d->gutter_changed || d->glyphs_changed || d->subwindows_changed) { preempted = redisplay_device (d); @@ -5679,6 +6519,7 @@ modeline_changed = 0; point_changed = 0; toolbar_changed = 0; + gutter_changed = 0; windows_changed = 0; windows_structure_changed = 0; RESET_CHANGED_SET_FLAGS; Index: src/redisplay.h =================================================================== RCS file: /usr/CVSroot/XEmacs/xemacs/src/redisplay.h,v retrieving revision 1.7.2.3 diff -u -r1.7.2.3 redisplay.h --- src/redisplay.h 1999/01/12 01:38:18 1.7.2.3 +++ src/redisplay.h 1999/07/11 18:40:16 @@ -233,6 +233,32 @@ Dynarr_declare (glyph_block); } glyph_block_dynarr; +/*************************************************************************/ +/* display lines */ +/*************************************************************************/ + +/* Modeline commentary: IMO the modeline is handled very badly, we + special case virtually *everything* in the redisplay routines for + the modeline. The fact that dl->bufpos can be either a buffer + position or a char count highlights this. There is no abstraction at + all that I can find and it means that the code is made very ugly as + a result. Either we should treat the modeline *entirely* separately, + or we should abstract to something that applies equally well to the + modeline and to buffer text, the things are not enormously different + after all and handling them identically at some level would + eliminate some bugs that still exist (mainly to do with modeline + handling). This problem doesn't help trying to implement gutters + which are somewhere in between buffer text and modeline text. + + Redisplay commentary: Everything in redisplay is tied very tightly + to the things that are being displayed, and the context, + e.g. buffers and windows. According to Chuck this is so that we can + get speed, which seems fine to me, however this usage is extended + too far down the redispay routines IMO. At some level there should + be functions that know how to display strings with extents and + faces, regardless of buffer etc. After all the window system does + not care. */ + typedef struct display_line display_line; struct display_line { @@ -268,6 +294,10 @@ /* Dynamic arrays of left and right glyph blocks */ glyph_block_dynarr *left_glyphs; glyph_block_dynarr *right_glyphs; + + face_index left_margin_findex; + face_index right_margin_findex; + face_index default_findex; }; #define DISPLAY_LINE_HEIGHT(dl) \ @@ -387,6 +417,10 @@ extern int toolbar_changed; extern int toolbar_changed_set; +/* non-nil if any gutter has changed */ +extern int gutter_changed; +extern int gutter_changed_set; + /* non-nil if any window has changed since the last time redisplay completed */ extern int windows_changed; @@ -426,6 +460,7 @@ #define MARK_MODELINE_CHANGED MARK_TYPE_CHANGED (modeline) #define MARK_POINT_CHANGED MARK_TYPE_CHANGED (point) #define MARK_TOOLBAR_CHANGED MARK_TYPE_CHANGED (toolbar) +#define MARK_GUTTER_CHANGED MARK_TYPE_CHANGED (gutter) #define MARK_GLYPHS_CHANGED MARK_TYPE_CHANGED (glyphs) #define MARK_SUBWINDOWS_CHANGED MARK_TYPE_CHANGED (subwindows) @@ -441,6 +476,7 @@ modeline_changed_set = 0; \ point_changed_set = 0; \ toolbar_changed_set = 0; \ + gutter_changed_set = 0; \ glyphs_changed_set = 0; \ subwindows_changed_set = 0; \ } while (0) @@ -522,6 +558,10 @@ Bufbyte *generate_formatted_string (struct window *w, Lisp_Object format_str, Lisp_Object result_str, face_index findex, int type); +void generate_displayable_area (struct window *w, Lisp_Object disp_string, + int xpos, int ypos, int width, int height, + display_line_dynarr* dl, + Bufpos start_pos, face_index default_face); int real_current_modeline_height (struct window *w); int pixel_to_glyph_translation (struct frame *f, int x_coord, int y_coord, int *col, int *row, Index: src/scrollbar.c =================================================================== RCS file: /usr/CVSroot/XEmacs/xemacs/src/scrollbar.c,v retrieving revision 1.17.2.1 diff -u -r1.17.2.1 scrollbar.c --- src/scrollbar.c 1998/12/05 16:56:26 1.17.2.1 +++ src/scrollbar.c 1999/07/11 18:40:19 @@ -468,7 +468,7 @@ y_offset = f->scrollbar_y_offset + (!NILP (w->scrollbar_on_top_p) ? WINDOW_TOP (w) - : WINDOW_TEXT_BOTTOM (w) + window_bottom_toolbar_height (w)); + : WINDOW_TEXT_BOTTOM (w) /* + window_bottom_toolbar_height (w)*/); } new_x = x_offset; Index: src/search.c =================================================================== RCS file: /usr/CVSroot/XEmacs/xemacs/src/search.c,v retrieving revision 1.14.2.2 diff -u -r1.14.2.2 search.c --- src/search.c 1999/01/18 22:00:18 1.14.2.2 +++ src/search.c 1999/07/11 18:40:26 @@ -700,6 +700,50 @@ return scan_buffer (buf, '\n', from, 0, count, 0, 1); } +Bytind +bi_find_next_emchar_in_string (struct Lisp_String* str, Emchar target, Bytind st, + EMACS_INT count) +{ + /* This function has been Mule-ized. */ + Bytind lim = string_length (str) -1; + Bufbyte* s = string_data (str); + + assert (count >= 0); + +#ifdef MULE + /* Due to the Mule representation of characters in a buffer, + we can simply search for characters in the range 0 - 127 + directly. For other characters, we do it the "hard" way. + Note that this way works for all characters but the other + way is faster. */ + if (target >= 0200) + { + while (st < lim && count > 0) + { + if (string_char (str, st) == target) + count--; + INC_CHARBYTIND (s, st); + } + } + else +#endif + { + while (st < lim && count > 0) + { + Bufbyte *bufptr = (Bufbyte *) memchr (charptr_n_addr (s, st), + (int) target, lim - st); + if (bufptr) + { + count--; + st = (Bytind)(bufptr - s) + 1; + } + else + st = lim; + } + } + return st; +} + /* Like find_next_newline, but returns position before the newline, not after, and only search up to TO. This isn't just find_next_newline (...)-1, because you might hit TO. */ Index: src/symsinit.h =================================================================== RCS file: /usr/CVSroot/XEmacs/xemacs/src/symsinit.h,v retrieving revision 1.31.2.7 diff -u -r1.31.2.7 symsinit.h --- src/symsinit.h 1999/07/05 07:28:25 1.31.2.7 +++ src/symsinit.h 1999/07/11 18:40:26 @@ -97,6 +97,7 @@ void syms_of_glyphs (void); void syms_of_gui_x (void); void syms_of_gui (void); +void syms_of_gutter (void); void syms_of_indent (void); void syms_of_intl (void); void syms_of_keymap (void); @@ -182,6 +183,7 @@ void specifier_type_create (void); void specifier_type_create_image (void); +void specifier_type_create_gutter (void); void specifier_type_create_objects (void); void specifier_type_create_toolbar (void); @@ -273,6 +275,7 @@ void vars_of_glyphs (void); void vars_of_gui_x (void); void vars_of_gui (void); +void vars_of_gutter (void); void vars_of_input_method_motif (void); void vars_of_input_method_xlib (void); void vars_of_indent (void); @@ -327,6 +330,7 @@ /* Initialize specifier variables (dump-time only). */ void specifier_vars_of_glyphs (void); +void specifier_vars_of_gutter (void); void specifier_vars_of_menubar (void); void specifier_vars_of_redisplay (void); void specifier_vars_of_scrollbar (void); Index: src/window.c =================================================================== RCS file: /usr/CVSroot/XEmacs/xemacs/src/window.c,v retrieving revision 1.41.2.9 diff -u -r1.41.2.9 window.c --- src/window.c 1999/06/22 18:03:01 1.41.2.9 +++ src/window.c 1999/07/11 18:40:41 @@ -38,6 +38,7 @@ #include "window.h" #include "elhash.h" #include "commands.h" +#include "gutter.h" Lisp_Object Qwindowp, Qwindow_live_p, Qwindow_configurationp; Lisp_Object Qscroll_up, Qscroll_down, Qdisplay_buffer; @@ -641,7 +642,7 @@ return window_is_leftmost (w) && window_is_rightmost (w); } -static int +int window_is_highest (struct window *w) { Lisp_Object parent, current_ancestor, window; @@ -669,7 +670,7 @@ return 0; } -static int +int window_is_lowest (struct window *w) { Lisp_Object parent, current_ancestor, window; @@ -972,32 +973,6 @@ return margin_width_internal (w, 0); } -static int -window_top_toolbar_height (struct window *w) -{ - /* #### implement this shit. */ - return 0; -} - -/* #### Currently used in scrollbar.c. Does it actually need to be? */ -int -window_bottom_toolbar_height (struct window *w) -{ - return 0; -} - -static int -window_left_toolbar_width (struct window *w) -{ - return 0; -} - -static int -window_right_toolbar_width (struct window *w) -{ - return 0; -} - /***************************************************************************** Window Gutters @@ -1019,47 +994,45 @@ int window_top_gutter_height (struct window *w) { - int toolbar_height = window_top_toolbar_height (w); + int gutter = WINDOW_REAL_TOP_GUTTER_BOUNDS (w); if (!NILP (w->hchild) || !NILP (w->vchild)) return 0; #ifdef HAVE_SCROLLBARS if (!NILP (w->scrollbar_on_top_p)) - return window_scrollbar_height (w) + toolbar_height; + return window_scrollbar_height (w) + gutter; else #endif - return toolbar_height; + return gutter; } int window_bottom_gutter_height (struct window *w) { - int other_height; + int gutter = WINDOW_REAL_BOTTOM_GUTTER_BOUNDS (w); if (!NILP (w->hchild) || !NILP (w->vchild)) return 0; - else - other_height = - window_modeline_height (w) + window_bottom_toolbar_height (w); + + gutter += window_modeline_height (w); #ifdef HAVE_SCROLLBARS if (NILP (w->scrollbar_on_top_p)) - return window_scrollbar_height (w) + other_height; + return window_scrollbar_height (w) + gutter; else #endif - return other_height; + return gutter; } int window_left_gutter_width (struct window *w, int modeline) { - int gutter = window_left_toolbar_width (w); + int gutter = WINDOW_REAL_LEFT_GUTTER_BOUNDS (w); if (!NILP (w->hchild) || !NILP (w->vchild)) return 0; - #ifdef HAVE_SCROLLBARS if (!modeline && !NILP (w->scrollbar_on_left_p)) gutter += window_scrollbar_width (w); @@ -1071,7 +1044,7 @@ int window_right_gutter_width (struct window *w, int modeline) { - int gutter = window_right_toolbar_width (w); + int gutter = WINDOW_REAL_RIGHT_GUTTER_BOUNDS (w); if (!NILP (w->hchild) || !NILP (w->vchild)) return 0; Index: src/window.h =================================================================== RCS file: /usr/CVSroot/XEmacs/xemacs/src/window.h,v retrieving revision 1.13.2.2 diff -u -r1.13.2.2 window.h --- src/window.h 1998/12/18 05:42:16 1.13.2.2 +++ src/window.h 1999/07/11 18:40:44 @@ -329,6 +329,8 @@ int window_displayed_height (struct window *); int window_is_leftmost (struct window *w); int window_is_rightmost (struct window *w); +int window_is_lowest (struct window *w); +int window_is_highest (struct window *w); int window_truncation_on (struct window *w); int window_needs_vertical_divider (struct window *); int window_scrollbar_width (struct window *w); @@ -340,7 +342,6 @@ int window_bottom_gutter_height (struct window *w); int window_left_gutter_width (struct window *w, int modeline); int window_right_gutter_width (struct window *w, int modeline); -int window_bottom_toolbar_height (struct window *w); void delete_all_subwindows (struct window *w); void set_window_pixheight (Lisp_Object window, int pixheight, Index: src/winslots.h =================================================================== RCS file: /usr/CVSroot/XEmacs/xemacs/src/winslots.h,v retrieving revision 1.4 diff -u -r1.4 winslots.h --- src/winslots.h 1998/06/30 06:36:06 1.4 +++ src/winslots.h 1999/07/11 18:40:44 @@ -97,6 +97,25 @@ WINDOW_SLOT (default_toolbar_visible_p, EQ); WINDOW_SLOT (default_toolbar_border_width, EQ); #endif /* HAVE_TOOLBARS */ + + /* Gutter specification for each of the four positions. + This is not a size hog because the value here is not copied, + and will be shared with the specs in the specifier. */ + WINDOW_SLOT_ARRAY (gutter, 4, EQUAL_WRAPPED); + /* Gutter size for each of the four positions. */ + WINDOW_SLOT_ARRAY (gutter_size, 4, EQUAL_WRAPPED); + /* Gutter border width for each of the four positions. */ + WINDOW_SLOT_ARRAY (gutter_border_width, 4, EQUAL_WRAPPED); + /* Gutter visibility status for each of the four positions. */ + WINDOW_SLOT_ARRAY (gutter_visible_p, 4, EQUAL_WRAPPED); + /* The following five don't really need to be cached except + that we need to know when they've changed. */ + WINDOW_SLOT (default_gutter, EQUAL_WRAPPED); + WINDOW_SLOT (default_gutter_width, EQ); + WINDOW_SLOT (default_gutter_height, EQ); + WINDOW_SLOT (default_gutter_visible_p, EQ); + WINDOW_SLOT (default_gutter_border_width, EQ); +/* margins */ WINDOW_SLOT (left_margin_width, EQ); WINDOW_SLOT (right_margin_width, EQ); WINDOW_SLOT (minimum_line_ascent, EQ); Index: tests/glyph-test.el =================================================================== RCS file: /usr/CVSroot/XEmacs/xemacs/tests/Attic/glyph-test.el,v retrieving revision 1.1.2.8 diff -u -r1.1.2.8 glyph-test.el --- tests/glyph-test.el 1999/06/29 14:35:35 1.1.2.8 +++ tests/glyph-test.el 1999/07/11 18:40:44 @@ -1,6 +1,7 @@ +(setq str "Hello There") (set-extent-begin-glyph - (make-extent (point) (point)) - (setq icon (make-glyph [xpm :file "../etc/xemacs-icon.xpm"]))) + (make-extent 0 0 str) + (make-glyph [xpm :file "../etc/xemacs-icon.xpm"])) (defun foo () (interactive) Index: src/gutter.h =================================================================== RCS file: gutter.h diff -N gutter.h --- /dev/null Sun Jul 11 03:13:12 1999 +++ src/gutter.h Sun Jul 11 11:43:43 1999 @@ -0,0 +1,121 @@ +/* Define general gutter support. + Copyright (C) 1999 Andy Piper. + +This file is part of XEmacs. + +XEmacs is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 2, or (at your option) any +later version. + +XEmacs is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with XEmacs; see the file COPYING. If not, write to +the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +/* Synched up with: Not in FSF. */ + +#ifndef _XEMACS_GUTTER_H_ +#define _XEMACS_GUTTER_H_ + +#include "specifier.h" + +#define DEVICE_SUPPORTS_GUTTERS_P(d) \ + (HAS_DEVMETH_P ((d), output_frame_gutters)) + +DECLARE_SPECIFIER_TYPE (gutter); +#define XGUTTER_SPECIFIER(x) XSPECIFIER_TYPE (x, gutter) +#define XSETGUTTER_SPECIFIER(x, p) XSETSPECIFIER_TYPE (x, p, gutter) +#define GUTTER_SPECIFIERP(x) SPECIFIER_TYPEP (x, gutter) +#define CHECK_GUTTER_SPECIFIER(x) CHECK_SPECIFIER_TYPE (x, gutter) +#define CONCHECK_GUTTER_SPECIFIER(x) CONCHECK_SPECIFIER_TYPE (x, gutter) + +#define DEFAULT_GUTTER_HEIGHT 0 +#define DEFAULT_GUTTER_WIDTH 0 +#define DEFAULT_GUTTER_BORDER_WIDTH 0 + +enum gutter_pos +{ + TOP_GUTTER, + BOTTOM_GUTTER, + LEFT_GUTTER, + RIGHT_GUTTER +}; + +extern Lisp_Object Qgutter; + +extern Lisp_Object Vgutter_size[4]; +extern Lisp_Object Vgutter_border_width[4]; +void update_frame_gutters (struct frame *f); +void init_frame_gutters (struct frame *f); +void init_device_gutters (struct device *d); +void init_global_gutters (struct device *d); +void free_frame_gutters (struct frame *f); +void redraw_exposed_gutters (struct frame *f, int x, int y, int width, + int height); + +#define WINDOW_GUTTER_BORDER_WIDTH(w, pos) \ +(NILP ((w)->gutter_border_width[pos]) ? 0 : XINT ((w)->gutter_border_width[pos])) +#define WINDOW_GUTTER_SIZE(w, pos) \ +(NILP ((w)->gutter_size[pos]) ? 0 : XINT ((w)->gutter_size[pos])) +#define WINDOW_GUTTER_VISIBLE(w, pos) \ +((w)->gutter_visible_p[pos]) +#define WINDOW_GUTTER(w, pos) \ +((w)->gutter[pos]) + +#define WINDOW_REAL_GUTTER_SIZE(w, pos) \ + (!NILP (WINDOW_GUTTER_VISIBLE (w, pos)) \ + ? WINDOW_GUTTER_SIZE (w, pos) \ + : 0) +#define WINDOW_REAL_GUTTER_VISIBLE(f, pos) \ + (WINDOW_REAL_GUTTER_SIZE (f, pos) > 0) +#define WINDOW_REAL_GUTTER_BORDER_WIDTH(f, pos) \ + (!NILP (WINDOW_GUTTER_VISIBLE (f, pos)) \ + ? WINDOW_GUTTER_BORDER_WIDTH (f, pos) \ + : 0) +#define WINDOW_REAL_GUTTER_BOUNDS(f, pos) \ + (WINDOW_REAL_GUTTER_SIZE (f,pos) + \ + 2 * WINDOW_REAL_GUTTER_BORDER_WIDTH (f,pos)) + +/* these macros predicate size on position and type of window */ +#define WINDOW_REAL_TOP_GUTTER_BOUNDS(w) \ + ((!MINI_WINDOW_P (w) && window_is_highest (w)) ? \ + WINDOW_REAL_GUTTER_BOUNDS (w,TOP_GUTTER) : 0) +#define WINDOW_REAL_BOTTOM_GUTTER_BOUNDS(w) \ + ((!MINI_WINDOW_P (w) && window_is_lowest (w)) ? \ + WINDOW_REAL_GUTTER_BOUNDS (w,BOTTOM_GUTTER) : 0) +#define WINDOW_REAL_LEFT_GUTTER_BOUNDS(w) \ + ((!MINI_WINDOW_P (w) && window_is_leftmost (w)) ? \ + WINDOW_REAL_GUTTER_BOUNDS (w,LEFT_GUTTER) : 0) +#define WINDOW_REAL_RIGHT_GUTTER_BOUNDS(w) \ + ((!MINI_WINDOW_P (w) && window_is_rightmost (w)) ? \ + WINDOW_REAL_GUTTER_BOUNDS (w,RIGHT_GUTTER) : 0) + +#define FRAME_GUTTER_VISIBLE(f, pos) \ + WINDOW_REAL_GUTTER_VISIBLE (XWINDOW (FRAME_LAST_NONMINIBUF_WINDOW (f)), pos) +#define FRAME_GUTTER_SIZE(f, pos) \ + WINDOW_REAL_GUTTER_SIZE (XWINDOW (FRAME_LAST_NONMINIBUF_WINDOW (f)), pos) +#define FRAME_GUTTER_BOUNDS(f, pos) \ + WINDOW_REAL_GUTTER_BOUNDS (XWINDOW (FRAME_LAST_NONMINIBUF_WINDOW (f)), pos) +#define FRAME_GUTTER_BORDER_WIDTH(f, pos) \ + WINDOW_REAL_GUTTER_BORDER_WIDTH (XWINDOW (FRAME_LAST_NONMINIBUF_WINDOW (f)), pos) + +#define FRAME_GUTTER(f, pos) \ +WINDOW_GUTTER (XWINDOW (FRAME_LAST_NONMINIBUF_WINDOW (f)), pos) + +/* these macros predicate size on position and type of window */ +#define FRAME_TOP_GUTTER_BOUNDS(f) \ + WINDOW_REAL_TOP_GUTTER_BOUNDS (XWINDOW (FRAME_LAST_NONMINIBUF_WINDOW (f))) +#define FRAME_BOTTOM_GUTTER_BOUNDS(f) \ + WINDOW_REAL_BOTTOM_GUTTER_BOUNDS (XWINDOW (FRAME_LAST_NONMINIBUF_WINDOW (f))) +#define FRAME_LEFT_GUTTER_BOUNDS(f) \ + WINDOW_REAL_LEFT_GUTTER_BOUNDS (XWINDOW (FRAME_LAST_NONMINIBUF_WINDOW (f))) +#define FRAME_RIGHT_GUTTER_BOUNDS(f) \ + WINDOW_REAL_RIGHT_GUTTER_BOUNDS (XWINDOW (FRAME_LAST_NONMINIBUF_WINDOW (f))) + +#endif /* _XEMACS_GUTTER_H_ */ Index: src/gutter.c =================================================================== RCS file: gutter.c diff -N gutter.c --- /dev/null Sun Jul 11 03:13:12 1999 +++ src/gutter.c Sun Jul 11 11:43:43 1999 @@ -0,0 +1,882 @@ +/* Gutter implementation. + Copyright (C) 1999 Andy Piper. + +This file is part of XEmacs. + +XEmacs is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 2, or (at your option) any +later version. + +XEmacs is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with XEmacs; see the file COPYING. If not, write to +the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +/* Synched up with: Not in FSF. */ + +/* Specifers ripped-off from toolbar.c */ + +#include +#include "lisp.h" + +#include "buffer.h" +#include "frame.h" +#include "device.h" +#include "faces.h" +#include "glyphs.h" +#include "redisplay.h" +#include "window.h" +#include "gutter.h" + +Lisp_Object Vgutter[4]; +Lisp_Object Vgutter_size[4]; +Lisp_Object Vgutter_visible_p[4]; +Lisp_Object Vgutter_border_width[4]; + +Lisp_Object Vdefault_gutter, Vdefault_gutter_visible_p; +Lisp_Object Vdefault_gutter_width, Vdefault_gutter_height; +Lisp_Object Vdefault_gutter_border_width; + +Lisp_Object Vdefault_gutter_position; + +#define SET_GUTTER_WAS_VISIBLE_FLAG(frame, pos, flag) \ + do { \ + switch (pos) \ + { \ + case TOP_GUTTER: \ + (frame)->top_gutter_was_visible = flag; \ + break; \ + case BOTTOM_GUTTER: \ + (frame)->bottom_gutter_was_visible = flag; \ + break; \ + case LEFT_GUTTER: \ + (frame)->left_gutter_was_visible = flag; \ + break; \ + case RIGHT_GUTTER: \ + (frame)->right_gutter_was_visible = flag; \ + break; \ + default: \ + abort (); \ + } \ + } while (0) + +static int gutter_was_visible (struct frame* frame, enum gutter_pos pos) +{ + switch (pos) + { + case TOP_GUTTER: + return (frame)->top_gutter_was_visible; + case BOTTOM_GUTTER: + return (frame)->bottom_gutter_was_visible; + case LEFT_GUTTER: + return (frame)->left_gutter_was_visible; + case RIGHT_GUTTER: + return (frame)->right_gutter_was_visible; + default: + abort (); + } +} + +void +get_gutter_coords (struct frame *f, enum gutter_pos pos, int *x, int *y, + int *width, int *height) +{ + struct window* w = XWINDOW (FRAME_ROOT_WINDOW (f)); + /* The top and bottom gutters take precedence over the left and + right. */ + + /* We adjust the width and height by one to give us a narrow border + at the outside edges. However, when we are simply determining + gutter location we don't want to do that. */ + + switch (pos) + { + case TOP_GUTTER: + *x = FRAME_LEFT_BORDER_END (f); + *y = FRAME_TOP_BORDER_END (f); + *width = FRAME_RIGHT_BORDER_START (f) + - FRAME_LEFT_BORDER_END (f); + *height = FRAME_TOP_GUTTER_BOUNDS (f); + break; + case BOTTOM_GUTTER: + *x = FRAME_LEFT_BORDER_END (f); + *y = FRAME_TOP_BORDER_END (f) + + w->pixel_height + - (window_modeline_height (w) + FRAME_BOTTOM_GUTTER_BOUNDS (f)); + *width = FRAME_RIGHT_BORDER_START (f) + - FRAME_LEFT_BORDER_END (f); + *height = FRAME_BOTTOM_GUTTER_BOUNDS (f); + break; + case LEFT_GUTTER: + *x = FRAME_LEFT_BORDER_END (f); + *y = FRAME_TOP_BORDER_END (f) + FRAME_TOP_GUTTER_BOUNDS (f); + *width = FRAME_LEFT_GUTTER_BOUNDS (f); + *height = FRAME_BOTTOM_BORDER_START (f) - + (FRAME_TOP_BORDER_END (f) + FRAME_TOP_GUTTER_BOUNDS (f) + + FRAME_BOTTOM_GUTTER_BOUNDS (f)); + break; + case RIGHT_GUTTER: + *x = WINDOW_TEXT_RIGHT (w); + *y = WINDOW_TEXT_TOP (w); + *width = FRAME_RIGHT_GUTTER_BOUNDS (f); + *height = FRAME_BOTTOM_BORDER_START (f) - + (FRAME_TOP_BORDER_END (f) + FRAME_TOP_GUTTER_BOUNDS (f) + + FRAME_BOTTOM_GUTTER_BOUNDS (f)); + break; + default: + abort (); + } +} + +static void +output_gutter (struct frame *f, enum gutter_pos pos) +{ + Lisp_Object frame; + Lisp_Object window = FRAME_LAST_NONMINIBUF_WINDOW (f); + struct device *d = XDEVICE (f->device); + struct window* w = XWINDOW (window); + int x, y, width, height; + int line; + int border_width = FRAME_GUTTER_BORDER_WIDTH (f, pos); + face_index findex = get_builtin_face_cache_index (w, Vgui_element_face); + display_line_dynarr* ddla, *cdla; + + if (!f->current_display_lines) + f->current_display_lines = Dynarr_new (display_line); + if (!f->desired_display_lines) + f->desired_display_lines = Dynarr_new (display_line); + + ddla = f->desired_display_lines; + cdla = f->current_display_lines; + + XSETFRAME (frame, f); + + get_gutter_coords (f, pos, &x, &y, &width, &height); + /* clear out what we want to cover + redisplay_clear_region (window, findex, x, y, width, height); */ + /* generate some display lines */ + generate_displayable_area (w, Fspecifier_instance(Vgutter[pos], window, + Qnil, Qnil), + x + border_width, y + border_width, + width - 2 * border_width, + height - 2 * border_width, ddla, 0, findex); + /* Output each line. */ + for (line = 0; line < Dynarr_length (ddla); line++) + { + output_display_line (w, 0, ddla, line, -1, -1); + } + + /* bevel the gutter area if so desired */ + if (border_width != 0) + { + MAYBE_DEVMETH (d, bevel_area, + (w, findex, x, y, width, height, border_width)); + } +} + +static void +clear_gutter (struct frame *f, enum gutter_pos pos) +{ + Lisp_Object frame; + int x, y, width, height; + struct window* w = XWINDOW (FRAME_LAST_NONMINIBUF_WINDOW (f)); + face_index findex = get_builtin_face_cache_index (w, Vgui_element_face); + get_gutter_coords (f, pos, &x, &y, &width, &height); + XSETFRAME (frame, f); + + SET_GUTTER_WAS_VISIBLE_FLAG (f, pos, 0); + + redisplay_clear_region (frame, findex, x, y, width, height); +} + +void +update_frame_gutters (struct frame *f) +{ + if (f->gutter_changed || f->frame_changed || f->clear) + { + int pos; + for (pos = 0; pos < 4; pos++) + f->current_gutter_size[pos] = FRAME_GUTTER_SIZE (f, pos); + /* and output */ + + for (pos = 0; pos < 4; pos++) + { + if (FRAME_GUTTER_VISIBLE (f, pos)) + output_gutter (f, pos); + else if (gutter_was_visible (f, pos)) + clear_gutter (f, pos); + } + + } + f->gutter_changed = 0; +} + +static void +redraw_exposed_gutter (struct frame *f, enum toolbar_pos pos, int x, int y, + int width, int height) +{ + int g_x, g_y, g_width, g_height; + + get_gutter_coords (f, pos, &g_x, &g_y, &g_width, &g_height); + + if (((y + height) < g_y) || (y > (g_y + g_height))) + return; + if (((x + width) < g_x) || (x > (g_x + g_width))) + return; + /* #### optimize this - redrawing hte whole gutter for every expose + is very expensive */ + + /* Even if none of the gutter is in the area, the blank region at + the very least must be because the first thing we did is verify + that some portion of the gutter is in the exposed region. */ + output_gutter (f, pos); +} + +void +redraw_exposed_gutters (struct frame *f, int x, int y, int width, + int height) +{ + int pos; + for (pos = 0; pos < 4; pos++) + { + if (FRAME_GUTTER_VISIBLE (f, pos)) + redraw_exposed_gutter (f, pos, x, y, width, height); + } +} + +void +free_frame_gutters (struct frame *f) +{ + if (f->current_display_lines) + Dynarr_free (f->current_display_lines); + if (f->desired_display_lines) + Dynarr_free (f->desired_display_lines); +} + +void +init_frame_gutters (struct frame *f) +{ + int pos; + /* We are here as far in frame creation so cached specifiers are + already recomputed, and possibly modified by resource + initialization. Remember current gutter geometry so next + redisplay will not needlessly relayout gutters. */ + for (pos = 0; pos < 4; pos++) + f->current_gutter_size[pos] = FRAME_GUTTER_SIZE (f, pos); +} + +static enum gutter_pos +decode_gutter_position (Lisp_Object position) +{ + if (EQ (position, Qtop)) return TOP_GUTTER; + if (EQ (position, Qbottom)) return BOTTOM_GUTTER; + if (EQ (position, Qleft)) return LEFT_GUTTER; + if (EQ (position, Qright)) return RIGHT_GUTTER; + signal_simple_error ("Invalid gutter position", position); + + return TOP_GUTTER; /* not reached */ +} + +DEFUN ("set-default-gutter-position", Fset_default_gutter_position, 1, 1, 0, /* +Set the position that the `default-gutter' will be displayed at. +Valid positions are 'top, 'bottom, 'left and 'right. +See `default-gutter-position'. +*/ + (position)) +{ + enum gutter_pos cur = decode_gutter_position (Vdefault_gutter_position); + enum gutter_pos new = decode_gutter_position (position); + + if (cur != new) + { + /* The following calls will automatically cause the dirty + flags to be set; we delay frame size changes to avoid + lots of frame flickering. */ + /* #### I think this should be GC protected. -sb */ + hold_frame_size_changes (); + set_specifier_fallback (Vgutter[cur], list1 (Fcons (Qnil, Qnil))); + set_specifier_fallback (Vgutter[new], Vdefault_gutter); + set_specifier_fallback (Vgutter_size[cur], list1 (Fcons (Qnil, Qzero))); + set_specifier_fallback (Vgutter_size[new], + new == TOP_GUTTER || new == BOTTOM_GUTTER + ? Vdefault_gutter_height + : Vdefault_gutter_width); + set_specifier_fallback (Vgutter_border_width[cur], + list1 (Fcons (Qnil, Qzero))); + set_specifier_fallback (Vgutter_border_width[new], + Vdefault_gutter_border_width); + set_specifier_fallback (Vgutter_visible_p[cur], + list1 (Fcons (Qnil, Qt))); + set_specifier_fallback (Vgutter_visible_p[new], + Vdefault_gutter_visible_p); + Vdefault_gutter_position = position; + unhold_frame_size_changes (); + } + + return position; +} + +DEFUN ("default-gutter-position", Fdefault_gutter_position, 0, 0, 0, /* +Return the position that the `default-gutter' will be displayed at. +The `default-gutter' will only be displayed here if the corresponding +position-specific gutter specifier does not provide a value. +*/ + ()) +{ + return Vdefault_gutter_position; +} + +DEFINE_SPECIFIER_TYPE (gutter); + +static void +gutter_after_change (Lisp_Object specifier, Lisp_Object locale) +{ + MARK_GUTTER_CHANGED; +} + +DEFUN ("gutter-specifier-p", Fgutter_specifier_p, 1, 1, 0, /* +Return non-nil if OBJECT is a gutter specifier. +Gutter specifiers are used to specify the format of a gutter. +The values of the variables `default-gutter', `top-gutter', +`left-gutter', `right-gutter', and `bottom-gutter' are always +gutter specifiers. + +Valid gutter instantiators are called "gutter descriptors" +and are lists of vectors. See `default-gutter' for a description +of the exact format. +*/ + (object)) +{ + return GUTTER_SPECIFIERP (object) ? Qt : Qnil; +} + + +/* + Helper for invalidating the real specifier when default + specifier caching changes +*/ +static void +recompute_overlaying_specifier (Lisp_Object real_one[4]) +{ + enum gutter_pos pos = decode_gutter_position (Vdefault_gutter_position); + Fset_specifier_dirty_flag (real_one[pos]); +} + +static void +gutter_specs_changed (Lisp_Object specifier, struct window *w, + Lisp_Object oldval) +{ + MARK_GUTTER_CHANGED; +} + +static void +default_gutter_specs_changed (Lisp_Object specifier, struct window *w, + Lisp_Object oldval) +{ + recompute_overlaying_specifier (Vgutter); +} + +static void +gutter_geometry_changed_in_window (Lisp_Object specifier, struct window *w, + Lisp_Object oldval) +{ + MARK_GUTTER_CHANGED; + MARK_WINDOWS_CHANGED (w); +} + +static void +default_gutter_size_changed_in_window (Lisp_Object specifier, struct window *w, + Lisp_Object oldval) +{ + recompute_overlaying_specifier (Vgutter_size); +} + +static void +default_gutter_border_width_changed_in_window (Lisp_Object specifier, + struct window *w, + Lisp_Object oldval) +{ + recompute_overlaying_specifier (Vgutter_border_width); +} + +static void +default_gutter_visible_p_changed_in_window (Lisp_Object specifier, + struct window *w, + Lisp_Object oldval) +{ + recompute_overlaying_specifier (Vgutter_visible_p); +} + +void +syms_of_gutter (void) +{ + DEFSUBR (Fgutter_specifier_p); + DEFSUBR (Fset_default_gutter_position); + DEFSUBR (Fdefault_gutter_position); +} + +void +vars_of_gutter (void) +{ + staticpro (&Vdefault_gutter_position); + Vdefault_gutter_position = Qtop; + + Fprovide (Qgutter); +} + +void +specifier_type_create_gutter (void) +{ + INITIALIZE_SPECIFIER_TYPE (gutter, "gutter", "gutter-specifier-p"); + + SPECIFIER_HAS_METHOD (gutter, after_change); +} + +void +specifier_vars_of_gutter (void) +{ + Lisp_Object fb; + + DEFVAR_SPECIFIER ("default-gutter", &Vdefault_gutter /* +Specifier for a fallback gutter. +Use `set-specifier' to change this. + +The position of this gutter is specified in the function +`default-gutter-position'. If the corresponding position-specific +gutter (e.g. `top-gutter' if `default-gutter-position' is 'top) +does not specify a gutter in a particular domain (usually a window), +then the value of `default-gutter' in that domain, if any, will be +used instead. + +Note that the gutter at any particular position will not be +displayed unless its visibility flag is true and its thickness +\(width or height, depending on orientation) is non-zero. The +visibility is controlled by the specifiers `top-gutter-visible-p', +`bottom-gutter-visible-p', `left-gutter-visible-p', and +`right-gutter-visible-p', and the thickness is controlled by the +specifiers `top-gutter-height', `bottom-gutter-height', +`left-gutter-width', and `right-gutter-width'. + +Note that one of the four visibility specifiers inherits from +`default-gutter-visibility' and one of the four thickness +specifiers inherits from either `default-gutter-width' or +`default-gutter-height' (depending on orientation), just +like for the gutter description specifiers (e.g. `top-gutter') +mentioned above. + +Therefore, if you are setting `default-gutter', you should control +the visibility and thickness using `default-gutter-visible-p', +`default-gutter-width', and `default-gutter-height', rather than +using position-specific specifiers. That way, you will get sane +behavior if the user changes the default gutter position. + +*/ ); + + Vdefault_gutter = Fmake_specifier (Qgutter); + /* #### It would be even nicer if the specifier caching + automatically knew about specifier fallbacks, so we didn't + have to do it ourselves. */ + set_specifier_caching (Vdefault_gutter, + slot_offset (struct window, + default_gutter), + default_gutter_specs_changed, + 0, 0); + + DEFVAR_SPECIFIER ("top-gutter", + &Vgutter[TOP_GUTTER] /* +Specifier for the gutter at the top of the frame. +Use `set-specifier' to change this. +See `default-gutter' for a description of a valid gutter instantiator. +*/ ); + Vgutter[TOP_GUTTER] = Fmake_specifier (Qgutter); + set_specifier_caching (Vgutter[TOP_GUTTER], + slot_offset (struct window, + gutter[TOP_GUTTER]), + gutter_specs_changed, + 0, 0); + + DEFVAR_SPECIFIER ("bottom-gutter", + &Vgutter[BOTTOM_GUTTER] /* +Specifier for the gutter at the bottom of the frame. +Use `set-specifier' to change this. +See `default-gutter' for a description of a valid gutter instantiator. + +Note that, unless the `default-gutter-position' is `bottom', by +default the height of the bottom gutter (controlled by +`bottom-gutter-height') is 0; thus, a bottom gutter will not be +displayed even if you provide a value for `bottom-gutter'. +*/ ); + Vgutter[BOTTOM_GUTTER] = Fmake_specifier (Qgutter); + set_specifier_caching (Vgutter[BOTTOM_GUTTER], + slot_offset (struct window, + gutter[BOTTOM_GUTTER]), + gutter_specs_changed, + 0, 0); + + DEFVAR_SPECIFIER ("left-gutter", + &Vgutter[LEFT_GUTTER] /* +Specifier for the gutter at the left edge of the frame. +Use `set-specifier' to change this. +See `default-gutter' for a description of a valid gutter instantiator. + +Note that, unless the `default-gutter-position' is `left', by +default the height of the left gutter (controlled by +`left-gutter-width') is 0; thus, a left gutter will not be +displayed even if you provide a value for `left-gutter'. +*/ ); + Vgutter[LEFT_GUTTER] = Fmake_specifier (Qgutter); + set_specifier_caching (Vgutter[LEFT_GUTTER], + slot_offset (struct window, + gutter[LEFT_GUTTER]), + gutter_specs_changed, + 0, 0); + + DEFVAR_SPECIFIER ("right-gutter", + &Vgutter[RIGHT_GUTTER] /* +Specifier for the gutter at the right edge of the frame. +Use `set-specifier' to change this. +See `default-gutter' for a description of a valid gutter instantiator. + +Note that, unless the `default-gutter-position' is `right', by +default the height of the right gutter (controlled by +`right-gutter-width') is 0; thus, a right gutter will not be +displayed even if you provide a value for `right-gutter'. +*/ ); + Vgutter[RIGHT_GUTTER] = Fmake_specifier (Qgutter); + set_specifier_caching (Vgutter[RIGHT_GUTTER], + slot_offset (struct window, + gutter[RIGHT_GUTTER]), + gutter_specs_changed, + 0, 0); + + /* initially, top inherits from default; this can be + changed with `set-default-gutter-position'. */ + fb = list1 (Fcons (Qnil, Qnil)); + set_specifier_fallback (Vdefault_gutter, fb); + set_specifier_fallback (Vgutter[TOP_GUTTER], Vdefault_gutter); + set_specifier_fallback (Vgutter[BOTTOM_GUTTER], fb); + set_specifier_fallback (Vgutter[LEFT_GUTTER], fb); + set_specifier_fallback (Vgutter[RIGHT_GUTTER], fb); + + DEFVAR_SPECIFIER ("default-gutter-height", &Vdefault_gutter_height /* +*Height of the default gutter, if it's oriented horizontally. +This is a specifier; use `set-specifier' to change it. + +The position of the default gutter is specified by the function +`set-default-gutter-position'. If the corresponding position-specific +gutter thickness specifier (e.g. `top-gutter-height' if +`default-gutter-position' is 'top) does not specify a thickness in a +particular domain (a window or a frame), then the value of +`default-gutter-height' or `default-gutter-width' (depending on the +gutter orientation) in that domain, if any, will be used instead. + +Note that `default-gutter-height' is only used when +`default-gutter-position' is 'top or 'bottom, and `default-gutter-width' +is only used when `default-gutter-position' is 'left or 'right. + +Note that all of the position-specific gutter thickness specifiers +have a fallback value of zero when they do not correspond to the +default gutter. Therefore, you will have to set a non-zero thickness +value if you want a position-specific gutter to be displayed. +*/ ); + Vdefault_gutter_height = Fmake_specifier (Qnatnum); + set_specifier_caching (Vdefault_gutter_height, + slot_offset (struct window, + default_gutter_height), + default_gutter_size_changed_in_window, + 0, 0); + + DEFVAR_SPECIFIER ("default-gutter-width", &Vdefault_gutter_width /* +*Width of the default gutter, if it's oriented vertically. +This is a specifier; use `set-specifier' to change it. + +See `default-gutter-height' for more information. +*/ ); + Vdefault_gutter_width = Fmake_specifier (Qnatnum); + set_specifier_caching (Vdefault_gutter_width, + slot_offset (struct window, + default_gutter_width), + default_gutter_size_changed_in_window, + 0, 0); + + DEFVAR_SPECIFIER ("top-gutter-height", + &Vgutter_size[TOP_GUTTER] /* +*Height of the top gutter. +This is a specifier; use `set-specifier' to change it. + +See `default-gutter-height' for more information. +*/ ); + Vgutter_size[TOP_GUTTER] = Fmake_specifier (Qnatnum); + set_specifier_caching (Vgutter_size[TOP_GUTTER], + slot_offset (struct window, + gutter_size[TOP_GUTTER]), + gutter_geometry_changed_in_window, + 0, 0); + + DEFVAR_SPECIFIER ("bottom-gutter-height", + &Vgutter_size[BOTTOM_GUTTER] /* +*Height of the bottom gutter. +This is a specifier; use `set-specifier' to change it. + +See `default-gutter-height' for more information. +*/ ); + Vgutter_size[BOTTOM_GUTTER] = Fmake_specifier (Qnatnum); + set_specifier_caching (Vgutter_size[BOTTOM_GUTTER], + slot_offset (struct window, + gutter_size[BOTTOM_GUTTER]), + gutter_geometry_changed_in_window, + 0, 0); + + DEFVAR_SPECIFIER ("left-gutter-width", + &Vgutter_size[LEFT_GUTTER] /* +*Width of left gutter. +This is a specifier; use `set-specifier' to change it. + +See `default-gutter-height' for more information. +*/ ); + Vgutter_size[LEFT_GUTTER] = Fmake_specifier (Qnatnum); + set_specifier_caching (Vgutter_size[LEFT_GUTTER], + slot_offset (struct window, + gutter_size[LEFT_GUTTER]), + gutter_geometry_changed_in_window, + 0, 0); + + DEFVAR_SPECIFIER ("right-gutter-width", + &Vgutter_size[RIGHT_GUTTER] /* +*Width of right gutter. +This is a specifier; use `set-specifier' to change it. + +See `default-gutter-height' for more information. +*/ ); + Vgutter_size[RIGHT_GUTTER] = Fmake_specifier (Qnatnum); + set_specifier_caching (Vgutter_size[RIGHT_GUTTER], + slot_offset (struct window, + gutter_size[RIGHT_GUTTER]), + gutter_geometry_changed_in_window, + 0, 0); + + fb = Qnil; +#ifdef HAVE_TTY + fb = Fcons (Fcons (list1 (Qtty), Qzero), fb); +#endif +#ifdef HAVE_X_WINDOWS + fb = Fcons (Fcons (list1 (Qx), make_int (DEFAULT_GUTTER_HEIGHT)), fb); +#endif +#ifdef HAVE_MS_WINDOWS + fb = Fcons (Fcons (list1 (Qmswindows), + make_int (DEFAULT_GUTTER_HEIGHT)), fb); +#endif + if (!NILP (fb)) + set_specifier_fallback (Vdefault_gutter_height, fb); + + fb = Qnil; +#ifdef HAVE_TTY + fb = Fcons (Fcons (list1 (Qtty), Qzero), fb); +#endif +#ifdef HAVE_X_WINDOWS + fb = Fcons (Fcons (list1 (Qx), make_int (DEFAULT_GUTTER_WIDTH)), fb); +#endif +#ifdef HAVE_MS_WINDOWS + fb = Fcons (Fcons (list1 (Qmswindows), + make_int (DEFAULT_GUTTER_WIDTH)), fb); +#endif + if (!NILP (fb)) + set_specifier_fallback (Vdefault_gutter_width, fb); + + set_specifier_fallback (Vgutter_size[TOP_GUTTER], Vdefault_gutter_height); + fb = list1 (Fcons (Qnil, Qzero)); + set_specifier_fallback (Vgutter_size[BOTTOM_GUTTER], fb); + set_specifier_fallback (Vgutter_size[LEFT_GUTTER], fb); + set_specifier_fallback (Vgutter_size[RIGHT_GUTTER], fb); + + DEFVAR_SPECIFIER ("default-gutter-border-width", + &Vdefault_gutter_border_width /* +*Width of the border around the default gutter. +This is a specifier; use `set-specifier' to change it. + +The position of the default gutter is specified by the function +`set-default-gutter-position'. If the corresponding position-specific +gutter border width specifier (e.g. `top-gutter-border-width' if +`default-gutter-position' is 'top) does not specify a border width in a +particular domain (a window or a frame), then the value of +`default-gutter-border-width' in that domain, if any, will be used +instead. + +*/ ); + Vdefault_gutter_border_width = Fmake_specifier (Qnatnum); + set_specifier_caching (Vdefault_gutter_border_width, + slot_offset (struct window, + default_gutter_border_width), + default_gutter_border_width_changed_in_window, + 0, 0); + + DEFVAR_SPECIFIER ("top-gutter-border-width", + &Vgutter_border_width[TOP_GUTTER] /* +*Border width of the top gutter. +This is a specifier; use `set-specifier' to change it. + +See `default-gutter-height' for more information. +*/ ); + Vgutter_border_width[TOP_GUTTER] = Fmake_specifier (Qnatnum); + set_specifier_caching (Vgutter_border_width[TOP_GUTTER], + slot_offset (struct window, + gutter_border_width[TOP_GUTTER]), + gutter_geometry_changed_in_window, + 0, 0); + + DEFVAR_SPECIFIER ("bottom-gutter-border-width", + &Vgutter_border_width[BOTTOM_GUTTER] /* +*Border width of the bottom gutter. +This is a specifier; use `set-specifier' to change it. + +See `default-gutter-height' for more information. +*/ ); + Vgutter_border_width[BOTTOM_GUTTER] = Fmake_specifier (Qnatnum); + set_specifier_caching (Vgutter_border_width[BOTTOM_GUTTER], + slot_offset (struct window, + gutter_border_width[BOTTOM_GUTTER]), + gutter_geometry_changed_in_window, + 0, 0); + + DEFVAR_SPECIFIER ("left-gutter-border-width", + &Vgutter_border_width[LEFT_GUTTER] /* +*Border width of left gutter. +This is a specifier; use `set-specifier' to change it. + +See `default-gutter-height' for more information. +*/ ); + Vgutter_border_width[LEFT_GUTTER] = Fmake_specifier (Qnatnum); + set_specifier_caching (Vgutter_border_width[LEFT_GUTTER], + slot_offset (struct window, + gutter_border_width[LEFT_GUTTER]), + gutter_geometry_changed_in_window, + 0, 0); + + DEFVAR_SPECIFIER ("right-gutter-border-width", + &Vgutter_border_width[RIGHT_GUTTER] /* +*Border width of right gutter. +This is a specifier; use `set-specifier' to change it. + +See `default-gutter-height' for more information. +*/ ); + Vgutter_border_width[RIGHT_GUTTER] = Fmake_specifier (Qnatnum); + set_specifier_caching (Vgutter_border_width[RIGHT_GUTTER], + slot_offset (struct window, + gutter_border_width[RIGHT_GUTTER]), + gutter_geometry_changed_in_window, + 0, 0); + + fb = Qnil; +#ifdef HAVE_TTY + fb = Fcons (Fcons (list1 (Qtty), Qzero), fb); +#endif +#ifdef HAVE_X_WINDOWS + fb = Fcons (Fcons (list1 (Qx), make_int (DEFAULT_GUTTER_BORDER_WIDTH)), fb); +#endif +#ifdef HAVE_MS_WINDOWS + fb = Fcons (Fcons (list1 (Qmswindows), make_int (DEFAULT_GUTTER_BORDER_WIDTH)), fb); +#endif + if (!NILP (fb)) + set_specifier_fallback (Vdefault_gutter_border_width, fb); + + set_specifier_fallback (Vgutter_border_width[TOP_GUTTER], Vdefault_gutter_border_width); + fb = list1 (Fcons (Qnil, Qzero)); + set_specifier_fallback (Vgutter_border_width[BOTTOM_GUTTER], fb); + set_specifier_fallback (Vgutter_border_width[LEFT_GUTTER], fb); + set_specifier_fallback (Vgutter_border_width[RIGHT_GUTTER], fb); + + DEFVAR_SPECIFIER ("default-gutter-visible-p", &Vdefault_gutter_visible_p /* +*Whether the default gutter is visible. +This is a specifier; use `set-specifier' to change it. + +The position of the default gutter is specified by the function +`set-default-gutter-position'. If the corresponding position-specific +gutter visibility specifier (e.g. `top-gutter-visible-p' if +`default-gutter-position' is 'top) does not specify a visible-p value +in a particular domain (a window or a frame), then the value of +`default-gutter-visible-p' in that domain, if any, will be used +instead. + +`default-gutter-visible-p' and all of the position-specific gutter +visibility specifiers have a fallback value of true. +*/ ); + Vdefault_gutter_visible_p = Fmake_specifier (Qboolean); + set_specifier_caching (Vdefault_gutter_visible_p, + slot_offset (struct window, + default_gutter_visible_p), + default_gutter_visible_p_changed_in_window, + 0, 0); + + DEFVAR_SPECIFIER ("top-gutter-visible-p", + &Vgutter_visible_p[TOP_GUTTER] /* +*Whether the top gutter is visible. +This is a specifier; use `set-specifier' to change it. + +See `default-gutter-visible-p' for more information. +*/ ); + Vgutter_visible_p[TOP_GUTTER] = Fmake_specifier (Qboolean); + set_specifier_caching (Vgutter_visible_p[TOP_GUTTER], + slot_offset (struct window, + gutter_visible_p[TOP_GUTTER]), + gutter_geometry_changed_in_window, + 0, 0); + + DEFVAR_SPECIFIER ("bottom-gutter-visible-p", + &Vgutter_visible_p[BOTTOM_GUTTER] /* +*Whether the bottom gutter is visible. +This is a specifier; use `set-specifier' to change it. + +See `default-gutter-visible-p' for more information. +*/ ); + Vgutter_visible_p[BOTTOM_GUTTER] = Fmake_specifier (Qboolean); + set_specifier_caching (Vgutter_visible_p[BOTTOM_GUTTER], + slot_offset (struct window, + gutter_visible_p[BOTTOM_GUTTER]), + gutter_geometry_changed_in_window, + 0, 0); + + DEFVAR_SPECIFIER ("left-gutter-visible-p", + &Vgutter_visible_p[LEFT_GUTTER] /* +*Whether the left gutter is visible. +This is a specifier; use `set-specifier' to change it. + +See `default-gutter-visible-p' for more information. +*/ ); + Vgutter_visible_p[LEFT_GUTTER] = Fmake_specifier (Qboolean); + set_specifier_caching (Vgutter_visible_p[LEFT_GUTTER], + slot_offset (struct window, + gutter_visible_p[LEFT_GUTTER]), + gutter_geometry_changed_in_window, + 0, 0); + + DEFVAR_SPECIFIER ("right-gutter-visible-p", + &Vgutter_visible_p[RIGHT_GUTTER] /* +*Whether the right gutter is visible. +This is a specifier; use `set-specifier' to change it. + +See `default-gutter-visible-p' for more information. +*/ ); + Vgutter_visible_p[RIGHT_GUTTER] = Fmake_specifier (Qboolean); + set_specifier_caching (Vgutter_visible_p[RIGHT_GUTTER], + slot_offset (struct window, + gutter_visible_p[RIGHT_GUTTER]), + gutter_geometry_changed_in_window, + 0, 0); + + /* initially, top inherits from default; this can be + changed with `set-default-gutter-position'. */ + fb = list1 (Fcons (Qnil, Qt)); + set_specifier_fallback (Vdefault_gutter_visible_p, fb); + set_specifier_fallback (Vgutter_visible_p[TOP_GUTTER], + Vdefault_gutter_visible_p); + set_specifier_fallback (Vgutter_visible_p[BOTTOM_GUTTER], fb); + set_specifier_fallback (Vgutter_visible_p[LEFT_GUTTER], fb); + set_specifier_fallback (Vgutter_visible_p[RIGHT_GUTTER], fb); + +}