summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/buffer.c16
-rw-r--r--src/buffer.h4
-rw-r--r--src/dispextern.h105
-rw-r--r--src/dispnew.c164
-rw-r--r--src/frame.c114
-rw-r--r--src/frame.h72
-rw-r--r--src/fringe.c12
-rw-r--r--src/keyboard.c436
-rw-r--r--src/keymap.c3
-rw-r--r--src/lisp.h1
-rw-r--r--src/menu.c2
-rw-r--r--src/msdos.c2
-rw-r--r--src/nsfns.m12
-rw-r--r--src/nsterm.m2
-rw-r--r--src/term.c3
-rw-r--r--src/termhooks.h8
-rw-r--r--src/w32fns.c110
-rw-r--r--src/w32inevt.c2
-rw-r--r--src/w32reg.c2
-rw-r--r--src/w32term.c66
-rw-r--r--src/w32term.h1
-rw-r--r--src/window.c134
-rw-r--r--src/window.h38
-rw-r--r--src/xdisp.c1165
-rw-r--r--src/xfaces.c6
-rw-r--r--src/xfns.c107
-rw-r--r--src/xterm.c63
-rw-r--r--src/xterm.h1
28 files changed, 2501 insertions, 150 deletions
diff --git a/src/buffer.c b/src/buffer.c
index 77e8b6bb779..8cb28d8aa70 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -249,6 +249,11 @@ bset_header_line_format (struct buffer *b, Lisp_Object val)
b->header_line_format_ = val;
}
static void
+bset_tab_line_format (struct buffer *b, Lisp_Object val)
+{
+ b->tab_line_format_ = val;
+}
+static void
bset_indicate_buffer_boundaries (struct buffer *b, Lisp_Object val)
{
b->indicate_buffer_boundaries_ = val;
@@ -1329,7 +1334,7 @@ No argument or nil as argument means use current buffer as BUFFER. */)
DEFUN ("force-mode-line-update", Fforce_mode_line_update,
Sforce_mode_line_update, 0, 1, 0,
doc: /* Force redisplay of the current buffer's mode line and header line.
-With optional non-nil ALL, force redisplay of all mode lines and
+With optional non-nil ALL, force redisplay of all mode lines, tab lines and
header lines. This function also forces recomputation of the
menu bar menus and the frame title. */)
(Lisp_Object all)
@@ -5194,6 +5199,7 @@ init_buffer_once (void)
XSETFASTINT (BVAR (&buffer_local_flags, scroll_up_aggressively), idx); ++idx;
XSETFASTINT (BVAR (&buffer_local_flags, scroll_down_aggressively), idx); ++idx;
XSETFASTINT (BVAR (&buffer_local_flags, header_line_format), idx); ++idx;
+ XSETFASTINT (BVAR (&buffer_local_flags, tab_line_format), idx); ++idx;
XSETFASTINT (BVAR (&buffer_local_flags, cursor_type), idx); ++idx;
XSETFASTINT (BVAR (&buffer_local_flags, extra_line_spacing), idx); ++idx;
XSETFASTINT (BVAR (&buffer_local_flags, cursor_in_non_selected_windows), idx); ++idx;
@@ -5239,6 +5245,7 @@ init_buffer_once (void)
/* real setup is done in bindings.el */
bset_mode_line_format (&buffer_defaults, build_pure_c_string ("%-"));
bset_header_line_format (&buffer_defaults, Qnil);
+ bset_tab_line_format (&buffer_defaults, Qnil);
bset_abbrev_mode (&buffer_defaults, Qnil);
bset_overwrite_mode (&buffer_defaults, Qnil);
bset_case_fold_search (&buffer_defaults, Qt);
@@ -5510,6 +5517,13 @@ syms_of_buffer (void)
Fput (Qprotected_field, Qerror_message,
build_pure_c_string ("Attempt to modify a protected field"));
+ DEFVAR_PER_BUFFER ("tab-line-format",
+ &BVAR (current_buffer, tab_line_format),
+ Qnil,
+ doc: /* Analogous to `mode-line-format', but controls the tab line.
+The tab line appears, optionally, at the top of a window;
+the mode line appears at the bottom. */);
+
DEFVAR_PER_BUFFER ("header-line-format",
&BVAR (current_buffer, header_line_format),
Qnil,
diff --git a/src/buffer.h b/src/buffer.h
index 280d4e9098e..37a50d276f1 100644
--- a/src/buffer.h
+++ b/src/buffer.h
@@ -348,6 +348,10 @@ struct buffer
of windows. Nil means don't display that line. */
Lisp_Object header_line_format_;
+ /* Analogous to mode_line_format for the line displayed at the top
+ of windows. Nil means don't display that line. */
+ Lisp_Object tab_line_format_;
+
/* Keys that are bound local to this buffer. */
Lisp_Object keymap_;
diff --git a/src/dispextern.h b/src/dispextern.h
index 05f199ff353..817f8c77d97 100644
--- a/src/dispextern.h
+++ b/src/dispextern.h
@@ -166,6 +166,7 @@ enum window_part
ON_MODE_LINE,
ON_VERTICAL_BORDER,
ON_HEADER_LINE,
+ ON_TAB_LINE,
ON_LEFT_FRINGE,
ON_RIGHT_FRINGE,
ON_LEFT_MARGIN,
@@ -762,6 +763,9 @@ struct glyph_matrix
which do their own scrolling. */
bool_bf no_scrolling_p : 1;
+ /* True means window displayed in this matrix has a tab line. */
+ bool_bf tab_line_p : 1;
+
/* True means window displayed in this matrix has a header
line. */
bool_bf header_line_p : 1;
@@ -1001,9 +1005,12 @@ struct glyph_row
implies that the row doesn't have marginal areas. */
bool_bf full_width_p : 1;
- /* True means row is a mode or header-line. */
+ /* True means row is a mode or header/tab-line. */
bool_bf mode_line_p : 1;
+ /* True means row is a tab-line. */
+ bool_bf tab_line_p : 1;
+
/* True in a current row means this row is overlapped by another row. */
bool_bf overlapped_p : 1;
@@ -1084,16 +1091,25 @@ struct glyph_row *matrix_row (struct glyph_matrix *, int);
#define MATRIX_MODE_LINE_ROW(MATRIX) \
((MATRIX)->rows + (MATRIX)->nrows - 1)
-/* Return a pointer to the row reserved for the header line in MATRIX.
+/* Return a pointer to the row reserved for the tab line in MATRIX.
This is always the first row in MATRIX because that's the only
way that works in frame-based redisplay. */
-#define MATRIX_HEADER_LINE_ROW(MATRIX) (MATRIX)->rows
+#define MATRIX_TAB_LINE_ROW(MATRIX) (MATRIX)->rows
+
+/* Return a pointer to the row reserved for the header line in MATRIX.
+ This is always the second row in MATRIX because that's the only
+ way that works in frame-based redisplay. */
+
+#define MATRIX_HEADER_LINE_ROW(MATRIX) \
+ ((MATRIX)->tab_line_p ? ((MATRIX)->rows + 1) : (MATRIX)->rows)
/* Return a pointer to first row in MATRIX used for text display. */
#define MATRIX_FIRST_TEXT_ROW(MATRIX) \
- ((MATRIX)->rows->mode_line_p ? (MATRIX)->rows + 1 : (MATRIX)->rows)
+ ((MATRIX)->rows->mode_line_p ? \
+ (((MATRIX)->rows + 1)->mode_line_p ? \
+ (MATRIX)->rows + 2 : (MATRIX)->rows + 1) : (MATRIX)->rows)
/* Return a pointer to the first glyph in the text area of a row.
MATRIX is the glyph matrix accessed, and ROW is the row index in
@@ -1162,7 +1178,7 @@ struct glyph_row *matrix_row (struct glyph_matrix *, int);
((ROW)->height != (ROW)->visible_height)
#define MR_PARTIALLY_VISIBLE_AT_TOP(W, ROW) \
- ((ROW)->y < WINDOW_HEADER_LINE_HEIGHT ((W)))
+ ((ROW)->y < (WINDOW_TAB_LINE_HEIGHT ((W)) + WINDOW_HEADER_LINE_HEIGHT ((W))))
#define MR_PARTIALLY_VISIBLE_AT_BOTTOM(W, ROW) \
(((ROW)->y + (ROW)->height - (ROW)->extra_line_spacing) \
@@ -1433,6 +1449,15 @@ struct glyph_string
? MATRIX_HEADER_LINE_ROW (MATRIX)->height \
: 0)
+/* Return the height of the tab line in glyph matrix MATRIX, or zero
+ if not known. This macro is called under circumstances where
+ MATRIX might not have been allocated yet. */
+
+#define MATRIX_TAB_LINE_HEIGHT(MATRIX) \
+ ((MATRIX) && (MATRIX)->rows \
+ ? MATRIX_TAB_LINE_ROW (MATRIX)->height \
+ : 0)
+
/* Return the desired face id for the mode line of a window, depending
on whether the window is selected or not, or if the window is the
scrolling window for the currently active minibuffer window.
@@ -1485,6 +1510,19 @@ struct glyph_string
: estimate_mode_line_height \
(XFRAME (W->frame), HEADER_LINE_FACE_ID))))
+/* Return the current height of the tab line of window W. If not known
+ from W->tab_line_height, look at W's current glyph matrix, or return
+ an estimation based on the height of the font of the face `tab-line'. */
+
+#define CURRENT_TAB_LINE_HEIGHT(W) \
+ (W->tab_line_height >= 0 \
+ ? W->tab_line_height \
+ : (W->tab_line_height \
+ = (MATRIX_TAB_LINE_HEIGHT (W->current_matrix) \
+ ? MATRIX_TAB_LINE_HEIGHT (W->current_matrix) \
+ : estimate_mode_line_height \
+ (XFRAME (W->frame), TAB_LINE_FACE_ID))))
+
/* Return the height of the desired mode line of window W. */
#define DESIRED_MODE_LINE_HEIGHT(W) \
@@ -1495,6 +1533,11 @@ struct glyph_string
#define DESIRED_HEADER_LINE_HEIGHT(W) \
MATRIX_HEADER_LINE_HEIGHT ((W)->desired_matrix)
+/* Return the height of the desired tab line of window W. */
+
+#define DESIRED_TAB_LINE_HEIGHT(W) \
+ MATRIX_TAB_LINE_HEIGHT ((W)->desired_matrix)
+
/* Return proper value to be used as baseline offset of font that has
ASCENT and DESCENT to draw characters by the font at the vertical
center of the line of frame F.
@@ -1780,6 +1823,8 @@ enum face_id
WINDOW_DIVIDER_FIRST_PIXEL_FACE_ID,
WINDOW_DIVIDER_LAST_PIXEL_FACE_ID,
INTERNAL_BORDER_FACE_ID,
+ TAB_BAR_FACE_ID,
+ TAB_LINE_FACE_ID,
BASIC_FACE_ID_SENTINEL
};
@@ -2283,6 +2328,9 @@ struct it
/* True means multibyte characters are enabled. */
bool_bf multibyte_p : 1;
+ /* True means window has a tab line at its top. */
+ bool_bf tab_line_p : 1;
+
/* True means window has a mode line at its top. */
bool_bf header_line_p : 1;
@@ -3130,6 +3178,50 @@ struct image_cache
/***********************************************************************
+ Tab-bars
+ ***********************************************************************/
+
+/* Enumeration defining where to find tab-bar item information in
+ tab-bar items vectors stored with frames. Each tab-bar item
+ occupies TAB_BAR_ITEM_NSLOTS elements in such a vector. */
+
+enum tab_bar_item_idx
+{
+ /* The key of the tab-bar item. Used to remove items when a binding
+ for `undefined' is found. */
+ TAB_BAR_ITEM_KEY,
+
+ /* Non-nil if item is enabled. */
+ TAB_BAR_ITEM_ENABLED_P,
+
+ /* Non-nil if item is selected (pressed). */
+ TAB_BAR_ITEM_SELECTED_P,
+
+ /* Caption. */
+ TAB_BAR_ITEM_CAPTION,
+
+ /* The binding. */
+ TAB_BAR_ITEM_BINDING,
+
+ /* Help string. */
+ TAB_BAR_ITEM_HELP,
+
+ /* Sentinel = number of slots in tab_bar_items occupied by one
+ tab-bar item. */
+ TAB_BAR_ITEM_NSLOTS
+};
+
+/* Default values of the above variables. */
+
+#define DEFAULT_TAB_BAR_BUTTON_MARGIN 4
+#define DEFAULT_TAB_BAR_BUTTON_RELIEF 1
+
+/* The height in pixels of the default tab-bar images. */
+
+#define DEFAULT_TAB_BAR_IMAGE_HEIGHT 18
+
+
+/***********************************************************************
Tool-bars
***********************************************************************/
@@ -3285,6 +3377,7 @@ extern bool help_echo_showing_p;
extern Lisp_Object help_echo_string, help_echo_window;
extern Lisp_Object help_echo_object, previous_help_echo_string;
extern ptrdiff_t help_echo_pos;
+extern int last_tab_bar_item;
extern int last_tool_bar_item;
extern void reseat_at_previous_visible_line_start (struct it *);
extern Lisp_Object lookup_glyphless_char_display (int, struct it *);
@@ -3332,6 +3425,8 @@ extern void get_glyph_string_clip_rect (struct glyph_string *,
NativeRectangle *nr);
extern Lisp_Object find_hot_spot (Lisp_Object, int, int);
+extern void handle_tab_bar_click (struct frame *,
+ int, int, bool, int);
extern void handle_tool_bar_click (struct frame *,
int, int, bool, int);
diff --git a/src/dispnew.c b/src/dispnew.c
index 799ef2beae8..4cf131522ec 100644
--- a/src/dispnew.c
+++ b/src/dispnew.c
@@ -80,7 +80,7 @@ static void adjust_decode_mode_spec_buffer (struct frame *);
static void fill_up_glyph_row_with_spaces (struct glyph_row *);
static void clear_window_matrices (struct window *, bool);
static void fill_up_glyph_row_area_with_spaces (struct glyph_row *, int);
-static int scrolling_window (struct window *, bool);
+static int scrolling_window (struct window *, int);
static bool update_window_line (struct window *, int, bool *);
static void mirror_make_current (struct window *, int);
#ifdef GLYPH_DEBUG
@@ -366,6 +366,8 @@ adjust_glyph_matrix (struct window *w, struct glyph_matrix *matrix, int x, int y
int i;
int new_rows;
bool marginal_areas_changed_p = 0;
+ bool tab_line_changed_p = 0;
+ bool tab_line_p = 0;
bool header_line_changed_p = 0;
bool header_line_p = 0;
int left = -1, right = -1;
@@ -377,9 +379,13 @@ adjust_glyph_matrix (struct window *w, struct glyph_matrix *matrix, int x, int y
{
window_box (w, ANY_AREA, 0, 0, &window_width, &window_height);
+ tab_line_p = window_wants_tab_line (w);
+ tab_line_changed_p = tab_line_p != matrix->tab_line_p;
+
header_line_p = window_wants_header_line (w);
header_line_changed_p = header_line_p != matrix->header_line_p;
}
+ matrix->tab_line_p = tab_line_p;
matrix->header_line_p = header_line_p;
/* If POOL is null, MATRIX is a window matrix for window-based redisplay.
@@ -397,6 +403,7 @@ adjust_glyph_matrix (struct window *w, struct glyph_matrix *matrix, int x, int y
if (!marginal_areas_changed_p
&& !XFRAME (w->frame)->fonts_changed
+ && !tab_line_changed_p
&& !header_line_changed_p
&& matrix->window_pixel_left == WINDOW_LEFT_PIXEL_EDGE (w)
&& matrix->window_pixel_top == WINDOW_TOP_PIXEL_EDGE (w)
@@ -448,7 +455,11 @@ adjust_glyph_matrix (struct window *w, struct glyph_matrix *matrix, int x, int y
if (w == NULL
|| (row == matrix->rows + dim.height - 1
&& window_wants_mode_line (w))
- || (row == matrix->rows && matrix->header_line_p))
+ || (row == matrix->rows && matrix->tab_line_p)
+ || (row == matrix->rows
+ && !matrix->tab_line_p && matrix->header_line_p)
+ || (row == (matrix->rows + 1)
+ && matrix->tab_line_p && matrix->header_line_p))
{
row->glyphs[TEXT_AREA]
= row->glyphs[LEFT_MARGIN_AREA];
@@ -478,6 +489,7 @@ adjust_glyph_matrix (struct window *w, struct glyph_matrix *matrix, int x, int y
Allocate glyph memory from the heap. */
if (dim.width > matrix->matrix_w
|| new_rows
+ || tab_line_changed_p
|| header_line_changed_p
|| marginal_areas_changed_p)
{
@@ -493,7 +505,11 @@ adjust_glyph_matrix (struct window *w, struct glyph_matrix *matrix, int x, int y
/* The mode line, if displayed, never has marginal areas. */
if ((row == matrix->rows + dim.height - 1
&& !(w && window_wants_mode_line (w)))
- || (row == matrix->rows && matrix->header_line_p))
+ || (row == matrix->rows && matrix->tab_line_p)
+ || (row == matrix->rows
+ && !matrix->tab_line_p && matrix->header_line_p)
+ || (row == (matrix->rows + 1)
+ && matrix->tab_line_p && matrix->header_line_p))
{
row->glyphs[TEXT_AREA]
= row->glyphs[LEFT_MARGIN_AREA];
@@ -539,6 +555,7 @@ adjust_glyph_matrix (struct window *w, struct glyph_matrix *matrix, int x, int y
upper window). Invalidate all rows that are no longer part
of the window. */
if (!marginal_areas_changed_p
+ && !tab_line_changed_p
&& !header_line_changed_p
&& new_rows == 0
&& dim.width == matrix->matrix_w
@@ -728,7 +745,7 @@ shift_glyph_matrix (struct window *w, struct glyph_matrix *matrix, int start, in
eassert (start >= 0 && start < matrix->nrows);
eassert (end >= 0 && end <= matrix->nrows);
- min_y = WINDOW_HEADER_LINE_HEIGHT (w);
+ min_y = WINDOW_TAB_LINE_HEIGHT (w) + WINDOW_HEADER_LINE_HEIGHT (w);
max_y = WINDOW_BOX_HEIGHT_NO_MODE_LINE (w);
for (; start < end; ++start)
@@ -767,6 +784,12 @@ clear_current_matrices (register struct frame *f)
clear_glyph_matrix (XWINDOW (f->menu_bar_window)->current_matrix);
#endif
+#if defined (HAVE_WINDOW_SYSTEM)
+ /* Clear the matrix of the tab-bar window, if any. */
+ if (WINDOWP (f->tab_bar_window))
+ clear_glyph_matrix (XWINDOW (f->tab_bar_window)->current_matrix);
+#endif
+
#if defined (HAVE_WINDOW_SYSTEM) && ! defined (HAVE_EXT_TOOL_BAR)
/* Clear the matrix of the tool-bar window, if any. */
if (WINDOWP (f->tool_bar_window))
@@ -792,6 +815,11 @@ clear_desired_matrices (register struct frame *f)
clear_glyph_matrix (XWINDOW (f->menu_bar_window)->desired_matrix);
#endif
+#if defined (HAVE_WINDOW_SYSTEM)
+ if (WINDOWP (f->tab_bar_window))
+ clear_glyph_matrix (XWINDOW (f->tab_bar_window)->desired_matrix);
+#endif
+
#if defined (HAVE_WINDOW_SYSTEM) && ! defined (HAVE_EXT_TOOL_BAR)
if (WINDOWP (f->tool_bar_window))
clear_glyph_matrix (XWINDOW (f->tool_bar_window)->desired_matrix);
@@ -857,7 +885,7 @@ blank_row (struct window *w, struct glyph_row *row, int y)
{
int min_y, max_y;
- min_y = WINDOW_HEADER_LINE_HEIGHT (w);
+ min_y = WINDOW_TAB_LINE_HEIGHT (w) + WINDOW_HEADER_LINE_HEIGHT (w);
max_y = WINDOW_BOX_HEIGHT_NO_MODE_LINE (w);
clear_glyph_row (row);
@@ -1062,7 +1090,7 @@ find_glyph_row_slice (struct glyph_matrix *window_matrix,
call to this function really clears it. In addition, this function
makes sure the marginal areas of ROW are in sync with the window's
display margins. MODE_LINE_P non-zero means we are preparing a
- glyph row for header line or mode line. */
+ glyph row for tab/header line or mode line. */
void
prepare_desired_row (struct window *w, struct glyph_row *row, bool mode_line_p)
@@ -1077,11 +1105,11 @@ prepare_desired_row (struct window *w, struct glyph_row *row, bool mode_line_p)
}
if (mode_line_p)
{
- /* Mode and header lines, if displayed, never have marginal
+ /* Mode and header/tab lines, if displayed, never have marginal
areas. If we are called with MODE_LINE_P non-zero, we are
- displaying the mode/header line in this window, and so the
+ displaying the mode/header/tab line in this window, and so the
marginal areas of this glyph row should be eliminated. This
- is needed when the mode/header line is switched on in a
+ is needed when the mode/header/tab line is switched on in a
window that has display margins. */
if (w->left_margin_cols > 0)
row->glyphs[TEXT_AREA] = row->glyphs[LEFT_MARGIN_AREA];
@@ -1099,7 +1127,7 @@ prepare_desired_row (struct window *w, struct glyph_row *row, bool mode_line_p)
/* Make sure the marginal areas of this row are in sync with
what the window wants, when the row actually displays text
- and not header/mode line. */
+ and not tab/header/mode line. */
if (w->left_margin_cols > 0
&& (left != row->glyphs[TEXT_AREA] - row->glyphs[LEFT_MARGIN_AREA]))
row->glyphs[TEXT_AREA] = row->glyphs[LEFT_MARGIN_AREA] + left;
@@ -1708,8 +1736,8 @@ required_matrix_height (struct window *w)
/* One partially visible line at the top and
bottom of the window. */
+ 2
- /* 2 for header and mode line. */
- + 2);
+ /* 3 for tab, header and mode line. */
+ + 3);
}
#endif /* HAVE_WINDOW_SYSTEM */
@@ -1862,6 +1890,7 @@ fake_current_matrices (Lisp_Object window)
- r->used[LEFT_MARGIN_AREA]
- r->used[RIGHT_MARGIN_AREA]);
r->mode_line_p = 0;
+ r->tab_line_p = 0;
}
}
}
@@ -2106,6 +2135,36 @@ adjust_frame_glyphs_for_window_redisplay (struct frame *f)
}
#endif
+#if defined (HAVE_WINDOW_SYSTEM)
+ {
+ /* Allocate/ reallocate matrices of the tab bar window. If we
+ don't have a tab bar window yet, make one. */
+ struct window *w;
+ if (NILP (f->tab_bar_window))
+ {
+ Lisp_Object frame;
+ fset_tab_bar_window (f, make_window ());
+ w = XWINDOW (f->tab_bar_window);
+ XSETFRAME (frame, f);
+ wset_frame (w, frame);
+ w->pseudo_window_p = 1;
+ }
+ else
+ w = XWINDOW (f->tab_bar_window);
+
+ w->pixel_left = 0;
+ w->left_col = 0;
+ w->pixel_top = FRAME_MENU_BAR_HEIGHT (f);
+ w->top_line = FRAME_MENU_BAR_LINES (f);
+ w->total_cols = FRAME_TOTAL_COLS (f);
+ w->pixel_width = (FRAME_PIXEL_WIDTH (f)
+ - 2 * FRAME_INTERNAL_BORDER_WIDTH (f));
+ w->total_lines = FRAME_TAB_BAR_LINES (f);
+ w->pixel_height = FRAME_TAB_BAR_HEIGHT (f);
+ allocate_matrices_for_window_redisplay (w);
+ }
+#endif
+
#if defined (HAVE_WINDOW_SYSTEM) && ! defined (HAVE_EXT_TOOL_BAR)
{
/* Allocate/ reallocate matrices of the tool bar window. If we
@@ -2125,8 +2184,8 @@ adjust_frame_glyphs_for_window_redisplay (struct frame *f)
w->pixel_left = 0;
w->left_col = 0;
- w->pixel_top = FRAME_MENU_BAR_HEIGHT (f);
- w->top_line = FRAME_MENU_BAR_LINES (f);
+ w->pixel_top = FRAME_MENU_BAR_HEIGHT (f) + FRAME_TAB_BAR_HEIGHT (f);
+ w->top_line = FRAME_MENU_BAR_LINES (f) + FRAME_TAB_BAR_LINES (f);
w->total_cols = FRAME_TOTAL_COLS (f);
w->pixel_width = (FRAME_PIXEL_WIDTH (f)
- 2 * FRAME_INTERNAL_BORDER_WIDTH (f));
@@ -2188,6 +2247,18 @@ free_glyphs (struct frame *f)
}
#endif
+#if defined (HAVE_WINDOW_SYSTEM)
+ /* Free the tab bar window and its glyph matrices. */
+ if (!NILP (f->tab_bar_window))
+ {
+ struct window *w = XWINDOW (f->tab_bar_window);
+ free_glyph_matrix (w->desired_matrix);
+ free_glyph_matrix (w->current_matrix);
+ w->desired_matrix = w->current_matrix = NULL;
+ fset_tab_bar_window (f, Qnil);
+ }
+#endif
+
#if defined (HAVE_WINDOW_SYSTEM) && ! defined (HAVE_EXT_TOOL_BAR)
/* Free the tool bar window and its glyph matrices. */
if (!NILP (f->tool_bar_window))
@@ -3082,6 +3153,29 @@ update_frame (struct frame *f, bool force_p, bool inhibit_hairy_id_p)
update_window (XWINDOW (f->menu_bar_window), true);
#endif
+#if defined (HAVE_WINDOW_SYSTEM)
+ /* Update the tab-bar window, if present. */
+ if (WINDOWP (f->tab_bar_window))
+ {
+ struct window *w = XWINDOW (f->tab_bar_window);
+
+ /* Update tab-bar window. */
+ if (w->must_be_updated_p)
+ {
+ Lisp_Object tem;
+
+ update_window (w, true);
+ w->must_be_updated_p = false;
+
+ /* Swap tab-bar strings. We swap because we want to
+ reuse strings. */
+ tem = f->current_tab_bar_string;
+ fset_current_tab_bar_string (f, f->desired_tab_bar_string);
+ fset_desired_tab_bar_string (f, tem);
+ }
+ }
+#endif
+
#if defined (HAVE_WINDOW_SYSTEM) && ! defined (HAVE_EXT_TOOL_BAR)
/* Update the tool-bar window, if present. */
if (WINDOWP (f->tool_bar_window))
@@ -3408,6 +3502,7 @@ update_window (struct window *w, bool force_p)
{
struct glyph_row *row, *end;
struct glyph_row *mode_line_row;
+ struct glyph_row *tab_line_row;
struct glyph_row *header_line_row;
int yb;
bool changed_p = 0, mouse_face_overwritten_p = 0;
@@ -3420,6 +3515,16 @@ update_window (struct window *w, bool force_p)
row = MATRIX_ROW (desired_matrix, 0);
end = MATRIX_MODE_LINE_ROW (desired_matrix);
+ /* Take note of the tab line, if there is one. We will
+ update it below, after updating all of the window's lines. */
+ if (row->mode_line_p && row->tab_line_p)
+ {
+ tab_line_row = row;
+ ++row;
+ }
+ else
+ tab_line_row = NULL;
+
/* Take note of the header line, if there is one. We will
update it below, after updating all of the window's lines. */
if (row->mode_line_p)
@@ -3449,7 +3554,8 @@ update_window (struct window *w, bool force_p)
/* Try reusing part of the display by copying. */
if (row < end && !desired_matrix->no_scrolling_p)
{
- int rc = scrolling_window (w, header_line_row != NULL);
+ int rc = scrolling_window (w, (tab_line_row != NULL ? 1 : 0)
+ + (header_line_row != NULL ? 1 : 0));
if (rc < 0)
{
/* All rows were found to be equal. */
@@ -3501,13 +3607,22 @@ update_window (struct window *w, bool force_p)
set_cursor:
+ /* Update the tab line after scrolling because a new tab
+ line would otherwise overwrite lines at the top of the window
+ that can be scrolled. */
+ if (tab_line_row && tab_line_row->enabled_p)
+ {
+ tab_line_row->y = 0;
+ update_window_line (w, 0, &mouse_face_overwritten_p);
+ }
+
/* Update the header line after scrolling because a new header
line would otherwise overwrite lines at the top of the window
that can be scrolled. */
if (header_line_row && header_line_row->enabled_p)
{
- header_line_row->y = 0;
- update_window_line (w, 0, &mouse_face_overwritten_p);
+ header_line_row->y = tab_line_row ? CURRENT_TAB_LINE_HEIGHT (w) : 0;
+ update_window_line (w, tab_line_row ? 1 : 0, &mouse_face_overwritten_p);
}
/* Fix the appearance of overlapping/overlapped rows. */
@@ -4178,7 +4293,7 @@ add_row_entry (struct glyph_row *row)
1 if we did scroll. */
static int
-scrolling_window (struct window *w, bool header_line_p)
+scrolling_window (struct window *w, int tab_line_p)
{
struct glyph_matrix *desired_matrix = w->desired_matrix;
struct glyph_matrix *current_matrix = w->current_matrix;
@@ -4191,7 +4306,7 @@ scrolling_window (struct window *w, bool header_line_p)
struct redisplay_interface *rif = FRAME_RIF (XFRAME (WINDOW_FRAME (w)));
/* Skip over rows equal at the start. */
- for (i = header_line_p; i < current_matrix->nrows - 1; ++i)
+ for (i = tab_line_p; i < current_matrix->nrows - 1; ++i)
{
struct glyph_row *d = MATRIX_ROW (desired_matrix, i);
struct glyph_row *c = MATRIX_ROW (current_matrix, i);
@@ -5318,7 +5433,8 @@ buffer_posn_from_coords (struct window *w, int *x, int *y, struct display_pos *p
start position, i.e. it excludes the header-line row, but
MATRIX_ROW includes the header-line row. Adjust for a possible
header-line row. */
- it_vpos = it.vpos + window_wants_header_line (w);
+ it_vpos = it.vpos + window_wants_header_line (w)
+ + window_wants_tab_line (w);
if (it_vpos < w->current_matrix->nrows
&& (row = MATRIX_ROW (w->current_matrix, it_vpos),
row->enabled_p))
@@ -5382,6 +5498,8 @@ mode_line_string (struct window *w, enum window_part part,
if (part == ON_MODE_LINE)
row = MATRIX_MODE_LINE_ROW (w->current_matrix);
+ else if (part == ON_TAB_LINE)
+ row = MATRIX_TAB_LINE_ROW (w->current_matrix);
else
row = MATRIX_HEADER_LINE_ROW (w->current_matrix);
y0 = *y - row->y;
@@ -5563,7 +5681,8 @@ handle_window_change_signal (int sig)
structures now. Let that be done later outside of the
signal handler. */
change_frame_size (XFRAME (frame), width,
- height - FRAME_MENU_BAR_LINES (XFRAME (frame)),
+ height - FRAME_MENU_BAR_LINES (XFRAME (frame))
+ - FRAME_TAB_BAR_LINES (XFRAME (frame)),
0, 1, 0, 0);
}
}
@@ -6243,7 +6362,8 @@ init_display_interactive (void)
change_frame_size (XFRAME (selected_frame),
FrameCols (t->display_info.tty),
FrameRows (t->display_info.tty)
- - FRAME_MENU_BAR_LINES (f), 0, 0, 1, 0);
+ - FRAME_MENU_BAR_LINES (f)
+ - FRAME_TAB_BAR_LINES (f), 0, 0, 1, 0);
/* Delete the initial terminal. */
if (--initial_terminal->reference_count == 0
diff --git a/src/frame.c b/src/frame.c
index 1d42d0cb4de..d72dfec0cf8 100644
--- a/src/frame.c
+++ b/src/frame.c
@@ -69,6 +69,9 @@ static struct frame *last_nonminibuf_frame;
/* False means there are no visible garbaged frames. */
bool frame_garbaged;
+/* The default tab bar height for future frames. */
+int frame_default_tab_bar_height;
+
/* The default tool bar height for future frames. */
#ifdef HAVE_EXT_TOOL_BAR
enum { frame_default_tool_bar_height = 0 };
@@ -230,6 +233,35 @@ set_menu_bar_lines (struct frame *f, Lisp_Object value, Lisp_Object oldval)
0, 1, 0, 0);
}
}
+
+static void
+set_tab_bar_lines (struct frame *f, Lisp_Object value, Lisp_Object oldval)
+{
+ int nlines;
+ int olines = FRAME_TAB_BAR_LINES (f);
+
+ /* Right now, tab bars don't work properly in minibuf-only frames;
+ most of the commands try to apply themselves to the minibuffer
+ frame itself, and get an error because you can't switch buffers
+ in or split the minibuffer window. */
+ if (FRAME_MINIBUF_ONLY_P (f))
+ return;
+
+ if (TYPE_RANGED_FIXNUMP (int, value))
+ nlines = XFIXNUM (value);
+ else
+ nlines = 0;
+
+ if (nlines != olines)
+ {
+ windows_or_buffers_changed = 14;
+ FRAME_TAB_BAR_LINES (f) = nlines;
+ FRAME_TAB_BAR_HEIGHT (f) = nlines * FRAME_LINE_HEIGHT (f);
+ change_frame_size (f, FRAME_COLS (f),
+ FRAME_LINES (f) + olines - nlines,
+ 0, 1, 0, 0);
+ }
+}
Lisp_Object Vframe_list;
@@ -379,6 +411,7 @@ frame_windows_min_size (Lisp_Object frame, Lisp_Object horizontal,
if ((FRAME_TERMCAP_P (f) || FRAME_MSDOS_P (f)) && NILP (horizontal))
{
int min_height = (FRAME_MENU_BAR_LINES (f)
+ + FRAME_TAB_BAR_LINES (f)
+ FRAME_WANTS_MODELINE_P (f)
+ 2); /* one text line and one echo-area line */
if (retval < min_height)
@@ -719,6 +752,15 @@ adjust_frame_size (struct frame *f, int new_width, int new_height, int inhibit,
if ((FRAME_TERMCAP_P (f) && !pretend) || FRAME_MSDOS_P (f))
FrameCols (FRAME_TTY (f)) = new_cols;
+#if defined (HAVE_WINDOW_SYSTEM)
+ if (WINDOWP (f->tab_bar_window))
+ {
+ XWINDOW (f->tab_bar_window)->pixel_width = new_windows_width;
+ XWINDOW (f->tab_bar_window)->total_cols
+ = new_windows_width / unit_width;
+ }
+#endif
+
#if defined (HAVE_WINDOW_SYSTEM) && ! defined (HAVE_EXT_TOOL_BAR)
if (WINDOWP (f->tool_bar_window))
{
@@ -838,6 +880,8 @@ make_frame (bool mini_p)
f->after_make_frame = false;
f->inhibit_horizontal_resize = false;
f->inhibit_vertical_resize = false;
+ f->tab_bar_redisplayed = false;
+ f->tab_bar_resized = false;
f->tool_bar_redisplayed = false;
f->tool_bar_resized = false;
f->column_width = 1; /* !FRAME_WINDOW_P value. */
@@ -856,6 +900,7 @@ make_frame (bool mini_p)
f->no_accept_focus = false;
f->z_group = z_group_none;
f->tooltip = false;
+ f->last_tab_bar_item = -1;
#ifndef HAVE_EXT_TOOL_BAR
f->last_tool_bar_item = -1;
#endif
@@ -1084,6 +1129,9 @@ make_initial_frame (void)
/* The default value of menu-bar-mode is t. */
set_menu_bar_lines (f, make_fixnum (1), Qnil);
+ /* The default value of tab-bar-mode is nil. */
+ set_tab_bar_lines (f, make_fixnum (0), Qnil);
+
/* Allocate glyph matrices. */
adjust_frame_glyphs (f);
@@ -1142,9 +1190,13 @@ make_terminal_frame (struct terminal *terminal)
#endif
FRAME_MENU_BAR_LINES (f) = NILP (Vmenu_bar_mode) ? 0 : 1;
- FRAME_LINES (f) = FRAME_LINES (f) - FRAME_MENU_BAR_LINES (f);
+ FRAME_TAB_BAR_LINES (f) = NILP (Vtab_bar_mode) ? 0 : 1;
+ FRAME_LINES (f) = FRAME_LINES (f) - FRAME_MENU_BAR_LINES (f)
+ - FRAME_TAB_BAR_LINES (f);
FRAME_MENU_BAR_HEIGHT (f) = FRAME_MENU_BAR_LINES (f) * FRAME_LINE_HEIGHT (f);
- FRAME_TEXT_HEIGHT (f) = FRAME_TEXT_HEIGHT (f) - FRAME_MENU_BAR_HEIGHT (f);
+ FRAME_TAB_BAR_HEIGHT (f) = FRAME_TAB_BAR_LINES (f) * FRAME_LINE_HEIGHT (f);
+ FRAME_TEXT_HEIGHT (f) = FRAME_TEXT_HEIGHT (f) - FRAME_MENU_BAR_HEIGHT (f)
+ - FRAME_TAB_BAR_HEIGHT (f);
/* Set the top frame to the newly created frame. */
if (FRAMEP (FRAME_TTY (f)->top_frame)
@@ -1266,7 +1318,8 @@ affects all frames on the same terminal device. */)
{
int width, height;
get_tty_size (fileno (FRAME_TTY (f)->input), &width, &height);
- adjust_frame_size (f, width, height - FRAME_MENU_BAR_LINES (f),
+ adjust_frame_size (f, width, height - FRAME_MENU_BAR_LINES (f)
+ - FRAME_TAB_BAR_LINES (f),
5, 0, Qterminal_frame);
}
@@ -3071,6 +3124,8 @@ store_frame_param (struct frame *f, Lisp_Object prop, Lisp_Object val)
{
if (EQ (prop, Qmenu_bar_lines))
set_menu_bar_lines (f, val, make_fixnum (FRAME_MENU_BAR_LINES (f)));
+ else if (EQ (prop, Qtab_bar_lines))
+ set_tab_bar_lines (f, val, make_fixnum (FRAME_TAB_BAR_LINES (f)));
else if (EQ (prop, Qname))
set_term_frame_name (f, val);
}
@@ -3166,6 +3221,8 @@ If FRAME is omitted or nil, return information on the currently selected frame.
Lisp_Object lines;
XSETFASTINT (lines, FRAME_MENU_BAR_LINES (f));
store_in_alist (&alist, Qmenu_bar_lines, lines);
+ XSETFASTINT (lines, FRAME_TAB_BAR_LINES (f));
+ store_in_alist (&alist, Qtab_bar_lines, lines);
}
return alist;
@@ -3695,6 +3752,7 @@ static const struct frame_parm_table frame_parms[] =
{"vertical-scroll-bars", SYMBOL_INDEX (Qvertical_scroll_bars)},
{"horizontal-scroll-bars", SYMBOL_INDEX (Qhorizontal_scroll_bars)},
{"visibility", SYMBOL_INDEX (Qvisibility)},
+ {"tab-bar-lines", SYMBOL_INDEX (Qtab_bar_lines)},
{"tool-bar-lines", SYMBOL_INDEX (Qtool_bar_lines)},
{"scroll-bar-foreground", SYMBOL_INDEX (Qscroll_bar_foreground)},
{"scroll-bar-background", SYMBOL_INDEX (Qscroll_bar_background)},
@@ -4450,6 +4508,8 @@ gui_set_font (struct frame *f, Lisp_Object arg, Lisp_Object oldval)
#ifdef HAVE_X_WINDOWS
store_frame_param (f, Qfont_parameter, font_param);
#endif
+ /* Recalculate tabbar height. */
+ f->n_tab_bar_rows = 0;
/* Recalculate toolbar height. */
f->n_tool_bar_rows = 0;
@@ -5390,8 +5450,8 @@ On Nextstep, this just calls `ns-parse-geometry'. */)
#define DEFAULT_COLS 80
long
-gui_figure_window_size (struct frame *f, Lisp_Object parms, bool toolbar_p,
- int *x_width, int *x_height)
+gui_figure_window_size (struct frame *f, Lisp_Object parms, bool tabbar_p,
+ bool toolbar_p, int *x_width, int *x_height)
{
Lisp_Object height, width, user_size, top, left, user_position;
long window_prompting = 0;
@@ -5411,6 +5471,36 @@ gui_figure_window_size (struct frame *f, Lisp_Object parms, bool toolbar_p,
f->top_pos = 0;
f->left_pos = 0;
+ /* Calculate a tab bar height so that the user gets a text display
+ area of the size he specified with -g or via .Xdefaults. Later
+ changes of the tab bar height don't change the frame size. This
+ is done so that users can create tall Emacs frames without having
+ to guess how tall the tab bar will get. */
+ if (tabbar_p && FRAME_TAB_BAR_LINES (f))
+ {
+ if (frame_default_tab_bar_height)
+ FRAME_TAB_BAR_HEIGHT (f) = frame_default_tab_bar_height;
+ else
+ {
+ int margin, relief;
+
+ relief = (tab_bar_button_relief < 0
+ ? DEFAULT_TAB_BAR_BUTTON_RELIEF
+ : min (tab_bar_button_relief, 1000000));
+
+ if (RANGED_FIXNUMP (1, Vtab_bar_button_margin, INT_MAX))
+ margin = XFIXNAT (Vtab_bar_button_margin);
+ else if (CONSP (Vtab_bar_button_margin)
+ && RANGED_FIXNUMP (1, XCDR (Vtab_bar_button_margin), INT_MAX))
+ margin = XFIXNAT (XCDR (Vtab_bar_button_margin));
+ else
+ margin = 0;
+
+ FRAME_TAB_BAR_HEIGHT (f)
+ = DEFAULT_TAB_BAR_IMAGE_HEIGHT + 2 * margin + 2 * relief;
+ }
+ }
+
/* Calculate a tool bar height so that the user gets a text display
area of the size he specified with -g or via .Xdefaults. Later
changes of the tool bar height don't change the frame size. This
@@ -5837,6 +5927,7 @@ syms_of_frame (void)
DEFSYM (Qtitle_bar_size, "title-bar-size");
DEFSYM (Qmenu_bar_external, "menu-bar-external");
DEFSYM (Qmenu_bar_size, "menu-bar-size");
+ DEFSYM (Qtab_bar_size, "tab-bar-size");
DEFSYM (Qtool_bar_external, "tool-bar-external");
DEFSYM (Qtool_bar_size, "tool-bar-size");
/* The following are used for frame_size_history. */
@@ -5860,7 +5951,9 @@ syms_of_frame (void)
DEFSYM (Qx_net_wm_state, "x-net-wm-state");
DEFSYM (Qx_handle_net_wm_state, "x-handle-net-wm-state");
DEFSYM (Qtb_size_cb, "tb-size-cb");
+ DEFSYM (Qupdate_frame_tab_bar, "update-frame-tab-bar");
DEFSYM (Qupdate_frame_tool_bar, "update-frame-tool-bar");
+ DEFSYM (Qfree_frame_tab_bar, "free-frame-tab-bar");
DEFSYM (Qfree_frame_tool_bar, "free-frame-tool-bar");
DEFSYM (Qx_set_menu_bar_lines, "x-set-menu-bar-lines");
DEFSYM (Qchange_frame_size, "change-frame-size");
@@ -5910,6 +6003,7 @@ syms_of_frame (void)
DEFSYM (Qscroll_bar_width, "scroll-bar-width");
DEFSYM (Qsticky, "sticky");
DEFSYM (Qtitle, "title");
+ DEFSYM (Qtab_bar_lines, "tab-bar-lines");
DEFSYM (Qtool_bar_lines, "tool-bar-lines");
DEFSYM (Qtool_bar_position, "tool-bar-position");
DEFSYM (Qunsplittable, "unsplittable");
@@ -6071,6 +6165,14 @@ either customize it (see the info node `Easy Customization')
or call the function `menu-bar-mode'. */);
Vmenu_bar_mode = Qt;
+ DEFVAR_LISP ("tab-bar-mode", Vtab_bar_mode,
+ doc: /* Non-nil if Tab-Bar mode is enabled.
+See the command `tab-bar-mode' for a description of this minor mode.
+Setting this variable directly does not take effect;
+either customize it (see the info node `Easy Customization')
+or call the function `tab-bar-mode'. */);
+ Vtab_bar_mode = Qnil;
+
DEFVAR_LISP ("tool-bar-mode", Vtool_bar_mode,
doc: /* Non-nil if Tool-Bar mode is enabled.
See the command `tool-bar-mode' for a description of this minor mode.
@@ -6200,7 +6302,7 @@ any of the parameters listed above, Emacs may try to enlarge the frame
even if this option is non-nil. */);
#if defined (HAVE_WINDOW_SYSTEM)
#if defined (USE_LUCID) || defined (USE_MOTIF) || defined (HAVE_NTGUI)
- frame_inhibit_implied_resize = list1 (Qtool_bar_lines);
+ frame_inhibit_implied_resize = list2 (Qtab_bar_lines, Qtool_bar_lines);
#else
frame_inhibit_implied_resize = Qnil;
#endif
diff --git a/src/frame.h b/src/frame.h
index fa45a32d6b6..1b21cc6284b 100644
--- a/src/frame.h
+++ b/src/frame.h
@@ -181,6 +181,15 @@ struct frame
Lisp_Object menu_bar_window;
#endif
+#if defined (HAVE_WINDOW_SYSTEM)
+ /* A window used to display the tab-bar of a frame. */
+ Lisp_Object tab_bar_window;
+
+ /* Desired and current contents displayed in that window. */
+ Lisp_Object desired_tab_bar_string;
+ Lisp_Object current_tab_bar_string;
+#endif
+
#if defined (HAVE_WINDOW_SYSTEM) && ! defined (HAVE_EXT_TOOL_BAR)
/* A window used to display the tool-bar of a frame. */
Lisp_Object tool_bar_window;
@@ -201,6 +210,9 @@ struct frame
Lisp_Object font_data;
#endif
+ /* Desired and current tab-bar items. */
+ Lisp_Object tab_bar_items;
+
/* Desired and current tool-bar items. */
Lisp_Object tool_bar_items;
/* tool_bar_items should be the last Lisp_Object member. */
@@ -208,6 +220,11 @@ struct frame
/* Cache of realized faces. */
struct face_cache *face_cache;
+#if defined (HAVE_WINDOW_SYSTEM)
+ /* Tab-bar item index of the item on which a mouse button was pressed. */
+ int last_tab_bar_item;
+#endif
+
#if defined (HAVE_WINDOW_SYSTEM) && ! defined (HAVE_EXT_TOOL_BAR)
/* Tool-bar item index of the item on which a mouse button was pressed. */
int last_tool_bar_item;
@@ -256,6 +273,12 @@ struct frame
/* Set to true when current redisplay has updated frame. */
bool_bf updated_p : 1;
+#if defined (HAVE_WINDOW_SYSTEM)
+ /* Set to true to minimize tab-bar height even when
+ auto-resize-tab-bar is set to grow-only. */
+ bool_bf minimize_tab_bar_window_p : 1;
+#endif
+
#if defined (HAVE_WINDOW_SYSTEM) && ! defined (HAVE_EXT_TOOL_BAR)
/* Set to true to minimize tool-bar height even when
auto-resize-tool-bar is set to grow-only. */
@@ -404,6 +427,10 @@ struct frame
/* Set to true after this frame was made by `make-frame'. */
bool_bf after_make_frame : 1;
+ /* Whether the tab bar height change should be taken into account. */
+ bool_bf tab_bar_redisplayed : 1;
+ bool_bf tab_bar_resized : 1;
+
/* Whether the tool bar height change should be taken into account. */
bool_bf tool_bar_redisplayed : 1;
bool_bf tool_bar_resized : 1;
@@ -435,6 +462,15 @@ struct frame
last time run_window_change_functions was called on it. */
ptrdiff_t number_of_windows;
+ /* Number of lines (rounded up) of tab bar. REMOVE THIS */
+ int tab_bar_lines;
+
+ /* Height of frame internal tab bar in pixels. */
+ int tab_bar_height;
+
+ int n_tab_bar_rows;
+ int n_tab_bar_items;
+
/* Number of lines (rounded up) of tool bar. REMOVE THIS */
int tool_bar_lines;
@@ -701,6 +737,28 @@ fset_title (struct frame *f, Lisp_Object val)
f->title = val;
}
INLINE void
+fset_tab_bar_items (struct frame *f, Lisp_Object val)
+{
+ f->tab_bar_items = val;
+}
+#if defined (HAVE_WINDOW_SYSTEM)
+INLINE void
+fset_tab_bar_window (struct frame *f, Lisp_Object val)
+{
+ f->tab_bar_window = val;
+}
+INLINE void
+fset_current_tab_bar_string (struct frame *f, Lisp_Object val)
+{
+ f->current_tab_bar_string = val;
+}
+INLINE void
+fset_desired_tab_bar_string (struct frame *f, Lisp_Object val)
+{
+ f->desired_tab_bar_string = val;
+}
+#endif /* HAVE_WINDOW_SYSTEM */
+INLINE void
fset_tool_bar_items (struct frame *f, Lisp_Object val)
{
f->tool_bar_items = val;
@@ -878,6 +936,12 @@ default_pixels_per_inch_y (void)
/* Pixel height of frame F's menu bar. */
#define FRAME_MENU_BAR_HEIGHT(f) (f)->menu_bar_height
+/* Number of lines of frame F used for the tab-bar. */
+#define FRAME_TAB_BAR_LINES(f) (f)->tab_bar_lines
+
+/* Pixel height of frame F's tab-bar. */
+#define FRAME_TAB_BAR_HEIGHT(f) (f)->tab_bar_height
+
/* True if this frame should display a tool bar
in a way that does not use any text lines. */
#ifdef HAVE_EXT_TOOL_BAR
@@ -901,11 +965,11 @@ default_pixels_per_inch_y (void)
/* Lines above the top-most window in frame F. */
#define FRAME_TOP_MARGIN(F) \
- (FRAME_MENU_BAR_LINES (F) + FRAME_TOOL_BAR_LINES (F))
+ (FRAME_MENU_BAR_LINES (F) + FRAME_TAB_BAR_LINES (F) + FRAME_TOOL_BAR_LINES (F))
/* Pixel height of frame F's top margin. */
#define FRAME_TOP_MARGIN_HEIGHT(F) \
- (FRAME_MENU_BAR_HEIGHT (F) + FRAME_TOOL_BAR_HEIGHT (F))
+ (FRAME_MENU_BAR_HEIGHT (F) + FRAME_TAB_BAR_HEIGHT (F) + FRAME_TOOL_BAR_HEIGHT (F))
/* True if this frame should display a menu bar
in a way that does not use any text lines. */
@@ -1255,6 +1319,8 @@ SET_FRAME_VISIBLE (struct frame *f, int v)
extern Lisp_Object selected_frame;
extern Lisp_Object old_selected_frame;
+extern int frame_default_tab_bar_height;
+
#ifndef HAVE_EXT_TOOL_BAR
extern int frame_default_tool_bar_height;
#endif
@@ -1566,7 +1632,7 @@ extern void gui_set_horizontal_scroll_bars (struct frame *, Lisp_Object, Lisp_Ob
extern void gui_set_scroll_bar_width (struct frame *, Lisp_Object, Lisp_Object);
extern void gui_set_scroll_bar_height (struct frame *, Lisp_Object, Lisp_Object);
-extern long gui_figure_window_size (struct frame *, Lisp_Object, bool, int *, int *);
+extern long gui_figure_window_size (struct frame *, Lisp_Object, bool, bool, int *, int *);
extern void gui_set_alpha (struct frame *, Lisp_Object, Lisp_Object);
extern void gui_set_no_special_glyphs (struct frame *, Lisp_Object, Lisp_Object);
diff --git a/src/fringe.c b/src/fringe.c
index 4c5a4d748fb..22f3bdc2ba8 100644
--- a/src/fringe.c
+++ b/src/fringe.c
@@ -634,7 +634,8 @@ draw_fringe_bitmap_1 (struct window *w, struct glyph_row *row, int left_p, int o
/* Clear left fringe if no bitmap to draw or if bitmap doesn't fill
the fringe. */
p.bx = -1;
- header_line_height = WINDOW_HEADER_LINE_HEIGHT (w);
+ header_line_height = WINDOW_TAB_LINE_HEIGHT (w)
+ + WINDOW_HEADER_LINE_HEIGHT (w);
p.by = WINDOW_TO_FRAME_PIXEL_Y (w, max (header_line_height, row->y));
p.ny = row->visible_height;
if (left_p)
@@ -1091,7 +1092,8 @@ update_window_fringes (struct window *w, bool keep_current_p)
struct glyph_row *row1;
int top_ind_max_y;
- top_ind_min_y = WINDOW_HEADER_LINE_HEIGHT (w);
+ top_ind_min_y = WINDOW_TAB_LINE_HEIGHT (w)
+ + WINDOW_HEADER_LINE_HEIGHT (w);
top_ind_max_y = top_ind_min_y + fb->height;
if (top_ind_max_y > yb)
top_ind_max_y = yb;
@@ -1148,8 +1150,10 @@ update_window_fringes (struct window *w, bool keep_current_p)
bot_ind_max_y = row->y + row->visible_height;
bot_ind_min_y = bot_ind_max_y - fb->height;
- if (bot_ind_min_y < WINDOW_HEADER_LINE_HEIGHT (w))
- bot_ind_min_y = WINDOW_HEADER_LINE_HEIGHT (w);
+ if (bot_ind_min_y < WINDOW_TAB_LINE_HEIGHT (w)
+ + WINDOW_HEADER_LINE_HEIGHT (w))
+ bot_ind_min_y = WINDOW_TAB_LINE_HEIGHT (w)
+ + WINDOW_HEADER_LINE_HEIGHT (w);
for (y = row->y, rn = bot_ind_rn - 1;
y >= bot_ind_min_y && rn >= 0;
diff --git a/src/keyboard.c b/src/keyboard.c
index a16d13cc7b8..40aaf496384 100644
--- a/src/keyboard.c
+++ b/src/keyboard.c
@@ -2385,7 +2385,8 @@ read_char (int commandflag, Lisp_Object map,
if (used_mouse_menu
/* Also check was_disabled so last-nonmenu-event won't return
a bad value when submenus are involved. (Bug#447) */
- && (EQ (c, Qtool_bar) || EQ (c, Qmenu_bar) || was_disabled))
+ && (EQ (c, Qtool_bar) || EQ (c, Qtab_bar) || EQ (c, Qmenu_bar)
+ || was_disabled))
*used_mouse_menu = true;
goto reread_for_input_method;
@@ -2666,6 +2667,7 @@ read_char (int commandflag, Lisp_Object map,
&& !NILP (prev_event)
&& EVENT_HAS_PARAMETERS (prev_event)
&& !EQ (XCAR (prev_event), Qmenu_bar)
+ && !EQ (XCAR (prev_event), Qtab_bar)
&& !EQ (XCAR (prev_event), Qtool_bar)
/* Don't bring up a menu if we already have another event. */
&& !CONSP (Vunread_command_events))
@@ -2930,7 +2932,7 @@ read_char (int commandflag, Lisp_Object map,
posn = POSN_POSN (xevent_start (c));
/* Handle menu-bar events:
insert the dummy prefix event `menu-bar'. */
- if (EQ (posn, Qmenu_bar) || EQ (posn, Qtool_bar))
+ if (EQ (posn, Qmenu_bar) || EQ (posn, Qtab_bar) || EQ (posn, Qtool_bar))
{
/* Change menu-bar to (menu-bar) as the event "position". */
POSN_SET_POSN (xevent_start (c), list1 (posn));
@@ -3974,6 +3976,7 @@ kbd_buffer_get_event (KBOARD **kbp,
if (used_mouse_menu
&& !EQ (event->ie.frame_or_window, event->ie.arg)
&& (event->kind == MENU_BAR_EVENT
+ || event->kind == TAB_BAR_EVENT
|| event->kind == TOOL_BAR_EVENT))
*used_mouse_menu = true;
#endif
@@ -5012,7 +5015,7 @@ make_lispy_position (struct frame *f, Lisp_Object x, Lisp_Object y,
int xret = 0, yret = 0;
/* The window or frame under frame pixel coordinates (x,y) */
Lisp_Object window_or_frame = f
- ? window_from_coordinates (f, XFIXNUM (x), XFIXNUM (y), &part, 0)
+ ? window_from_coordinates (f, XFIXNUM (x), XFIXNUM (y), &part, 0, 0)
: Qnil;
if (WINDOWP (window_or_frame))
@@ -5036,17 +5039,21 @@ make_lispy_position (struct frame *f, Lisp_Object x, Lisp_Object y,
if (part == ON_TEXT)
{
xret = XFIXNUM (x) - window_box_left (w, TEXT_AREA);
- yret = wy - WINDOW_HEADER_LINE_HEIGHT (w);
+ yret = wy - WINDOW_TAB_LINE_HEIGHT (w) - WINDOW_HEADER_LINE_HEIGHT (w);
}
/* For mode line and header line clicks, return X, Y relative to
the left window edge. Use mode_line_string to look for a
string on the click position. */
- else if (part == ON_MODE_LINE || part == ON_HEADER_LINE)
+ else if (part == ON_MODE_LINE || part == ON_TAB_LINE
+ || part == ON_HEADER_LINE)
{
Lisp_Object string;
ptrdiff_t charpos;
- posn = (part == ON_MODE_LINE) ? Qmode_line : Qheader_line;
+ posn = (part == ON_MODE_LINE ? Qmode_line
+ : (part == ON_TAB_LINE ? Qtab_line
+ : Qheader_line));
+
/* Note that mode_line_string takes COL, ROW as pixels and
converts them to characters. */
col = wx;
@@ -5075,7 +5082,7 @@ make_lispy_position (struct frame *f, Lisp_Object x, Lisp_Object y,
if (STRINGP (string))
string_info = Fcons (string, make_fixnum (charpos));
xret = wx;
- yret = wy - WINDOW_HEADER_LINE_HEIGHT (w);
+ yret = wy - WINDOW_TAB_LINE_HEIGHT (w) - WINDOW_HEADER_LINE_HEIGHT (w);
}
else if (part == ON_LEFT_FRINGE)
{
@@ -5085,7 +5092,7 @@ make_lispy_position (struct frame *f, Lisp_Object x, Lisp_Object y,
dx = wx
- (WINDOW_HAS_FRINGES_OUTSIDE_MARGINS (w)
? 0 : window_box_width (w, LEFT_MARGIN_AREA));
- dy = yret = wy - WINDOW_HEADER_LINE_HEIGHT (w);
+ dy = yret = wy - WINDOW_TAB_LINE_HEIGHT (w) - WINDOW_HEADER_LINE_HEIGHT (w);
}
else if (part == ON_RIGHT_FRINGE)
{
@@ -5098,7 +5105,7 @@ make_lispy_position (struct frame *f, Lisp_Object x, Lisp_Object y,
- (WINDOW_HAS_FRINGES_OUTSIDE_MARGINS (w)
? window_box_width (w, RIGHT_MARGIN_AREA)
: 0);
- dy = yret = wy - WINDOW_HEADER_LINE_HEIGHT (w);
+ dy = yret = wy - WINDOW_TAB_LINE_HEIGHT (w) - WINDOW_HEADER_LINE_HEIGHT (w);
}
else if (part == ON_VERTICAL_BORDER)
{
@@ -5953,6 +5960,16 @@ make_lispy_event (struct input_event *event)
/* Make an event (select-window (WINDOW)). */
return list2 (Qselect_window, list1 (event->frame_or_window));
+ case TAB_BAR_EVENT:
+ if (EQ (event->arg, event->frame_or_window))
+ /* This is the prefix key. We translate this to
+ `(tab_bar)' because the code in keyboard.c for tab bar
+ events, which we use, relies on this. */
+ return list1 (Qtab_bar);
+ else if (SYMBOLP (event->arg))
+ return apply_modifiers (event->modifiers, event->arg);
+ return event->arg;
+
case TOOL_BAR_EVENT:
if (EQ (event->arg, event->frame_or_window))
/* This is the prefix key. We translate this to
@@ -6730,6 +6747,7 @@ lucid_event_type_list_p (Lisp_Object object)
if (EQ (XCAR (object), Qhelp_echo)
|| EQ (XCAR (object), Qvertical_line)
|| EQ (XCAR (object), Qmode_line)
+ || EQ (XCAR (object), Qtab_line)
|| EQ (XCAR (object), Qheader_line))
return 0;
@@ -7891,6 +7909,389 @@ parse_menu_item (Lisp_Object item, int inmenubar)
/***********************************************************************
+ Tab-bars
+ ***********************************************************************/
+
+/* A vector holding tab bar items while they are parsed in function
+ tab_bar_items. Each item occupies TAB_BAR_ITEM_NSCLOTS elements
+ in the vector. */
+
+static Lisp_Object tab_bar_items_vector;
+
+/* A vector holding the result of parse_tab_bar_item. Layout is like
+ the one for a single item in tab_bar_items_vector. */
+
+static Lisp_Object tab_bar_item_properties;
+
+/* Next free index in tab_bar_items_vector. */
+
+static int ntab_bar_items;
+
+/* Function prototypes. */
+
+static void init_tab_bar_items (Lisp_Object);
+static void process_tab_bar_item (Lisp_Object, Lisp_Object, Lisp_Object,
+ void *);
+static bool parse_tab_bar_item (Lisp_Object, Lisp_Object);
+static void append_tab_bar_item (void);
+
+
+/* Return a vector of tab bar items for keymaps currently in effect.
+ Reuse vector REUSE if non-nil. Return in *NITEMS the number of
+ tab bar items found. */
+
+Lisp_Object
+tab_bar_items (Lisp_Object reuse, int *nitems)
+{
+ Lisp_Object *maps;
+ Lisp_Object mapsbuf[3];
+ ptrdiff_t nmaps, i;
+ Lisp_Object oquit;
+ Lisp_Object *tmaps;
+ USE_SAFE_ALLOCA;
+
+ *nitems = 0;
+
+ /* In order to build the menus, we need to call the keymap
+ accessors. They all call maybe_quit. But this function is called
+ during redisplay, during which a quit is fatal. So inhibit
+ quitting while building the menus. We do this instead of
+ specbind because (1) errors will clear it anyway and (2) this
+ avoids risk of specpdl overflow. */
+ oquit = Vinhibit_quit;
+ Vinhibit_quit = Qt;
+
+ /* Initialize tab_bar_items_vector and protect it from GC. */
+ init_tab_bar_items (reuse);
+
+ /* Build list of keymaps in maps. Set nmaps to the number of maps
+ to process. */
+
+ /* Should overriding-terminal-local-map and overriding-local-map apply? */
+ if (!NILP (Voverriding_local_map_menu_flag)
+ && !NILP (Voverriding_local_map))
+ {
+ /* Yes, use them (if non-nil) as well as the global map. */
+ maps = mapsbuf;
+ nmaps = 0;
+ if (!NILP (KVAR (current_kboard, Voverriding_terminal_local_map)))
+ maps[nmaps++] = KVAR (current_kboard, Voverriding_terminal_local_map);
+ if (!NILP (Voverriding_local_map))
+ maps[nmaps++] = Voverriding_local_map;
+ }
+ else
+ {
+ /* No, so use major and minor mode keymaps and keymap property.
+ Note that tab-bar bindings in the local-map and keymap
+ properties may not work reliably, as they are only
+ recognized when the tab-bar (or mode-line) is updated,
+ which does not normally happen after every command. */
+ ptrdiff_t nminor = current_minor_maps (NULL, &tmaps);
+ SAFE_NALLOCA (maps, 1, nminor + 4);
+ nmaps = 0;
+ Lisp_Object tem = KVAR (current_kboard, Voverriding_terminal_local_map);
+ if (!NILP (tem) && !NILP (Voverriding_local_map_menu_flag))
+ maps[nmaps++] = tem;
+ if (tem = get_local_map (PT, current_buffer, Qkeymap), !NILP (tem))
+ maps[nmaps++] = tem;
+ if (nminor != 0)
+ {
+ memcpy (maps + nmaps, tmaps, nminor * sizeof (maps[0]));
+ nmaps += nminor;
+ }
+ maps[nmaps++] = get_local_map (PT, current_buffer, Qlocal_map);
+ }
+
+ /* Add global keymap at the end. */
+ maps[nmaps++] = current_global_map;
+
+ /* Process maps in reverse order and look up in each map the prefix
+ key `tab-bar'. */
+ for (i = nmaps - 1; i >= 0; --i)
+ if (!NILP (maps[i]))
+ {
+ Lisp_Object keymap;
+
+ keymap = get_keymap (access_keymap (maps[i], Qtab_bar, 1, 0, 1), 0, 1);
+ if (CONSP (keymap))
+ map_keymap (keymap, process_tab_bar_item, Qnil, NULL, 1);
+ }
+
+ Vinhibit_quit = oquit;
+ *nitems = ntab_bar_items / TAB_BAR_ITEM_NSLOTS;
+ SAFE_FREE ();
+ return tab_bar_items_vector;
+}
+
+
+/* Process the definition of KEY which is DEF. */
+
+static void
+process_tab_bar_item (Lisp_Object key, Lisp_Object def, Lisp_Object data, void *args)
+{
+ int i;
+
+ if (EQ (def, Qundefined))
+ {
+ /* If a map has an explicit `undefined' as definition,
+ discard any previously made item. */
+ for (i = 0; i < ntab_bar_items; i += TAB_BAR_ITEM_NSLOTS)
+ {
+ Lisp_Object *v = XVECTOR (tab_bar_items_vector)->contents + i;
+
+ if (EQ (key, v[TAB_BAR_ITEM_KEY]))
+ {
+ if (ntab_bar_items > i + TAB_BAR_ITEM_NSLOTS)
+ memmove (v, v + TAB_BAR_ITEM_NSLOTS,
+ ((ntab_bar_items - i - TAB_BAR_ITEM_NSLOTS)
+ * word_size));
+ ntab_bar_items -= TAB_BAR_ITEM_NSLOTS;
+ break;
+ }
+ }
+ }
+ else if (parse_tab_bar_item (key, def))
+ /* Append a new tab bar item to tab_bar_items_vector. Accept
+ more than one definition for the same key. */
+ append_tab_bar_item ();
+}
+
+/* Access slot with index IDX of vector tab_bar_item_properties. */
+#define PROP(IDX) AREF (tab_bar_item_properties, (IDX))
+static void
+set_prop_tab_bar (ptrdiff_t idx, Lisp_Object val)
+{
+ ASET (tab_bar_item_properties, idx, val);
+}
+
+
+/* Parse a tab bar item specification ITEM for key KEY and return the
+ result in tab_bar_item_properties. Value is false if ITEM is
+ invalid.
+
+ ITEM is a list `(menu-item CAPTION BINDING PROPS...)'.
+
+ CAPTION is the caption of the item, If it's not a string, it is
+ evaluated to get a string.
+
+ BINDING is the tab bar item's binding. Tab-bar items with keymaps
+ as binding are currently ignored.
+
+ The following properties are recognized:
+
+ - `:enable FORM'.
+
+ FORM is evaluated and specifies whether the tab bar item is
+ enabled or disabled.
+
+ - `:visible FORM'
+
+ FORM is evaluated and specifies whether the tab bar item is visible.
+
+ - `:filter FUNCTION'
+
+ FUNCTION is invoked with one parameter `(quote BINDING)'. Its
+ result is stored as the new binding.
+
+ - `:button (TYPE SELECTED)'
+
+ TYPE must be one of `:radio' or `:toggle'. SELECTED is evaluated
+ and specifies whether the button is selected (pressed) or not.
+
+ - `:image IMAGES'
+
+ IMAGES is either a single image specification or a vector of four
+ image specifications. See enum tab_bar_item_images.
+
+ - `:help HELP-STRING'.
+
+ Gives a help string to display for the tab bar item.
+
+ - `:label LABEL-STRING'.
+
+ A text label to show with the tab bar button if labels are enabled. */
+
+static bool
+parse_tab_bar_item (Lisp_Object key, Lisp_Object item)
+{
+ Lisp_Object filter = Qnil;
+ Lisp_Object caption;
+ int i;
+
+ /* Definition looks like `(menu-item CAPTION BINDING PROPS...)'.
+ Rule out items that aren't lists, don't start with
+ `menu-item' or whose rest following `tab-bar-item' is not a
+ list. */
+ if (!CONSP (item))
+ return 0;
+
+ /* As an exception, allow old-style menu separators. */
+ if (STRINGP (XCAR (item)))
+ item = list1 (XCAR (item));
+ else if (!EQ (XCAR (item), Qmenu_item)
+ || (item = XCDR (item), !CONSP (item)))
+ return 0;
+
+ /* Create tab_bar_item_properties vector if necessary. Reset it to
+ defaults. */
+ if (VECTORP (tab_bar_item_properties))
+ {
+ for (i = 0; i < TAB_BAR_ITEM_NSLOTS; ++i)
+ set_prop_tab_bar (i, Qnil);
+ }
+ else
+ tab_bar_item_properties = make_nil_vector (TAB_BAR_ITEM_NSLOTS);
+
+ /* Set defaults. */
+ set_prop_tab_bar (TAB_BAR_ITEM_KEY, key);
+ set_prop_tab_bar (TAB_BAR_ITEM_ENABLED_P, Qt);
+
+ /* Get the caption of the item. If the caption is not a string,
+ evaluate it to get a string. If we don't get a string, skip this
+ item. */
+ caption = XCAR (item);
+ if (!STRINGP (caption))
+ {
+ caption = menu_item_eval_property (caption);
+ if (!STRINGP (caption))
+ return 0;
+ }
+ set_prop_tab_bar (TAB_BAR_ITEM_CAPTION, caption);
+
+ /* If the rest following the caption is not a list, the menu item is
+ either a separator, or invalid. */
+ item = XCDR (item);
+ if (!CONSP (item))
+ {
+ if (menu_separator_name_p (SSDATA (caption)))
+ {
+ set_prop_tab_bar (TAB_BAR_ITEM_ENABLED_P, Qnil);
+ set_prop_tab_bar (TAB_BAR_ITEM_SELECTED_P, Qnil);
+ set_prop_tab_bar (TAB_BAR_ITEM_CAPTION, Qnil);
+ return 1;
+ }
+ return 0;
+ }
+
+ /* Store the binding. */
+ set_prop_tab_bar (TAB_BAR_ITEM_BINDING, XCAR (item));
+ item = XCDR (item);
+
+ /* Ignore cached key binding, if any. */
+ if (CONSP (item) && CONSP (XCAR (item)))
+ item = XCDR (item);
+
+ /* Process the rest of the properties. */
+ for (; CONSP (item) && CONSP (XCDR (item)); item = XCDR (XCDR (item)))
+ {
+ Lisp_Object ikey, value;
+
+ ikey = XCAR (item);
+ value = XCAR (XCDR (item));
+
+ if (EQ (ikey, QCenable))
+ {
+ /* `:enable FORM'. */
+ if (!NILP (Venable_disabled_menus_and_buttons))
+ set_prop_tab_bar (TAB_BAR_ITEM_ENABLED_P, Qt);
+ else
+ set_prop_tab_bar (TAB_BAR_ITEM_ENABLED_P, value);
+ }
+ else if (EQ (ikey, QCvisible))
+ {
+ /* `:visible FORM'. If got a visible property and that
+ evaluates to nil then ignore this item. */
+ if (NILP (menu_item_eval_property (value)))
+ return 0;
+ }
+ else if (EQ (ikey, QChelp))
+ /* `:help HELP-STRING'. */
+ set_prop_tab_bar (TAB_BAR_ITEM_HELP, value);
+ else if (EQ (ikey, QCfilter))
+ /* ':filter FORM'. */
+ filter = value;
+ else if (EQ (ikey, QCbutton) && CONSP (value))
+ {
+ /* `:button (TYPE . SELECTED)'. */
+ Lisp_Object type, selected;
+
+ type = XCAR (value);
+ selected = XCDR (value);
+ if (EQ (type, QCtoggle) || EQ (type, QCradio))
+ {
+ set_prop_tab_bar (TAB_BAR_ITEM_SELECTED_P, selected);
+ }
+ }
+ }
+
+ /* If got a filter apply it on binding. */
+ if (!NILP (filter))
+ set_prop_tab_bar (TAB_BAR_ITEM_BINDING,
+ (menu_item_eval_property
+ (list2 (filter,
+ list2 (Qquote,
+ PROP (TAB_BAR_ITEM_BINDING))))));
+
+ /* See if the binding is a keymap. Give up if it is. */
+ if (CONSP (get_keymap (PROP (TAB_BAR_ITEM_BINDING), 0, 1)))
+ return 0;
+
+ /* Enable or disable selection of item. */
+ if (!EQ (PROP (TAB_BAR_ITEM_ENABLED_P), Qt))
+ set_prop_tab_bar (TAB_BAR_ITEM_ENABLED_P,
+ menu_item_eval_property (PROP (TAB_BAR_ITEM_ENABLED_P)));
+
+ /* Handle radio buttons or toggle boxes. */
+ if (!NILP (PROP (TAB_BAR_ITEM_SELECTED_P)))
+ set_prop_tab_bar (TAB_BAR_ITEM_SELECTED_P,
+ menu_item_eval_property (PROP (TAB_BAR_ITEM_SELECTED_P)));
+
+ return 1;
+
+#undef PROP
+}
+
+
+/* Initialize tab_bar_items_vector. REUSE, if non-nil, is a vector
+ that can be reused. */
+
+static void
+init_tab_bar_items (Lisp_Object reuse)
+{
+ if (VECTORP (reuse))
+ tab_bar_items_vector = reuse;
+ else
+ tab_bar_items_vector = make_nil_vector (64);
+ ntab_bar_items = 0;
+}
+
+
+/* Append parsed tab bar item properties from
+ tab_bar_item_properties */
+
+static void
+append_tab_bar_item (void)
+{
+ ptrdiff_t incr
+ = (ntab_bar_items
+ - (ASIZE (tab_bar_items_vector) - TAB_BAR_ITEM_NSLOTS));
+
+ /* Enlarge tab_bar_items_vector if necessary. */
+ if (incr > 0)
+ tab_bar_items_vector = larger_vector (tab_bar_items_vector, incr, -1);
+
+ /* Append entries from tab_bar_item_properties to the end of
+ tab_bar_items_vector. */
+ vcopy (tab_bar_items_vector, ntab_bar_items,
+ XVECTOR (tab_bar_item_properties)->contents, TAB_BAR_ITEM_NSLOTS);
+ ntab_bar_items += TAB_BAR_ITEM_NSLOTS;
+}
+
+
+
+
+
+/***********************************************************************
Tool-bars
***********************************************************************/
@@ -8402,6 +8803,7 @@ read_char_x_menu_prompt (Lisp_Object map,
use a real menu for mouse selection. */
if (EVENT_HAS_PARAMETERS (prev_event)
&& !EQ (XCAR (prev_event), Qmenu_bar)
+ && !EQ (XCAR (prev_event), Qtab_bar)
&& !EQ (XCAR (prev_event), Qtool_bar))
{
/* Display the menu and get the selection. */
@@ -9377,7 +9779,7 @@ read_key_sequence (Lisp_Object *keybuf, Lisp_Object prompt,
posn = POSN_POSN (xevent_start (key));
/* Handle menu-bar events:
insert the dummy prefix event `menu-bar'. */
- if (EQ (posn, Qmenu_bar) || EQ (posn, Qtool_bar))
+ if (EQ (posn, Qmenu_bar) || EQ (posn, Qtab_bar) || EQ (posn, Qtool_bar))
{
if (READ_KEY_ELTS - t <= 1)
error ("Key sequence too long");
@@ -10237,7 +10639,8 @@ On such systems, Emacs starts a subshell instead of suspending. */)
get_tty_size (fileno (CURTTY ()->input), &width, &height);
if (width != old_width || height != old_height)
change_frame_size (SELECTED_FRAME (), width,
- height - FRAME_MENU_BAR_LINES (SELECTED_FRAME ()),
+ height - FRAME_MENU_BAR_LINES (SELECTED_FRAME ())
+ - FRAME_TAB_BAR_LINES (SELECTED_FRAME ()),
0, 0, 0, 0);
run_hook (intern ("suspend-resume-hook"));
@@ -11057,6 +11460,11 @@ syms_of_keyboard (void)
staticpro (&item_properties);
item_properties = Qnil;
+ staticpro (&tab_bar_item_properties);
+ tab_bar_item_properties = Qnil;
+ staticpro (&tab_bar_items_vector);
+ tab_bar_items_vector = Qnil;
+
staticpro (&tool_bar_item_properties);
tool_bar_item_properties = Qnil;
staticpro (&tool_bar_items_vector);
@@ -11610,6 +12018,12 @@ See also `pre-command-hook'. */);
The elements of the list are event types that may have menu bar bindings. */);
Vmenu_bar_final_items = Qnil;
+ DEFVAR_LISP ("tab-bar-separator-image-expression", Vtab_bar_separator_image_expression,
+ doc: /* Expression evaluating to the image spec for a tab-bar separator.
+This is used internally by graphical displays that do not render
+tab-bar separators natively. Otherwise it is unused (e.g. on GTK). */);
+ Vtab_bar_separator_image_expression = Qnil;
+
DEFVAR_LISP ("tool-bar-separator-image-expression", Vtool_bar_separator_image_expression,
doc: /* Expression evaluating to the image spec for a tool-bar separator.
This is used internally by graphical displays that do not render
diff --git a/src/keymap.c b/src/keymap.c
index b1e09a92f20..da2786c8449 100644
--- a/src/keymap.c
+++ b/src/keymap.c
@@ -3663,7 +3663,8 @@ be preferred. */);
DEFSYM (Qmode_line, "mode-line");
staticpro (&Vmouse_events);
- Vmouse_events = pure_list (Qmenu_bar, Qtool_bar, Qheader_line, Qmode_line,
+ Vmouse_events = pure_list (Qmenu_bar, Qtab_bar, Qtool_bar,
+ Qtab_line, Qheader_line, Qmode_line,
intern_c_string ("mouse-1"),
intern_c_string ("mouse-2"),
intern_c_string ("mouse-3"),
diff --git a/src/lisp.h b/src/lisp.h
index bcb91b2decc..fe20add2d70 100644
--- a/src/lisp.h
+++ b/src/lisp.h
@@ -4391,6 +4391,7 @@ extern bool input_pending;
extern sigjmp_buf return_to_command_loop;
#endif
extern Lisp_Object menu_bar_items (Lisp_Object);
+extern Lisp_Object tab_bar_items (Lisp_Object, int *);
extern Lisp_Object tool_bar_items (Lisp_Object, int *);
extern void discard_mouse_events (void);
#ifdef USABLE_SIGIO
diff --git a/src/menu.c b/src/menu.c
index f67bdf05667..3d9cdb02115 100644
--- a/src/menu.c
+++ b/src/menu.c
@@ -1130,6 +1130,7 @@ x_popup_menu_1 (Lisp_Object position, Lisp_Object menu)
/* Decode the first argument: find the window and the coordinates. */
if (EQ (position, Qt)
|| (CONSP (position) && (EQ (XCAR (position), Qmenu_bar)
+ || EQ (XCAR (position), Qtab_bar)
|| EQ (XCAR (position), Qtool_bar))))
{
get_current_pos_p = 1;
@@ -1506,6 +1507,7 @@ for instance using the window manager, then this produces a quit and
/* Decode the first argument: find the window or frame to use. */
if (EQ (position, Qt)
|| (CONSP (position) && (EQ (XCAR (position), Qmenu_bar)
+ || EQ (XCAR (position), Qtab_bar)
|| EQ (XCAR (position), Qtool_bar))))
window = selected_window;
else if (CONSP (position))
diff --git a/src/msdos.c b/src/msdos.c
index d13f2304852..1192b37a0d4 100644
--- a/src/msdos.c
+++ b/src/msdos.c
@@ -2655,7 +2655,7 @@ dos_rawgetc (void)
static Lisp_Object last_mouse_window;
mouse_window = window_from_coordinates
- (SELECTED_FRAME (), mouse_last_x, mouse_last_y, 0, 0);
+ (SELECTED_FRAME (), mouse_last_x, mouse_last_y, 0, 0, 0);
/* A window will be selected only when it is not
selected now, and the last mouse movement event was
not in it. A minibuffer window will be selected iff
diff --git a/src/nsfns.m b/src/nsfns.m
index 2470c05c4b5..184fd71678e 100644
--- a/src/nsfns.m
+++ b/src/nsfns.m
@@ -610,6 +610,15 @@ ns_set_menu_bar_lines (struct frame *f, Lisp_Object value, Lisp_Object oldval)
}
+/* tabbar support */
+static void
+ns_set_tab_bar_lines (struct frame *f, Lisp_Object value, Lisp_Object oldval)
+{
+ /* Currently unimplemented. */
+ NSTRACE ("ns_set_tab_bar_lines");
+}
+
+
/* toolbar support */
static void
ns_set_tool_bar_lines (struct frame *f, Lisp_Object value, Lisp_Object oldval)
@@ -923,6 +932,7 @@ frame_parm_handler ns_frame_parm_handlers[] =
gui_set_vertical_scroll_bars, /* generic OK */
gui_set_horizontal_scroll_bars, /* generic OK */
gui_set_visibility, /* generic OK */
+ ns_set_tab_bar_lines,
ns_set_tool_bar_lines,
0, /* x_set_scroll_bar_foreground, will ignore (not possible on NS) */
0, /* x_set_scroll_bar_background, will ignore (not possible on NS) */
@@ -1297,7 +1307,7 @@ DEFUN ("x-create-frame", Fx_create_frame, Sx_create_frame,
RES_TYPE_STRING);
parms = get_geometry_from_preferences (dpyinfo, parms);
- window_prompting = gui_figure_window_size (f, parms, true,
+ window_prompting = gui_figure_window_size (f, parms, false, true,
&x_width, &x_height);
tem = gui_display_get_arg (dpyinfo, parms, Qunsplittable, 0, 0,
diff --git a/src/nsterm.m b/src/nsterm.m
index 42ef4dd0106..c8094d0ee37 100644
--- a/src/nsterm.m
+++ b/src/nsterm.m
@@ -6859,7 +6859,7 @@ not_in_argv (NSString *arg)
NSTRACE_MSG ("mouse_autoselect_window");
static Lisp_Object last_mouse_window;
Lisp_Object window
- = window_from_coordinates (emacsframe, pt.x, pt.y, 0, 0);
+ = window_from_coordinates (emacsframe, pt.x, pt.y, 0, 0, 0);
if (WINDOWP (window)
&& !EQ (window, last_mouse_window)
diff --git a/src/term.c b/src/term.c
index 5f70c7a3d4f..642010549bf 100644
--- a/src/term.c
+++ b/src/term.c
@@ -2341,7 +2341,8 @@ frame's terminal). */)
was suspended. */
get_tty_size (fileno (t->display_info.tty->input), &width, &height);
if (width != old_width || height != old_height)
- change_frame_size (f, width, height - FRAME_MENU_BAR_LINES (f),
+ change_frame_size (f, width, height - FRAME_MENU_BAR_LINES (f)
+ - FRAME_TAB_BAR_LINES (f),
0, 0, 0, 0);
SET_FRAME_VISIBLE (XFRAME (t->display_info.tty->top_frame), 1);
}
diff --git a/src/termhooks.h b/src/termhooks.h
index f1827128f19..4830a85d31b 100644
--- a/src/termhooks.h
+++ b/src/termhooks.h
@@ -194,6 +194,11 @@ enum event_kind
the help to show. */
HELP_EVENT,
+ /* An event from a tab-bar. Member `arg' of the input event
+ contains the tab-bar item selected. If `frame_or_window'
+ and `arg' are equal, this is a prefix event. */
+ TAB_BAR_EVENT,
+
/* An event from a tool-bar. Member `arg' of the input event
contains the tool-bar item selected. If `frame_or_window'
and `arg' are equal, this is a prefix event. */
@@ -624,6 +629,9 @@ struct terminal
Lisp_Object (*popup_dialog_hook) (struct frame *f, Lisp_Object header,
Lisp_Object contents);
+ /* This hook is called to change the frame's (internal) tab-bar. */
+ void (*change_tab_bar_height_hook) (struct frame *f, int height);
+
/* This hook is called to change the frame's (internal) tool-bar. */
void (*change_tool_bar_height_hook) (struct frame *f, int height);
diff --git a/src/w32fns.c b/src/w32fns.c
index 8bc5707bfa7..4ef075f715b 100644
--- a/src/w32fns.c
+++ b/src/w32fns.c
@@ -1773,6 +1773,94 @@ w32_set_menu_bar_lines (struct frame *f, Lisp_Object value, Lisp_Object oldval)
}
+/* Set the number of lines used for the tab bar of frame F to VALUE.
+ VALUE not an integer, or < 0 means set the lines to zero. OLDVAL
+ is the old number of tab bar lines. This function changes the
+ height of all windows on frame F to match the new tab bar height.
+ The frame's height doesn't change. */
+
+static void
+w32_set_tab_bar_lines (struct frame *f, Lisp_Object value, Lisp_Object oldval)
+{
+ int nlines;
+
+ /* Treat tab bars like menu bars. */
+ if (FRAME_MINIBUF_ONLY_P (f))
+ return;
+
+ /* Use VALUE only if an int >= 0. */
+ if (RANGED_FIXNUMP (0, value, INT_MAX))
+ nlines = XFIXNAT (value);
+ else
+ nlines = 0;
+
+ w32_change_tab_bar_height (f, nlines * FRAME_LINE_HEIGHT (f));
+}
+
+
+/* Set the pixel height of the tab bar of frame F to HEIGHT. */
+void
+w32_change_tab_bar_height (struct frame *f, int height)
+{
+ int unit = FRAME_LINE_HEIGHT (f);
+ int old_height = FRAME_TAB_BAR_HEIGHT (f);
+ int lines = (height + unit - 1) / unit;
+ Lisp_Object fullscreen;
+
+ /* Make sure we redisplay all windows in this frame. */
+ fset_redisplay (f);
+
+ /* Recalculate tab bar and frame text sizes. */
+ FRAME_TAB_BAR_HEIGHT (f) = height;
+ FRAME_TAB_BAR_LINES (f) = lines;
+ /* Store the `tab-bar-lines' and `height' frame parameters. */
+ store_frame_param (f, Qtab_bar_lines, make_fixnum (lines));
+ store_frame_param (f, Qheight, make_fixnum (FRAME_LINES (f)));
+
+ /* We also have to make sure that the internal border at the top of
+ the frame, below the menu bar or tab bar, is redrawn when the
+ tab bar disappears. This is so because the internal border is
+ below the tab bar if one is displayed, but is below the menu bar
+ if there isn't a tab bar. The tab bar draws into the area
+ below the menu bar. */
+ if (FRAME_W32_WINDOW (f) && FRAME_TAB_BAR_HEIGHT (f) == 0)
+ {
+ clear_frame (f);
+ clear_current_matrices (f);
+ }
+
+ if ((height < old_height) && WINDOWP (f->tab_bar_window))
+ clear_glyph_matrix (XWINDOW (f->tab_bar_window)->current_matrix);
+
+ /* Recalculate tabbar height. */
+ f->n_tab_bar_rows = 0;
+ if (old_height == 0
+ && (!f->after_make_frame
+ || NILP (frame_inhibit_implied_resize)
+ || (CONSP (frame_inhibit_implied_resize)
+ && NILP (Fmemq (Qtab_bar_lines, frame_inhibit_implied_resize)))))
+ f->tab_bar_redisplayed = f->tab_bar_resized = false;
+
+ adjust_frame_size (f, -1, -1,
+ ((!f->tab_bar_resized
+ && (NILP (fullscreen =
+ get_frame_param (f, Qfullscreen))
+ || EQ (fullscreen, Qfullwidth))) ? 1
+ : (old_height == 0 || height == 0) ? 2
+ : 4),
+ false, Qtab_bar_lines);
+
+ f->tab_bar_resized = f->tab_bar_redisplayed;
+
+ /* adjust_frame_size might not have done anything, garbage frame
+ here. */
+ adjust_frame_glyphs (f);
+ SET_FRAME_GARBAGED (f);
+ if (FRAME_W32_WINDOW (f))
+ w32_clear_under_internal_border (f);
+}
+
+
/* Set the number of lines used for the tool bar of frame F to VALUE.
VALUE not an integer, or < 0 means set the lines to zero. OLDVAL is
the old number of tool bar lines (and is unused). This function may
@@ -5989,6 +6077,11 @@ DEFUN ("x-create-frame", Fx_create_frame, Sx_create_frame,
/* No menu bar for child frames. */
store_frame_param (f, Qmenu_bar_lines, make_fixnum (0));
+ gui_default_parameter (f, parameters, Qtab_bar_lines,
+ NILP (Vtab_bar_mode)
+ ? make_fixnum (0) : make_fixnum (1),
+ NULL, NULL, RES_TYPE_NUMBER);
+
gui_default_parameter (f, parameters, Qtool_bar_lines,
NILP (Vtool_bar_mode)
? make_fixnum (0) : make_fixnum (1),
@@ -6018,7 +6111,7 @@ DEFUN ("x-create-frame", Fx_create_frame, Sx_create_frame,
f->output_data.w32->current_cursor = f->output_data.w32->nontext_cursor;
- window_prompting = gui_figure_window_size (f, parameters, true,
+ window_prompting = gui_figure_window_size (f, parameters, true, true,
&x_width, &x_height);
tem = gui_display_get_arg (dpyinfo, parameters, Qunsplittable, 0, 0,
@@ -6986,7 +7079,7 @@ w32_create_tip_frame (struct w32_display_info *dpyinfo, Lisp_Object parms)
f->output_data.w32->parent_desc = FRAME_DISPLAY_INFO (f)->root_window;
f->output_data.w32->explicit_parent = false;
- gui_figure_window_size (f, parms, true, &x_width, &x_height);
+ gui_figure_window_size (f, parms, true, true, &x_width, &x_height);
/* No fringes on tip frame. */
f->fringe_cols = 0;
@@ -8739,6 +8832,9 @@ and width values are in pixels.
`menu-bar-size' is a cons of the width and height of the menu bar of
FRAME.
+`tab-bar-size' is a cons of the width and height of the tab bar of
+ FRAME.
+
`tool-bar-external', if non-nil, means the tool bar is external (never
included in the inner edges of FRAME).
@@ -8761,6 +8857,7 @@ and width values are in pixels.
unsigned int external_border_width, external_border_height;
int title_bar_width = 0, title_bar_height = 0;
int single_menu_bar_height, wrapped_menu_bar_height, menu_bar_height;
+ int tab_bar_height = FRAME_TAB_BAR_HEIGHT (f);
int tool_bar_height = FRAME_TOOL_BAR_HEIGHT (f);
int internal_border_width = FRAME_INTERNAL_BORDER_WIDTH (f);
@@ -8834,6 +8931,13 @@ and width values are in pixels.
Fcons (make_fixnum
(menu_bar.rcBar.right - menu_bar.rcBar.left),
make_fixnum (menu_bar_height))),
+ Fcons (Qtab_bar_size,
+ Fcons (make_fixnum
+ (tab_bar_height
+ ? (right - left - 2 * external_border_width
+ - 2 * internal_border_width)
+ : 0),
+ make_fixnum (tab_bar_height))),
Fcons (Qtool_bar_external, Qnil),
Fcons (Qtool_bar_position, tool_bar_height ? Qtop : Qnil),
Fcons (Qtool_bar_size,
@@ -8925,6 +9029,7 @@ menu bar or tool bar of FRAME. */)
return list4 (make_fixnum (left + internal_border_width),
make_fixnum (top
+ + FRAME_TAB_BAR_HEIGHT (f)
+ FRAME_TOOL_BAR_HEIGHT (f)
+ internal_border_width),
make_fixnum (right - internal_border_width),
@@ -10212,6 +10317,7 @@ frame_parm_handler w32_frame_parm_handlers[] =
gui_set_vertical_scroll_bars,
gui_set_horizontal_scroll_bars,
gui_set_visibility,
+ w32_set_tab_bar_lines,
w32_set_tool_bar_lines,
0, /* x_set_scroll_bar_foreground, */
0, /* x_set_scroll_bar_background, */
diff --git a/src/w32inevt.c b/src/w32inevt.c
index fc1f90cd025..2b6979bda24 100644
--- a/src/w32inevt.c
+++ b/src/w32inevt.c
@@ -492,7 +492,7 @@ do_mouse_event (MOUSE_EVENT_RECORD *event,
if (!NILP (Vmouse_autoselect_window))
{
Lisp_Object mouse_window = window_from_coordinates (f, mx, my,
- 0, 0);
+ 0, 0, 0);
/* A window will be selected only when it is not
selected now, and the last mouse movement event was
not in it. A minibuffer window will be selected iff
diff --git a/src/w32reg.c b/src/w32reg.c
index 99b3973d708..f156c378f93 100644
--- a/src/w32reg.c
+++ b/src/w32reg.c
@@ -36,6 +36,8 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
"emacs.tooltip.attributeBackground:SystemInfoWindow\0" \
"emacs.tool-bar.attributeForeground:SystemButtonText\0" \
"emacs.tool-bar.attributeBackground:SystemButtonFace\0" \
+ "emacs.tab-bar.attributeForeground:SystemButtonText\0" \
+ "emacs.tab-bar.attributeBackground:SystemButtonFace\0" \
"emacs.menu.attributeForeground:SystemMenuText\0" \
"emacs.menu.attributeBackground:SystemMenu\0" \
"emacs.scroll-bar.attributeForeground:SystemScrollbar\0"
diff --git a/src/w32term.c b/src/w32term.c
index e5874f2d365..82256db1721 100644
--- a/src/w32term.c
+++ b/src/w32term.c
@@ -168,6 +168,8 @@ int w32_keyboard_codepage;
int w32_message_fd = -1;
#endif /* CYGWIN */
+static void w32_handle_tab_bar_click (struct frame *,
+ struct input_event *);
static void w32_handle_tool_bar_click (struct frame *,
struct input_event *);
static void w32_define_cursor (Window, Emacs_Cursor);
@@ -3604,6 +3606,29 @@ w32_mouse_position (struct frame **fp, int insist, Lisp_Object *bar_window,
/***********************************************************************
+ Tab-bars
+ ***********************************************************************/
+
+/* Handle mouse button event on the tab-bar of frame F, at
+ frame-relative coordinates X/Y. EVENT_TYPE is either ButtonPress
+ or ButtonRelease. */
+
+static void
+w32_handle_tab_bar_click (struct frame *f, struct input_event *button_event)
+{
+ int x = XFIXNAT (button_event->x);
+ int y = XFIXNAT (button_event->y);
+
+ if (button_event->modifiers & down_modifier)
+ handle_tab_bar_click (f, x, y, 1, 0);
+ else
+ handle_tab_bar_click (f, x, y, 0,
+ button_event->modifiers & ~up_modifier);
+}
+
+
+
+/***********************************************************************
Tool-bars
***********************************************************************/
@@ -4843,6 +4868,7 @@ w32_read_socket (struct terminal *terminal,
if (f && !FRAME_ICONIFIED_P (f))
{
if (!hlinfo->mouse_face_hidden && FIXNUMP (Vmouse_highlight)
+ && !EQ (f->tab_bar_window, hlinfo->mouse_face_window)
&& !EQ (f->tool_bar_window, hlinfo->mouse_face_window))
{
clear_mouse_face (hlinfo);
@@ -4868,6 +4894,7 @@ w32_read_socket (struct terminal *terminal,
if (f && !FRAME_ICONIFIED_P (f))
{
if (!hlinfo->mouse_face_hidden && FIXNUMP (Vmouse_highlight)
+ && !EQ (f->tab_bar_window, hlinfo->mouse_face_window)
&& !EQ (f->tool_bar_window, hlinfo->mouse_face_window))
{
clear_mouse_face (hlinfo);
@@ -4946,6 +4973,7 @@ w32_read_socket (struct terminal *terminal,
if (f && !FRAME_ICONIFIED_P (f))
{
if (!hlinfo->mouse_face_hidden && FIXNUMP (Vmouse_highlight)
+ && !EQ (f->tab_bar_window, hlinfo->mouse_face_window)
&& !EQ (f->tool_bar_window, hlinfo->mouse_face_window))
{
clear_mouse_face (hlinfo);
@@ -4998,7 +5026,7 @@ w32_read_socket (struct terminal *terminal,
{
static Lisp_Object last_mouse_window;
Lisp_Object window = window_from_coordinates
- (f, LOWORD (msg.msg.lParam), HIWORD (msg.msg.lParam), 0, 0);
+ (f, LOWORD (msg.msg.lParam), HIWORD (msg.msg.lParam), 0, 0, 0);
/* Window will be selected only when it is not
selected now and last mouse movement event was
@@ -5051,6 +5079,7 @@ w32_read_socket (struct terminal *terminal,
{
/* If we decide we want to generate an event to be seen
by the rest of Emacs, we put it here. */
+ bool tab_bar_p = 0;
bool tool_bar_p = 0;
int button = 0;
int up = 0;
@@ -5060,6 +5089,31 @@ w32_read_socket (struct terminal *terminal,
{
w32_construct_mouse_click (&inev, &msg, f);
+ /* Is this in the tab-bar? */
+ if (WINDOWP (f->tab_bar_window)
+ && WINDOW_TOTAL_LINES (XWINDOW (f->tab_bar_window)))
+ {
+ Lisp_Object window;
+ int x = XFIXNAT (inev.x);
+ int y = XFIXNAT (inev.y);
+
+ window = window_from_coordinates (f, x, y, 0, 1, 1);
+
+ if (EQ (window, f->tab_bar_window))
+ {
+ w32_handle_tab_bar_click (f, &inev);
+ tab_bar_p = 1;
+ }
+ }
+
+ if (tab_bar_p
+ || (dpyinfo->w32_focus_frame
+ && f != dpyinfo->w32_focus_frame
+ /* This does not help when the click happens in
+ a grand-parent frame. */
+ && !frame_ancestor_p (f, dpyinfo->w32_focus_frame)))
+ inev.kind = NO_EVENT;
+
/* Is this in the tool-bar? */
if (WINDOWP (f->tool_bar_window)
&& WINDOW_TOTAL_LINES (XWINDOW (f->tool_bar_window)))
@@ -5068,7 +5122,7 @@ w32_read_socket (struct terminal *terminal,
int x = XFIXNAT (inev.x);
int y = XFIXNAT (inev.y);
- window = window_from_coordinates (f, x, y, 0, 1);
+ window = window_from_coordinates (f, x, y, 0, 1, 1);
if (EQ (window, f->tool_bar_window))
{
@@ -5104,6 +5158,8 @@ w32_read_socket (struct terminal *terminal,
if (f != 0)
{
f->mouse_moved = false;
+ if (!tab_bar_p)
+ f->last_tab_bar_item = -1;
if (!tool_bar_p)
f->last_tool_bar_item = -1;
}
@@ -5127,6 +5183,7 @@ w32_read_socket (struct terminal *terminal,
event; any subsequent mouse-movement Emacs events
should reflect only motion after the ButtonPress. */
f->mouse_moved = false;
+ f->last_tab_bar_item = -1;
f->last_tool_bar_item = -1;
dpyinfo->last_mouse_frame = f;
}
@@ -5140,6 +5197,7 @@ w32_read_socket (struct terminal *terminal,
{
w32_construct_mouse_wheel (&inev, &msg, f1);
f1->mouse_moved = false;
+ f1->last_tab_bar_item = -1;
f1->last_tool_bar_item = -1;
dpyinfo->last_mouse_frame = f1;
}
@@ -5936,7 +5994,8 @@ w32_draw_window_cursor (struct window *w, struct glyph_row *glyph_row,
= (WINDOW_TO_FRAME_PIXEL_Y (w, w->phys_cursor.y)
+ glyph_row->ascent - w->phys_cursor_ascent);
w32_system_caret_window = w;
- w32_system_caret_hdr_height = WINDOW_HEADER_LINE_HEIGHT (w);
+ w32_system_caret_hdr_height = WINDOW_TAB_LINE_HEIGHT (w)
+ + WINDOW_HEADER_LINE_HEIGHT (w);
w32_system_caret_mode_height = WINDOW_MODE_LINE_HEIGHT (w);
PostMessage (hwnd, WM_IME_STARTCOMPOSITION, 0, 0);
@@ -7192,6 +7251,7 @@ w32_create_terminal (struct w32_display_info *dpyinfo)
terminal->menu_show_hook = w32_menu_show;
terminal->activate_menubar_hook = w32_activate_menubar;
terminal->popup_dialog_hook = w32_popup_dialog;
+ terminal->change_tab_bar_height_hook = w32_change_tab_bar_height;
terminal->change_tool_bar_height_hook = w32_change_tool_bar_height;
terminal->set_vertical_scroll_bar_hook = w32_set_vertical_scroll_bar;
terminal->set_horizontal_scroll_bar_hook = w32_set_horizontal_scroll_bar;
diff --git a/src/w32term.h b/src/w32term.h
index 6133e100c17..378f274d7ed 100644
--- a/src/w32term.h
+++ b/src/w32term.h
@@ -233,6 +233,7 @@ extern void w32_real_positions (struct frame *f, int *xptr, int *yptr);
extern void w32_clear_under_internal_border (struct frame *);
+extern void w32_change_tab_bar_height (struct frame *, int);
extern void w32_change_tool_bar_height (struct frame *, int);
extern void w32_implicitly_set_name (struct frame *, Lisp_Object, Lisp_Object);
extern void w32_set_scroll_bar_default_width (struct frame *);
diff --git a/src/window.c b/src/window.c
index 321b3e01b79..95197985e84 100644
--- a/src/window.c
+++ b/src/window.c
@@ -1002,6 +1002,7 @@ static int
window_body_height (struct window *w, bool pixelwise)
{
int height = (w->pixel_height
+ - WINDOW_TAB_LINE_HEIGHT (w)
- WINDOW_HEADER_LINE_HEIGHT (w)
- (WINDOW_HAS_HORIZONTAL_SCROLL_BAR (w)
? WINDOW_SCROLL_BAR_AREA_HEIGHT (w)
@@ -1131,6 +1132,15 @@ WINDOW must be a live window and defaults to the selected one. */)
return (make_fixnum (WINDOW_HEADER_LINE_HEIGHT (decode_live_window (window))));
}
+DEFUN ("window-tab-line-height", Fwindow_tab_line_height,
+ Swindow_tab_line_height, 0, 1, 0,
+ doc: /* Return the height in pixels of WINDOW's tab-line.
+WINDOW must be a live window and defaults to the selected one. */)
+ (Lisp_Object window)
+{
+ return (make_fixnum (WINDOW_TAB_LINE_HEIGHT (decode_live_window (window))));
+}
+
DEFUN ("window-right-divider-width", Fwindow_right_divider_width,
Swindow_right_divider_width, 0, 1, 0,
doc: /* Return the width in pixels of WINDOW's right divider.
@@ -1249,7 +1259,8 @@ end-trigger value is reset to nil. */)
if it is on the border between the window and its right sibling,
return ON_VERTICAL_BORDER;
if it is on a scroll bar, return ON_SCROLL_BAR;
- if it is on the window's top line, return ON_HEADER_LINE;
+ if it is on the window's top line, return ON_TAB_LINE;
+ if it is on the window's header line, return ON_HEADER_LINE;
if it is in left or right fringe of the window,
return ON_LEFT_FRINGE or ON_RIGHT_FRINGE;
if it is in the marginal area to the left/right of the window,
@@ -1299,15 +1310,19 @@ coordinates_in_window (register struct window *w, int x, int y)
- CURRENT_MODE_LINE_HEIGHT (w)
- WINDOW_BOTTOM_DIVIDER_WIDTH (w))))
return ON_HORIZONTAL_SCROLL_BAR;
- /* On the mode or header line? */
+ /* On the mode or header/tab line? */
else if ((window_wants_mode_line (w)
&& y >= (bottom_y
- CURRENT_MODE_LINE_HEIGHT (w)
- WINDOW_BOTTOM_DIVIDER_WIDTH (w))
&& y <= bottom_y - WINDOW_BOTTOM_DIVIDER_WIDTH (w)
&& (part = ON_MODE_LINE))
+ || (window_wants_tab_line (w)
+ && y < top_y + CURRENT_TAB_LINE_HEIGHT (w)
+ && (part = ON_TAB_LINE))
|| (window_wants_header_line (w)
- && y < top_y + CURRENT_HEADER_LINE_HEIGHT (w)
+ && y < top_y + CURRENT_TAB_LINE_HEIGHT (w)
+ + CURRENT_HEADER_LINE_HEIGHT (w)
&& (part = ON_HEADER_LINE)))
{
/* If it's under/over the scroll bar portion of the mode/header
@@ -1407,6 +1422,7 @@ window_relative_x_coord (struct window *w, enum window_part part, int x)
case ON_TEXT:
return x - window_box_left (w, TEXT_AREA);
+ case ON_TAB_LINE:
case ON_HEADER_LINE:
case ON_MODE_LINE:
case ON_LEFT_FRINGE:
@@ -1457,6 +1473,7 @@ If they are in the bottom divider of WINDOW, `bottom-divider' is returned.
If they are in the right divider of WINDOW, `right-divider' is returned.
If they are in the mode line of WINDOW, `mode-line' is returned.
If they are in the header line of WINDOW, `header-line' is returned.
+If they are in the tab line of WINDOW, `tab-line' is returned.
If they are in the left fringe of WINDOW, `left-fringe' is returned.
If they are in the right fringe of WINDOW, `right-fringe' is returned.
If they are on the border between WINDOW and its right sibling,
@@ -1502,6 +1519,9 @@ If they are in the windows's left or right marginal areas, `left-margin'\n\
case ON_HEADER_LINE:
return Qheader_line;
+ case ON_TAB_LINE:
+ return Qtab_line;
+
case ON_LEFT_FRINGE:
return Qleft_fringe;
@@ -1585,7 +1605,7 @@ check_window_containing (struct window *w, void *user_data)
Lisp_Object
window_from_coordinates (struct frame *f, int x, int y,
- enum window_part *part, bool tool_bar_p)
+ enum window_part *part, bool tab_bar_p, bool tool_bar_p)
{
Lisp_Object window;
struct check_window_data cw;
@@ -1598,6 +1618,21 @@ window_from_coordinates (struct frame *f, int x, int y,
cw.window = &window, cw.x = x, cw.y = y; cw.part = part;
foreach_window (f, check_window_containing, &cw);
+#if defined (HAVE_WINDOW_SYSTEM)
+ /* If not found above, see if it's in the tab bar window, if a tab
+ bar exists. */
+ if (NILP (window)
+ && tab_bar_p
+ && WINDOWP (f->tab_bar_window)
+ && WINDOW_TOTAL_LINES (XWINDOW (f->tab_bar_window)) > 0
+ && (coordinates_in_window (XWINDOW (f->tab_bar_window), x, y)
+ != ON_NOTHING))
+ {
+ *part = ON_TEXT;
+ window = f->tab_bar_window;
+ }
+#endif
+
#if defined (HAVE_WINDOW_SYSTEM) && ! defined (HAVE_EXT_TOOL_BAR)
/* If not found above, see if it's in the tool bar window, if a tool
bar exists. */
@@ -1633,7 +1668,7 @@ column 0. */)
+ FRAME_INTERNAL_BORDER_WIDTH (f)),
(FRAME_PIXEL_Y_FROM_CANON_Y (f, y)
+ FRAME_INTERNAL_BORDER_WIDTH (f)),
- 0, false);
+ 0, false, false);
}
DEFUN ("window-point", Fwindow_point, Swindow_point, 0, 1, 0,
@@ -1945,6 +1980,14 @@ Return nil if window display is not up-to-date. In that case, use
goto found_row;
}
+ if (EQ (line, Qtab_line))
+ {
+ if (!window_wants_tab_line (w))
+ return Qnil;
+ row = MATRIX_TAB_LINE_ROW (w->current_matrix);
+ return row->enabled_p ? list4i (row->height, 0, 0, 0) : Qnil;
+ }
+
if (EQ (line, Qheader_line))
{
if (!window_wants_header_line (w))
@@ -1959,7 +2002,8 @@ Return nil if window display is not up-to-date. In that case, use
return (row->enabled_p ?
list4i (row->height,
0, /* not accurate */
- (WINDOW_HEADER_LINE_HEIGHT (w)
+ (WINDOW_TAB_LINE_HEIGHT (w)
+ + WINDOW_HEADER_LINE_HEIGHT (w)
+ window_text_bottom_y (w)),
0)
: Qnil);
@@ -2045,8 +2089,9 @@ though when run from an idle timer with a delay of zero seconds. */)
int max_y = NILP (body) ? WINDOW_PIXEL_HEIGHT (w) : window_text_bottom_y (w);
Lisp_Object rows = Qnil;
int window_width = NILP (body) ? w->pixel_width : window_body_width (w, true);
+ int tab_line_height = WINDOW_TAB_LINE_HEIGHT (w);
int header_line_height = WINDOW_HEADER_LINE_HEIGHT (w);
- int subtract = NILP (body) ? 0 : header_line_height;
+ int subtract = NILP (body) ? 0 : (tab_line_height + header_line_height);
bool invert = !NILP (inverse);
bool left_flag = !NILP (left);
@@ -4246,7 +4291,7 @@ make_window (void)
non-Lisp data, so do it only for slots which should not be zero. */
w->nrows_scale_factor = w->ncols_scale_factor = 1;
w->left_fringe_width = w->right_fringe_width = -1;
- w->mode_line_height = w->header_line_height = -1;
+ w->mode_line_height = w->tab_line_height = w->header_line_height = -1;
#ifdef HAVE_WINDOW_SYSTEM
w->phys_cursor_type = NO_CURSOR;
w->phys_cursor_width = -1;
@@ -4772,7 +4817,7 @@ Third argument SIDE nil (or `below') specifies that the new window shall
be located below WINDOW. SIDE `above' means the new window shall be
located above WINDOW. In both cases PIXEL-SIZE specifies the pixel
height of the new window including space reserved for the mode and/or
-header line.
+header/tab line.
SIDE t (or `right') specifies that the new window shall be located on
the right side of WINDOW. SIDE `left' means the new window shall be
@@ -5350,6 +5395,41 @@ window_wants_header_line (struct window *w)
: 0);
}
+
+/**
+ * window_wants_tab_line:
+ *
+ * Return 1 if window W wants a tab line and is high enough to
+ * accommodate it, 0 otherwise.
+ *
+ * W wants a tab line if it's a leaf window and neither a minibuffer
+ * nor a pseudo window. Moreover, its 'window-mode-line-format'
+ * parameter must not be 'none' and either that parameter or W's
+ * buffer's 'mode-line-format' value must be non-nil. Finally, W must
+ * be higher than its frame's canonical character height and be able
+ * to accommodate a mode line and a header line too if necessary (the
+ * mode line and a header line prevail).
+ */
+bool
+window_wants_tab_line (struct window *w)
+{
+ Lisp_Object window_tab_line_format =
+ window_parameter (w, Qtab_line_format);
+
+ return ((WINDOW_LEAF_P (w)
+ && !MINI_WINDOW_P (w)
+ && !WINDOW_PSEUDO_P (w)
+ && !EQ (window_tab_line_format, Qnone)
+ && (!NILP (window_tab_line_format)
+ || !NILP (BVAR (XBUFFER (WINDOW_BUFFER (w)), tab_line_format)))
+ && (WINDOW_PIXEL_HEIGHT (w)
+ > (((window_wants_mode_line (w) ? 1 : 0)
+ + (window_wants_header_line (w) ? 1 : 0)
+ + 1) * WINDOW_FRAME_LINE_HEIGHT (w))))
+ ? 1
+ : 0);
+}
+
/* Return number of lines of text in window W, not counting the mode
line and header line, if any. Do NOT use this for windows on GUI
frames; use window_body_height instead. This function is only for
@@ -5366,6 +5446,9 @@ window_internal_height (struct window *w)
if (window_wants_header_line (w))
--ht;
+ if (window_wants_tab_line (w))
+ --ht;
+
return ht;
}
@@ -5726,8 +5809,9 @@ window_scroll_pixel_based (Lisp_Object window, int n, bool whole, bool noerror)
move_it_to (&it, PT, -1, -1, -1, MOVE_TO_POS);
if (IT_CHARPOS (it) == PT
&& it.current_y >= this_scroll_margin
- && it.current_y <= last_y - WINDOW_HEADER_LINE_HEIGHT (w)
- && (NILP (Vscroll_preserve_screen_position)
+ && it.current_y <= last_y - WINDOW_TAB_LINE_HEIGHT (w)
+ - WINDOW_HEADER_LINE_HEIGHT (w)
+ && (NILP (Vscroll_preserve_screen_position)
|| EQ (Vscroll_preserve_screen_position, Qt)))
/* We found PT at a legitimate height. Leave it alone. */
;
@@ -5742,7 +5826,8 @@ window_scroll_pixel_based (Lisp_Object window, int n, bool whole, bool noerror)
is necessary because we set it.current_y to 0, above. */
move_it_to (&it, -1,
window_scroll_pixel_based_preserve_x,
- goal_y - WINDOW_HEADER_LINE_HEIGHT (w),
+ goal_y - WINDOW_TAB_LINE_HEIGHT (w)
+ - WINDOW_HEADER_LINE_HEIGHT (w),
-1, MOVE_TO_Y | MOVE_TO_X);
}
@@ -5778,8 +5863,9 @@ window_scroll_pixel_based (Lisp_Object window, int n, bool whole, bool noerror)
/* We subtract WINDOW_HEADER_LINE_HEIGHT because
it.y is relative to the bottom of the header
line, see above. */
- (it.last_visible_y - WINDOW_HEADER_LINE_HEIGHT (w)
- - partial_line_height (&it) - this_scroll_margin - 1),
+ (it.last_visible_y - WINDOW_TAB_LINE_HEIGHT (w)
+ - WINDOW_HEADER_LINE_HEIGHT (w)
+ - partial_line_height (&it) - this_scroll_margin - 1),
-1,
MOVE_TO_POS | MOVE_TO_Y);
@@ -5817,13 +5903,15 @@ window_scroll_pixel_based (Lisp_Object window, int n, bool whole, bool noerror)
if (it.what == IT_EOB)
partial_p =
it.current_y + it.ascent + it.descent
- > it.last_visible_y - this_scroll_margin - WINDOW_HEADER_LINE_HEIGHT (w);
+ > it.last_visible_y - this_scroll_margin
+ - WINDOW_TAB_LINE_HEIGHT (w) - WINDOW_HEADER_LINE_HEIGHT (w);
else
{
move_it_by_lines (&it, 1);
partial_p =
it.current_y
- > it.last_visible_y - this_scroll_margin - WINDOW_HEADER_LINE_HEIGHT (w);
+ > it.last_visible_y - this_scroll_margin
+ - WINDOW_TAB_LINE_HEIGHT (w) - WINDOW_HEADER_LINE_HEIGHT (w);
}
if (charpos == PT && !partial_p
@@ -6370,6 +6458,9 @@ and redisplay normally--don't erase and redraw the frame. */)
/* Invalidate pixel data calculated for all compositions. */
for (i = 0; i < n_compositions; i++)
composition_table[i]->font = NULL;
+#if defined (HAVE_WINDOW_SYSTEM)
+ WINDOW_XFRAME (w)->minimize_tab_bar_window_p = 1;
+#endif
#if defined (HAVE_WINDOW_SYSTEM) && ! defined (HAVE_EXT_TOOL_BAR)
WINDOW_XFRAME (w)->minimize_tool_bar_window_p = 1;
#endif
@@ -6686,13 +6777,13 @@ struct save_window_data
/* We should be able to do without the following two. */
int frame_cols, frame_lines;
- /* These two should get eventually replaced by their pixel
+ /* These three should get eventually replaced by their pixel
counterparts. */
- int frame_menu_bar_lines, frame_tool_bar_lines;
+ int frame_menu_bar_lines, frame_tab_bar_lines, frame_tool_bar_lines;
int frame_text_width, frame_text_height;
/* These are currently unused. We need them as soon as we convert
to pixels. */
- int frame_menu_bar_height, frame_tool_bar_height;
+ int frame_menu_bar_height, frame_tab_bar_height, frame_tool_bar_height;
} GCALIGNED_STRUCT;
/* This is saved as a Lisp_Vector. */
@@ -7370,10 +7461,12 @@ saved by this function. */)
data->frame_cols = FRAME_COLS (f);
data->frame_lines = FRAME_LINES (f);
data->frame_menu_bar_lines = FRAME_MENU_BAR_LINES (f);
+ data->frame_tab_bar_lines = FRAME_TAB_BAR_LINES (f);
data->frame_tool_bar_lines = FRAME_TOOL_BAR_LINES (f);
data->frame_text_width = FRAME_TEXT_WIDTH (f);
data->frame_text_height = FRAME_TEXT_HEIGHT (f);
data->frame_menu_bar_height = FRAME_MENU_BAR_HEIGHT (f);
+ data->frame_tab_bar_height = FRAME_TAB_BAR_HEIGHT (f);
data->frame_tool_bar_height = FRAME_TOOL_BAR_HEIGHT (f);
data->selected_frame = selected_frame;
data->current_window = FRAME_SELECTED_WINDOW (f);
@@ -7663,6 +7756,7 @@ set_window_scroll_bars (struct window *w, Lisp_Object width,
/* Don't change anything if new scroll bar won't fit. */
if ((WINDOW_PIXEL_HEIGHT (w)
+ - WINDOW_TAB_LINE_HEIGHT (w)
- WINDOW_HEADER_LINE_HEIGHT (w)
- WINDOW_MODE_LINE_HEIGHT (w)
- (new_height == -1 ? FRAME_SCROLL_BAR_AREA_HEIGHT (f) : new_height))
@@ -8086,6 +8180,7 @@ syms_of_window (void)
DEFSYM (Qmark_for_redisplay, "mark-for-redisplay");
DEFSYM (Qmode_line_format, "mode-line-format");
DEFSYM (Qheader_line_format, "header-line-format");
+ DEFSYM (Qtab_line_format, "tab-line-format");
DEFVAR_LISP ("temp-buffer-show-function", Vtemp_buffer_show_function,
doc: /* Non-nil means call as function to display a help buffer.
@@ -8389,6 +8484,7 @@ displayed after a scrolling operation to be somewhat inaccurate. */);
defsubr (&Sset_window_redisplay_end_trigger);
defsubr (&Swindow_mode_line_height);
defsubr (&Swindow_header_line_height);
+ defsubr (&Swindow_tab_line_height);
defsubr (&Swindow_right_divider_width);
defsubr (&Swindow_bottom_divider_width);
defsubr (&Swindow_scroll_bar_width);
diff --git a/src/window.h b/src/window.h
index dfbc6385312..21d2f3d3671 100644
--- a/src/window.h
+++ b/src/window.h
@@ -361,6 +361,9 @@ struct window
/* Effective height of the header line, or -1 if not known. */
int header_line_height;
+ /* Effective height of the tab line, or -1 if not known. */
+ int tab_line_height;
+
/* Z - the buffer position of the last glyph in the current
matrix of W. Only valid if window_end_valid is true. */
ptrdiff_t window_end_pos;
@@ -697,7 +700,7 @@ wset_next_buffers (struct window *w, Lisp_Object val)
(WINDOW_LEFT_EDGE_COL (W) + WINDOW_TOTAL_COLS (W))
/* Return the canonical frame line at which window W starts.
- This includes a header line, if any. */
+ This includes a header/tab line, if any. */
#define WINDOW_TOP_EDGE_LINE(W) (W)->top_line
/* Return the canonical frame line before which window W ends.
@@ -715,7 +718,7 @@ wset_next_buffers (struct window *w, Lisp_Object val)
(WINDOW_LEFT_PIXEL_EDGE (W) + WINDOW_PIXEL_WIDTH (W))
/* Return the top pixel edge at which window W starts.
- This includes a header line, if any. */
+ This includes a header/tab line, if any. */
#define WINDOW_TOP_PIXEL_EDGE(W) (W)->pixel_top
/* Return the bottom pixel edge before which window W ends.
@@ -745,6 +748,15 @@ wset_next_buffers (struct window *w, Lisp_Object val)
#define WINDOW_MENU_BAR_P(W) false
#endif
+/* True if W is a tab bar window. */
+#if defined (HAVE_WINDOW_SYSTEM)
+#define WINDOW_TAB_BAR_P(W) \
+ (WINDOWP (WINDOW_XFRAME (W)->tab_bar_window) \
+ && (W) == XWINDOW (WINDOW_XFRAME (W)->tab_bar_window))
+#else
+#define WINDOW_TAB_BAR_P(W) false
+#endif
+
/* True if W is a tool bar window. */
#if defined (HAVE_WINDOW_SYSTEM) && ! defined (HAVE_EXT_TOOL_BAR)
#define WINDOW_TOOL_BAR_P(W) \
@@ -756,13 +768,13 @@ wset_next_buffers (struct window *w, Lisp_Object val)
/* Return the frame y-position at which window W starts. */
#define WINDOW_TOP_EDGE_Y(W) \
- (((WINDOW_MENU_BAR_P (W) || WINDOW_TOOL_BAR_P (W)) \
+ (((WINDOW_MENU_BAR_P (W) || WINDOW_TAB_BAR_P (W) || WINDOW_TOOL_BAR_P (W)) \
? 0 : FRAME_INTERNAL_BORDER_WIDTH (WINDOW_XFRAME (W))) \
+ WINDOW_TOP_PIXEL_EDGE (W))
/* Return the frame y-position before which window W ends. */
#define WINDOW_BOTTOM_EDGE_Y(W) \
- (((WINDOW_MENU_BAR_P (W) || WINDOW_TOOL_BAR_P (W)) \
+ (((WINDOW_MENU_BAR_P (W) || WINDOW_TAB_BAR_P (W) || WINDOW_TOOL_BAR_P (W)) \
? 0 : FRAME_INTERNAL_BORDER_WIDTH (WINDOW_XFRAME (W))) \
+ WINDOW_BOTTOM_PIXEL_EDGE (W))
@@ -996,6 +1008,16 @@ wset_next_buffers (struct window *w, Lisp_Object val)
#define WINDOW_HEADER_LINE_LINES(W) \
window_wants_header_line (W)
+/* Height in pixels of the tab line.
+ Zero if W doesn't have a tab line. */
+#define WINDOW_TAB_LINE_HEIGHT(W) \
+ (window_wants_tab_line (W) \
+ ? CURRENT_TAB_LINE_HEIGHT (W) \
+ : 0)
+
+#define WINDOW_TAB_LINE_LINES(W) \
+ window_wants_tab_line (W)
+
/* Pixel height of window W without mode line, bottom scroll bar and
bottom divider. */
#define WINDOW_BOX_HEIGHT_NO_MODE_LINE(W) \
@@ -1004,14 +1026,15 @@ wset_next_buffers (struct window *w, Lisp_Object val)
- WINDOW_SCROLL_BAR_AREA_HEIGHT (W) \
- WINDOW_MODE_LINE_HEIGHT (W))
-/* Pixel height of window W without mode and header line and bottom
+/* Pixel height of window W without mode and header/tab line and bottom
divider. */
#define WINDOW_BOX_TEXT_HEIGHT(W) \
(WINDOW_PIXEL_HEIGHT ((W)) \
- WINDOW_BOTTOM_DIVIDER_WIDTH (W) \
- WINDOW_SCROLL_BAR_AREA_HEIGHT (W) \
- WINDOW_MODE_LINE_HEIGHT (W) \
- - WINDOW_HEADER_LINE_HEIGHT (W))
+ - WINDOW_HEADER_LINE_HEIGHT (W) \
+ - WINDOW_TAB_LINE_HEIGHT (W))
/* Return the frame position where the horizontal scroll bar of window W
starts. */
@@ -1068,7 +1091,7 @@ extern Lisp_Object minibuf_selected_window;
extern Lisp_Object make_window (void);
extern Lisp_Object window_from_coordinates (struct frame *, int, int,
- enum window_part *, bool);
+ enum window_part *, bool, bool);
extern void resize_frame_windows (struct frame *, int, bool);
extern void restore_window_configuration (Lisp_Object);
extern void delete_all_child_windows (Lisp_Object);
@@ -1158,6 +1181,7 @@ extern bool compare_window_configurations (Lisp_Object, Lisp_Object, bool);
extern void mark_window_cursors_off (struct window *);
extern bool window_wants_mode_line (struct window *);
extern bool window_wants_header_line (struct window *);
+extern bool window_wants_tab_line (struct window *);
extern int window_internal_height (struct window *);
extern int window_body_width (struct window *w, bool);
enum margin_unit { MARGIN_IN_LINES, MARGIN_IN_PIXELS };
diff --git a/src/xdisp.c b/src/xdisp.c
index 95895ec3acb..89a72ff7511 100644
--- a/src/xdisp.c
+++ b/src/xdisp.c
@@ -947,6 +947,8 @@ static int store_mode_line_string (const char *, Lisp_Object, bool, int, int,
Lisp_Object);
static const char *decode_mode_spec (struct window *, int, int, Lisp_Object *);
static void display_menu_bar (struct window *);
+static void display_tab_bar (struct window *);
+static void update_tab_bar (struct frame *, bool);
static ptrdiff_t display_count_lines (ptrdiff_t, ptrdiff_t, ptrdiff_t,
ptrdiff_t *);
static void pint2str (register char *, register int, register ptrdiff_t);
@@ -1080,7 +1082,7 @@ window_box_height (struct window *w)
height -= WINDOW_BOTTOM_DIVIDER_WIDTH (w);
height -= WINDOW_SCROLL_BAR_AREA_HEIGHT (w);
- /* Note: the code below that determines the mode-line/header-line
+ /* Note: the code below that determines the mode-line/header-line/tab-line
height is essentially the same as that contained in the macro
CURRENT_{MODE,HEADER}_LINE_HEIGHT, except that it checks whether
the appropriate glyph row has its `mode_line_p' flag set,
@@ -1098,6 +1100,18 @@ window_box_height (struct window *w)
height -= estimate_mode_line_height (f, CURRENT_MODE_LINE_FACE_ID (w));
}
+ if (window_wants_tab_line (w))
+ {
+ struct glyph_row *tl_row
+ = (w->current_matrix && w->current_matrix->rows
+ ? MATRIX_TAB_LINE_ROW (w->current_matrix)
+ : 0);
+ if (tl_row && tl_row->mode_line_p)
+ height -= tl_row->height;
+ else
+ height -= estimate_mode_line_height (f, TAB_LINE_FACE_ID);
+ }
+
if (window_wants_header_line (w))
{
struct glyph_row *hl_row
@@ -1210,6 +1224,8 @@ window_box (struct window *w, enum glyph_row_area area, int *box_x,
if (box_y)
{
*box_y = WINDOW_TOP_EDGE_Y (w);
+ if (window_wants_tab_line (w))
+ *box_y += CURRENT_TAB_LINE_HEIGHT (w);
if (window_wants_header_line (w))
*box_y += CURRENT_HEADER_LINE_HEIGHT (w);
}
@@ -1435,13 +1451,14 @@ pos_visible_p (struct window *w, ptrdiff_t charpos, int *x, int *y,
/* Some Lisp hook could call us in the middle of redisplaying this
very window. If, by some bad luck, we are retrying redisplay
- because we found that the mode-line height and/or header-line
+ because we found that the mode-line height and/or tab/header-line
height needs to be updated, the assignment of mode_line_height
and header_line_height below could disrupt that, due to the
selected/nonselected window dance during mode-line display, and
we could infloop. Avoid that. */
int prev_mode_line_height = w->mode_line_height;
int prev_header_line_height = w->header_line_height;
+ int prev_tab_line_height = w->tab_line_height;
/* Compute exact mode line heights. */
if (window_wants_mode_line (w))
{
@@ -1455,6 +1472,18 @@ pos_visible_p (struct window *w, ptrdiff_t charpos, int *x, int *y,
: window_mode_line_format);
}
+ if (window_wants_tab_line (w))
+ {
+ Lisp_Object window_tab_line_format
+ = window_parameter (w, Qtab_line_format);
+
+ w->tab_line_height
+ = display_mode_line (w, TAB_LINE_FACE_ID,
+ NILP (window_tab_line_format)
+ ? BVAR (current_buffer, tab_line_format)
+ : window_tab_line_format);
+ }
+
if (window_wants_header_line (w))
{
Lisp_Object window_header_line_format
@@ -1511,7 +1540,7 @@ pos_visible_p (struct window *w, ptrdiff_t charpos, int *x, int *y,
glyph. */
int top_x = it.current_x;
int top_y = it.current_y;
- int window_top_y = WINDOW_HEADER_LINE_HEIGHT (w);
+ int window_top_y = WINDOW_TAB_LINE_HEIGHT (w) + WINDOW_HEADER_LINE_HEIGHT (w);
int bottom_y;
struct it save_it;
void *save_it_data = NULL;
@@ -1779,7 +1808,8 @@ pos_visible_p (struct window *w, ptrdiff_t charpos, int *x, int *y,
- it.last_visible_y));
*rowh = max (0, (min (it2.current_y + it2.max_ascent + it2.max_descent,
it.last_visible_y)
- - max (it2.current_y,
+ - max (max (it2.current_y,
+ WINDOW_TAB_LINE_HEIGHT (w)),
WINDOW_HEADER_LINE_HEIGHT (w))));
*vpos = it2.vpos;
if (it2.bidi_it.paragraph_dir == R2L)
@@ -1820,6 +1850,7 @@ pos_visible_p (struct window *w, ptrdiff_t charpos, int *x, int *y,
/* Restore potentially overwritten values. */
w->mode_line_height = prev_mode_line_height;
w->header_line_height = prev_header_line_height;
+ w->tab_line_height = prev_tab_line_height;
return visible_p;
}
@@ -2212,7 +2243,7 @@ get_glyph_string_clip_rects (struct glyph_string *s, NativeRectangle *rects, int
intentionally draws over other lines. */
if (s->for_overlaps)
{
- r.y = WINDOW_HEADER_LINE_HEIGHT (s->w);
+ r.y = WINDOW_TAB_LINE_HEIGHT (s->w) + WINDOW_HEADER_LINE_HEIGHT (s->w);
r.height = window_text_bottom_y (s->w) - r.y;
/* Alas, the above simple strategy does not work for the
@@ -2239,7 +2270,7 @@ get_glyph_string_clip_rects (struct glyph_string *s, NativeRectangle *rects, int
partially visible lines at the top of a window. */
if (!s->row->full_width_p
&& MATRIX_ROW_PARTIALLY_VISIBLE_AT_TOP_P (s->w, s->row))
- r.y = WINDOW_HEADER_LINE_HEIGHT (s->w);
+ r.y = WINDOW_TAB_LINE_HEIGHT (s->w) + WINDOW_HEADER_LINE_HEIGHT (s->w);
else
r.y = max (0, s->row->y);
}
@@ -2416,7 +2447,7 @@ get_phys_cursor_geometry (struct window *w, struct glyph_row *row,
h = min (h, row->height);
h0 = min (h0, ascent + glyph->descent);
- y0 = WINDOW_HEADER_LINE_HEIGHT (w);
+ y0 = WINDOW_TAB_LINE_HEIGHT (w) + WINDOW_HEADER_LINE_HEIGHT (w);
if (y < y0)
{
h = max (h - (y0 - y) + 1, h0);
@@ -2460,7 +2491,7 @@ remember_mouse_glyph (struct frame *f, int gx, int gy, NativeRectangle *rect)
goto virtual_glyph;
}
else if (!f->glyphs_initialized_p
- || (window = window_from_coordinates (f, gx, gy, &part, false),
+ || (window = window_from_coordinates (f, gx, gy, &part, false, false),
NILP (window)))
{
width = FRAME_SMALLEST_CHAR_WIDTH (f);
@@ -2495,11 +2526,14 @@ remember_mouse_glyph (struct frame *f, int gx, int gy, NativeRectangle *rect)
area = RIGHT_MARGIN_AREA;
goto text_glyph;
+ case ON_TAB_LINE:
case ON_HEADER_LINE:
case ON_MODE_LINE:
- gr = (part == ON_HEADER_LINE
- ? MATRIX_HEADER_LINE_ROW (w->current_matrix)
- : MATRIX_MODE_LINE_ROW (w->current_matrix));
+ gr = (part == ON_TAB_LINE
+ ? MATRIX_TAB_LINE_ROW (w->current_matrix)
+ : (part == ON_HEADER_LINE
+ ? MATRIX_HEADER_LINE_ROW (w->current_matrix)
+ : MATRIX_MODE_LINE_ROW (w->current_matrix)));
gy = gr->y;
area = TEXT_AREA;
goto text_glyph_row_found;
@@ -2545,7 +2579,8 @@ remember_mouse_glyph (struct frame *f, int gx, int gy, NativeRectangle *rect)
gx += (x / width) * width;
}
- if (part != ON_MODE_LINE && part != ON_HEADER_LINE)
+ if (part != ON_MODE_LINE && part != ON_HEADER_LINE
+ && part != ON_TAB_LINE)
{
gx += window_box_left_offset (w, area);
/* Don't expand over the modeline to make sure the vertical
@@ -2560,7 +2595,8 @@ remember_mouse_glyph (struct frame *f, int gx, int gy, NativeRectangle *rect)
gx = (x / width) * width;
y -= gy;
gy += (y / height) * height;
- if (part != ON_MODE_LINE && part != ON_HEADER_LINE)
+ if (part != ON_MODE_LINE && part != ON_HEADER_LINE
+ && part != ON_TAB_LINE)
/* See comment above. */
height = min (height,
max (0, WINDOW_BOX_HEIGHT_NO_MODE_LINE (w) - gy));
@@ -2920,8 +2956,14 @@ init_iterator (struct it *it, struct window *w,
if (base_face_id == MODE_LINE_FACE_ID
|| base_face_id == MODE_LINE_INACTIVE_FACE_ID)
row = MATRIX_MODE_LINE_ROW (w->desired_matrix);
+ else if (base_face_id == TAB_LINE_FACE_ID)
+ row = MATRIX_TAB_LINE_ROW (w->desired_matrix);
else if (base_face_id == HEADER_LINE_FACE_ID)
- row = MATRIX_HEADER_LINE_ROW (w->desired_matrix);
+ {
+ /* Header line row depends on whether tab line is enabled. */
+ w->desired_matrix->tab_line_p = window_wants_tab_line (w);
+ row = MATRIX_HEADER_LINE_ROW (w->desired_matrix);
+ }
}
/* Clear IT, and set it->object and other IT's Lisp objects to Qnil.
@@ -3097,8 +3139,9 @@ init_iterator (struct it *it, struct window *w,
it->last_visible_x -= it->continuation_pixel_width;
}
+ it->tab_line_p = window_wants_tab_line (w);
it->header_line_p = window_wants_header_line (w);
- body_height = WINDOW_HEADER_LINE_HEIGHT (w);
+ body_height = WINDOW_TAB_LINE_HEIGHT (w) + WINDOW_HEADER_LINE_HEIGHT (w);
it->current_y = body_height + w->vscroll;
}
@@ -3201,7 +3244,7 @@ void
start_display (struct it *it, struct window *w, struct text_pos pos)
{
struct glyph_row *row;
- bool first_vpos = window_wants_header_line (w);
+ int first_vpos = window_wants_tab_line (w) + window_wants_header_line (w);
row = w->desired_matrix->rows + first_vpos;
init_iterator (it, w, CHARPOS (pos), BYTEPOS (pos), row, DEFAULT_FACE_ID);
@@ -10413,11 +10456,16 @@ include the height of both, if present, in the return value. */)
/* Subtract height of header-line which was counted automatically by
start_display. */
y = it.current_y + it.max_ascent + it.max_descent
- - WINDOW_HEADER_LINE_HEIGHT (w);
+ - WINDOW_TAB_LINE_HEIGHT (w) - WINDOW_HEADER_LINE_HEIGHT (w);
/* Don't return more than Y-LIMIT. */
if (y > max_y)
y = max_y;
+ if (EQ (mode_and_header_line, Qtab_line)
+ || EQ (mode_and_header_line, Qt))
+ /* Re-add height of tab-line as requested. */
+ y = y + WINDOW_TAB_LINE_HEIGHT (w);
+
if (EQ (mode_and_header_line, Qheader_line)
|| EQ (mode_and_header_line, Qt))
/* Re-add height of header-line as requested. */
@@ -12363,6 +12411,7 @@ prepare_menu_bars (void)
continue;
menu_bar_hooks_run = update_menu_bar (f, false, menu_bar_hooks_run);
+ update_tab_bar (f, false);
#ifdef HAVE_WINDOW_SYSTEM
update_tool_bar (f, false);
#endif
@@ -12374,6 +12423,7 @@ prepare_menu_bars (void)
{
struct frame *sf = SELECTED_FRAME ();
update_menu_bar (sf, true, false);
+ update_tab_bar (sf, true);
#ifdef HAVE_WINDOW_SYSTEM
update_tool_bar (sf, true);
#endif
@@ -12490,8 +12540,10 @@ update_menu_bar (struct frame *f, bool save_match_data, bool hooks_run)
return hooks_run;
}
+
+
/***********************************************************************
- Tool-bars
+ Tab-bars
***********************************************************************/
#ifdef HAVE_WINDOW_SYSTEM
@@ -12510,6 +12562,871 @@ fast_set_selected_frame (Lisp_Object frame)
}
}
+#endif /* HAVE_WINDOW_SYSTEM */
+
+/* Update the tab-bar item list for frame F. This has to be done
+ before we start to fill in any display lines. Called from
+ prepare_menu_bars. If SAVE_MATCH_DATA, we must save
+ and restore it here. */
+
+static void
+update_tab_bar (struct frame *f, bool save_match_data)
+{
+ bool do_update = false;
+
+#ifdef HAVE_WINDOW_SYSTEM
+ if (FRAME_WINDOW_P (f) && WINDOWP (f->tab_bar_window)) {
+ if (WINDOW_TOTAL_LINES (XWINDOW (f->tab_bar_window)) > 0)
+ do_update = true;
+ }
+ else
+#endif
+ if (FRAME_TAB_BAR_LINES (f) > 0)
+ do_update = true;
+
+ if (do_update)
+ {
+ Lisp_Object window;
+ struct window *w;
+
+ window = FRAME_SELECTED_WINDOW (f);
+ w = XWINDOW (window);
+
+ /* If the user has switched buffers or windows, we need to
+ recompute to reflect the new bindings. But we'll
+ recompute when update_mode_lines is set too; that means
+ that people can use force-mode-line-update to request
+ that the menu bar be recomputed. The adverse effect on
+ the rest of the redisplay algorithm is about the same as
+ windows_or_buffers_changed anyway. */
+ if (windows_or_buffers_changed
+ || w->update_mode_line
+ || update_mode_lines
+ || window_buffer_changed (w))
+ {
+ struct buffer *prev = current_buffer;
+ ptrdiff_t count = SPECPDL_INDEX ();
+ Lisp_Object new_tab_bar;
+ int new_n_tab_bar;
+
+ /* Set current_buffer to the buffer of the selected
+ window of the frame, so that we get the right local
+ keymaps. */
+ set_buffer_internal_1 (XBUFFER (w->contents));
+
+ /* Save match data, if we must. */
+ if (save_match_data)
+ record_unwind_save_match_data ();
+
+ /* Make sure that we don't accidentally use bogus keymaps. */
+ if (NILP (Voverriding_local_map_menu_flag))
+ {
+ specbind (Qoverriding_terminal_local_map, Qnil);
+ specbind (Qoverriding_local_map, Qnil);
+ }
+
+ /* We must temporarily set the selected frame to this frame
+ before calling tab_bar_items, because the calculation of
+ the tab-bar keymap uses the selected frame (see
+ `tab-bar-make-keymap' in tab-bar.el). */
+ eassert (EQ (selected_window,
+ /* Since we only explicitly preserve selected_frame,
+ check that selected_window would be redundant. */
+ XFRAME (selected_frame)->selected_window));
+#ifdef HAVE_WINDOW_SYSTEM
+ Lisp_Object frame;
+ record_unwind_protect (fast_set_selected_frame, selected_frame);
+ XSETFRAME (frame, f);
+ fast_set_selected_frame (frame);
+#endif
+
+ /* Build desired tab-bar items from keymaps. */
+ new_tab_bar
+ = tab_bar_items (Fcopy_sequence (f->tab_bar_items),
+ &new_n_tab_bar);
+
+ /* Redisplay the tab-bar if we changed it. */
+ if (new_n_tab_bar != f->n_tab_bar_items
+ || NILP (Fequal (new_tab_bar, f->tab_bar_items)))
+ {
+ /* Redisplay that happens asynchronously due to an expose event
+ may access f->tab_bar_items. Make sure we update both
+ variables within BLOCK_INPUT so no such event interrupts. */
+ block_input ();
+ fset_tab_bar_items (f, new_tab_bar);
+ f->n_tab_bar_items = new_n_tab_bar;
+ w->update_mode_line = true;
+ unblock_input ();
+ }
+
+ unbind_to (count, Qnil);
+ set_buffer_internal_1 (prev);
+ }
+ }
+}
+
+/* Redisplay the tab bar in the frame for window W.
+
+ The tab bar of X frames that don't have X toolkit support is
+ displayed in a special window W->frame->tab_bar_window.
+
+ The tab bar of terminal frames is treated specially as far as
+ glyph matrices are concerned. Tab bar lines are not part of
+ windows, so the update is done directly on the frame matrix rows
+ for the tab bar. */
+
+static void
+display_tab_bar (struct window *w)
+{
+ struct frame *f = XFRAME (WINDOW_FRAME (w));
+ struct it it;
+ Lisp_Object items;
+ int i;
+
+ /* Don't do all this for graphical frames. */
+#ifdef HAVE_NTGUI
+ if (FRAME_W32_P (f))
+ return;
+#endif
+#if defined (USE_X_TOOLKIT) || defined (USE_GTK)
+ if (FRAME_X_P (f))
+ return;
+#endif
+
+#ifdef HAVE_NS
+ if (FRAME_NS_P (f))
+ return;
+#endif /* HAVE_NS */
+
+#if defined (USE_X_TOOLKIT) || defined (USE_GTK)
+ eassert (!FRAME_WINDOW_P (f));
+ init_iterator (&it, w, -1, -1, f->desired_matrix->rows
+ + (FRAME_MENU_BAR_LINES (f) > 0 ? 1 : 0),
+ TAB_BAR_FACE_ID);
+ it.first_visible_x = 0;
+ it.last_visible_x = FRAME_PIXEL_WIDTH (f);
+#elif defined (HAVE_X_WINDOWS) /* X without toolkit. */
+ if (FRAME_WINDOW_P (f))
+ {
+ /* Tab bar lines are displayed in the desired matrix of the
+ dummy window tab_bar_window. */
+ struct window *tab_w;
+ tab_w = XWINDOW (f->tab_bar_window);
+ init_iterator (&it, tab_w, -1, -1, tab_w->desired_matrix->rows,
+ TAB_BAR_FACE_ID);
+ it.first_visible_x = 0;
+ it.last_visible_x = FRAME_PIXEL_WIDTH (f);
+ }
+ else
+#endif /* not USE_X_TOOLKIT and not USE_GTK */
+ {
+ /* This is a TTY frame, i.e. character hpos/vpos are used as
+ pixel x/y. */
+ init_iterator (&it, w, -1, -1, f->desired_matrix->rows
+ + (FRAME_MENU_BAR_LINES (f) > 0 ? 1 : 0),
+ TAB_BAR_FACE_ID);
+ it.first_visible_x = 0;
+ it.last_visible_x = FRAME_COLS (f);
+ }
+
+ /* FIXME: This should be controlled by a user option. See the
+ comments in redisplay_tool_bar and display_mode_line about
+ this. */
+ it.paragraph_embedding = L2R;
+
+ /* Clear all rows of the tab bar. */
+ for (i = 0; i < FRAME_TAB_BAR_LINES (f); ++i)
+ {
+ struct glyph_row *row = it.glyph_row + i;
+ clear_glyph_row (row);
+ row->enabled_p = true;
+ row->full_width_p = true;
+ row->reversed_p = false;
+ }
+
+ /* Display all items of the tab bar. */
+ items = it.f->tab_bar_items;
+ for (i = 0; i < it.f->n_tab_bar_items; ++i)
+ {
+ Lisp_Object string;
+
+ /* Stop at nil string. */
+ string = AREF (items, i * TAB_BAR_ITEM_NSLOTS + TAB_BAR_ITEM_CAPTION);
+ if (NILP (string))
+ break;
+
+ if (it.current_x < it.last_visible_x)
+ display_string (NULL, string, Qnil, 0, 0, &it,
+ SCHARS (string), 0, 0, STRING_MULTIBYTE (string));
+ }
+
+ /* Fill out the line with spaces. */
+ if (it.current_x < it.last_visible_x)
+ display_string ("", Qnil, Qnil, 0, 0, &it, -1, 0, 0, -1);
+
+ /* Compute the total height of the lines. */
+ compute_line_metrics (&it);
+}
+
+#ifdef HAVE_WINDOW_SYSTEM
+
+/* Set F->desired_tab_bar_string to a Lisp string representing frame
+ F's desired tab-bar contents. F->tab_bar_items must have
+ been set up previously by calling prepare_menu_bars. */
+
+static void
+build_desired_tab_bar_string (struct frame *f)
+{
+ int i;
+ Lisp_Object caption;
+
+ caption = Qnil;
+
+ /* Prepare F->desired_tab_bar_string. Make a new string. */
+ fset_desired_tab_bar_string (f, build_string (""));
+
+ /* Put a `display' property on the string for the captions to display,
+ put a `menu_item' property on tab-bar items with a value that
+ is the index of the item in F's tab-bar item vector. */
+ for (i = 0; i < f->n_tab_bar_items; ++i)
+ {
+#define PROP(IDX) \
+ AREF (f->tab_bar_items, i * TAB_BAR_ITEM_NSLOTS + (IDX))
+
+ caption = Fcopy_sequence (PROP (TAB_BAR_ITEM_CAPTION));
+
+ /* Put a `display' text property on the string for the caption to
+ display. Put a `menu-item' property on the string that gives
+ the start of this item's properties in the tab-bar items
+ vector. */
+ AUTO_LIST2 (props, Qmenu_item, make_fixnum (i * TAB_BAR_ITEM_NSLOTS));
+
+ Fadd_text_properties (make_fixnum (0), make_fixnum (SCHARS (caption)),
+ props, caption);
+
+ f->desired_tab_bar_string =
+ concat2 (f->desired_tab_bar_string, caption);
+
+#undef PROP
+ }
+}
+
+
+/* Display one line of the tab-bar of frame IT->f.
+
+ HEIGHT specifies the desired height of the tab-bar line.
+ If the actual height of the glyph row is less than HEIGHT, the
+ row's height is increased to HEIGHT, and the icons are centered
+ vertically in the new height.
+
+ If HEIGHT is -1, we are counting needed tab-bar lines, so don't
+ count a final empty row in case the tab-bar width exactly matches
+ the window width.
+*/
+
+static void
+display_tab_bar_line (struct it *it, int height)
+{
+ struct glyph_row *row = it->glyph_row;
+ int max_x = it->last_visible_x;
+ struct glyph *last;
+
+ /* Don't extend on a previously drawn tab bar items (Bug#16058). */
+ clear_glyph_row (row);
+ row->enabled_p = true;
+ row->y = it->current_y;
+
+ /* Note that this isn't made use of if the face hasn't a box,
+ so there's no need to check the face here. */
+ it->start_of_box_run_p = true;
+
+ while (it->current_x < max_x)
+ {
+ int x, n_glyphs_before, i, nglyphs;
+ struct it it_before;
+
+ /* Get the next display element. */
+ if (!get_next_display_element (it))
+ {
+ /* Don't count empty row if we are counting needed tab-bar lines. */
+ if (height < 0 && !it->hpos)
+ return;
+ break;
+ }
+
+ /* Produce glyphs. */
+ n_glyphs_before = row->used[TEXT_AREA];
+ it_before = *it;
+
+ PRODUCE_GLYPHS (it);
+
+ nglyphs = row->used[TEXT_AREA] - n_glyphs_before;
+ i = 0;
+ x = it_before.current_x;
+ while (i < nglyphs)
+ {
+ struct glyph *glyph = row->glyphs[TEXT_AREA] + n_glyphs_before + i;
+
+ if (x + glyph->pixel_width > max_x)
+ {
+ /* Glyph doesn't fit on line. Backtrack. */
+ row->used[TEXT_AREA] = n_glyphs_before;
+ *it = it_before;
+ /* If this is the only glyph on this line, it will never fit on the
+ tab-bar, so skip it. But ensure there is at least one glyph,
+ so we don't accidentally disable the tab-bar. */
+ if (n_glyphs_before == 0
+ && (it->vpos > 0 || IT_STRING_CHARPOS (*it) < it->end_charpos-1))
+ break;
+ goto out;
+ }
+
+ ++it->hpos;
+ x += glyph->pixel_width;
+ ++i;
+ }
+
+ /* Stop at line end. */
+ if (ITERATOR_AT_END_OF_LINE_P (it))
+ break;
+
+ set_iterator_to_next (it, true);
+ }
+
+ out:;
+
+ row->displays_text_p = row->used[TEXT_AREA] != 0;
+
+ /* Use default face for the border below the tab bar.
+
+ FIXME: When auto-resize-tab-bars is grow-only, there is
+ no additional border below the possibly empty tab-bar lines.
+ So to make the extra empty lines look "normal", we have to
+ use the tab-bar face for the border too. */
+ if (!MATRIX_ROW_DISPLAYS_TEXT_P (row)
+ && !EQ (Vauto_resize_tab_bars, Qgrow_only))
+ it->face_id = DEFAULT_FACE_ID;
+
+ extend_face_to_end_of_line (it);
+ last = row->glyphs[TEXT_AREA] + row->used[TEXT_AREA] - 1;
+ last->right_box_line_p = true;
+ if (last == row->glyphs[TEXT_AREA])
+ last->left_box_line_p = true;
+
+ /* Make line the desired height and center it vertically. */
+ if ((height -= it->max_ascent + it->max_descent) > 0)
+ {
+ /* Don't add more than one line height. */
+ height %= FRAME_LINE_HEIGHT (it->f);
+ it->max_ascent += height / 2;
+ it->max_descent += (height + 1) / 2;
+ }
+
+ compute_line_metrics (it);
+
+ /* If line is empty, make it occupy the rest of the tab-bar. */
+ if (!MATRIX_ROW_DISPLAYS_TEXT_P (row))
+ {
+ row->height = row->phys_height = it->last_visible_y - row->y;
+ row->visible_height = row->height;
+ row->ascent = row->phys_ascent = 0;
+ row->extra_line_spacing = 0;
+ }
+
+ row->full_width_p = true;
+ row->continued_p = false;
+ row->truncated_on_left_p = false;
+ row->truncated_on_right_p = false;
+
+ it->current_x = it->hpos = 0;
+ it->current_y += row->height;
+ ++it->vpos;
+ ++it->glyph_row;
+}
+
+
+/* Value is the number of pixels needed to make all tab-bar items of
+ frame F visible. The actual number of glyph rows needed is
+ returned in *N_ROWS if non-NULL. */
+static int
+tab_bar_height (struct frame *f, int *n_rows, bool pixelwise)
+{
+ struct window *w = XWINDOW (f->tab_bar_window);
+ struct it it;
+ /* tab_bar_height is called from redisplay_tab_bar after building
+ the desired matrix, so use (unused) mode-line row as temporary row to
+ avoid destroying the first tab-bar row. */
+ struct glyph_row *temp_row = MATRIX_MODE_LINE_ROW (w->desired_matrix);
+
+ /* Initialize an iterator for iteration over
+ F->desired_tab_bar_string in the tab-bar window of frame F. */
+ init_iterator (&it, w, -1, -1, temp_row, TAB_BAR_FACE_ID);
+ temp_row->reversed_p = false;
+ it.first_visible_x = 0;
+ it.last_visible_x = WINDOW_PIXEL_WIDTH (w);
+ reseat_to_string (&it, NULL, f->desired_tab_bar_string,
+ 0, 0, 0, STRING_MULTIBYTE (f->desired_tab_bar_string));
+ it.paragraph_embedding = L2R;
+
+ while (!ITERATOR_AT_END_P (&it))
+ {
+ clear_glyph_row (temp_row);
+ it.glyph_row = temp_row;
+ display_tab_bar_line (&it, -1);
+ }
+ clear_glyph_row (temp_row);
+
+ /* f->n_tab_bar_rows == 0 means "unknown"; -1 means no tab-bar. */
+ if (n_rows)
+ *n_rows = it.vpos > 0 ? it.vpos : -1;
+
+ if (pixelwise)
+ return it.current_y;
+ else
+ return (it.current_y + FRAME_LINE_HEIGHT (f) - 1) / FRAME_LINE_HEIGHT (f);
+}
+
+DEFUN ("tab-bar-height", Ftab_bar_height, Stab_bar_height,
+ 0, 2, 0,
+ doc: /* Return the number of lines occupied by the tab bar of FRAME.
+If FRAME is nil or omitted, use the selected frame. Optional argument
+PIXELWISE non-nil means return the height of the tab bar in pixels. */)
+ (Lisp_Object frame, Lisp_Object pixelwise)
+{
+ int height = 0;
+
+ struct frame *f = decode_any_frame (frame);
+
+ if (WINDOWP (f->tab_bar_window)
+ && WINDOW_PIXEL_HEIGHT (XWINDOW (f->tab_bar_window)) > 0)
+ {
+ update_tab_bar (f, true);
+ if (f->n_tab_bar_items)
+ {
+ build_desired_tab_bar_string (f);
+ height = tab_bar_height (f, NULL, !NILP (pixelwise));
+ }
+ }
+
+ return make_fixnum (height);
+}
+
+
+/* Display the tab-bar of frame F. Value is true if tab-bar's
+ height should be changed. */
+static bool
+redisplay_tab_bar (struct frame *f)
+{
+ f->tab_bar_redisplayed = true;
+
+ struct window *w;
+ struct it it;
+ struct glyph_row *row;
+
+ /* If frame hasn't a tab-bar window or if it is zero-height, don't
+ do anything. This means you must start with tab-bar-lines
+ non-zero to get the auto-sizing effect. Or in other words, you
+ can turn off tab-bars by specifying tab-bar-lines zero. */
+ if (!WINDOWP (f->tab_bar_window)
+ || (w = XWINDOW (f->tab_bar_window),
+ WINDOW_TOTAL_LINES (w) == 0))
+ return false;
+
+ /* Set up an iterator for the tab-bar window. */
+ init_iterator (&it, w, -1, -1, w->desired_matrix->rows, TAB_BAR_FACE_ID);
+ it.first_visible_x = 0;
+ it.last_visible_x = WINDOW_PIXEL_WIDTH (w);
+ row = it.glyph_row;
+ row->reversed_p = false;
+
+ /* Build a string that represents the contents of the tab-bar. */
+ build_desired_tab_bar_string (f);
+ reseat_to_string (&it, NULL, f->desired_tab_bar_string, 0, 0, 0,
+ STRING_MULTIBYTE (f->desired_tab_bar_string));
+ /* FIXME: This should be controlled by a user option. But it
+ doesn't make sense to have an R2L tab bar if the menu bar cannot
+ be drawn also R2L, and making the menu bar R2L is tricky due
+ tabkit-specific code that implements it. If an R2L tab bar is
+ ever supported, display_tab_bar_line should also be augmented to
+ call unproduce_glyphs like display_line and display_string
+ do. */
+ it.paragraph_embedding = L2R;
+
+ if (f->n_tab_bar_rows == 0)
+ {
+ int new_height = tab_bar_height (f, &f->n_tab_bar_rows, true);
+
+ if (new_height != WINDOW_PIXEL_HEIGHT (w))
+ {
+ if (FRAME_TERMINAL (f)->change_tab_bar_height_hook)
+ FRAME_TERMINAL (f)->change_tab_bar_height_hook (f, new_height);
+ frame_default_tab_bar_height = new_height;
+ /* Always do that now. */
+ clear_glyph_matrix (w->desired_matrix);
+ f->fonts_changed = true;
+ return true;
+ }
+ }
+
+ /* Display as many lines as needed to display all tab-bar items. */
+
+ if (f->n_tab_bar_rows > 0)
+ {
+ int border, rows, height, extra;
+
+ if (TYPE_RANGED_FIXNUMP (int, Vtab_bar_border))
+ border = XFIXNUM (Vtab_bar_border);
+ else if (EQ (Vtab_bar_border, Qinternal_border_width))
+ border = FRAME_INTERNAL_BORDER_WIDTH (f);
+ else if (EQ (Vtab_bar_border, Qborder_width))
+ border = f->border_width;
+ else
+ border = 0;
+ if (border < 0)
+ border = 0;
+
+ rows = f->n_tab_bar_rows;
+ height = max (1, (it.last_visible_y - border) / rows);
+ extra = it.last_visible_y - border - height * rows;
+
+ while (it.current_y < it.last_visible_y)
+ {
+ int h = 0;
+ if (extra > 0 && rows-- > 0)
+ {
+ h = (extra + rows - 1) / rows;
+ extra -= h;
+ }
+ display_tab_bar_line (&it, height + h);
+ }
+ }
+ else
+ {
+ while (it.current_y < it.last_visible_y)
+ display_tab_bar_line (&it, 0);
+ }
+
+ /* It doesn't make much sense to try scrolling in the tab-bar
+ window, so don't do it. */
+ w->desired_matrix->no_scrolling_p = true;
+ w->must_be_updated_p = true;
+
+ if (!NILP (Vauto_resize_tab_bars))
+ {
+ bool change_height_p = true;
+
+ /* If we couldn't display everything, change the tab-bar's
+ height if there is room for more. */
+ if (IT_STRING_CHARPOS (it) < it.end_charpos)
+ change_height_p = true;
+
+ /* We subtract 1 because display_tab_bar_line advances the
+ glyph_row pointer before returning to its caller. We want to
+ examine the last glyph row produced by
+ display_tab_bar_line. */
+ row = it.glyph_row - 1;
+
+ /* If there are blank lines at the end, except for a partially
+ visible blank line at the end that is smaller than
+ FRAME_LINE_HEIGHT, change the tab-bar's height. */
+ if (!MATRIX_ROW_DISPLAYS_TEXT_P (row)
+ && row->height >= FRAME_LINE_HEIGHT (f))
+ change_height_p = true;
+
+ /* If row displays tab-bar items, but is partially visible,
+ change the tab-bar's height. */
+ if (MATRIX_ROW_DISPLAYS_TEXT_P (row)
+ && MATRIX_ROW_BOTTOM_Y (row) > it.last_visible_y)
+ change_height_p = true;
+
+ /* Resize windows as needed by changing the `tab-bar-lines'
+ frame parameter. */
+ if (change_height_p)
+ {
+ int nrows;
+ int new_height = tab_bar_height (f, &nrows, true);
+
+ change_height_p = ((EQ (Vauto_resize_tab_bars, Qgrow_only)
+ && !f->minimize_tab_bar_window_p)
+ ? (new_height > WINDOW_PIXEL_HEIGHT (w))
+ : (new_height != WINDOW_PIXEL_HEIGHT (w)));
+ f->minimize_tab_bar_window_p = false;
+
+ if (change_height_p)
+ {
+ if (FRAME_TERMINAL (f)->change_tab_bar_height_hook)
+ FRAME_TERMINAL (f)->change_tab_bar_height_hook (f, new_height);
+ frame_default_tab_bar_height = new_height;
+ clear_glyph_matrix (w->desired_matrix);
+ f->n_tab_bar_rows = nrows;
+ f->fonts_changed = true;
+
+ return true;
+ }
+ }
+ }
+
+ f->minimize_tab_bar_window_p = false;
+ return false;
+}
+
+/* Get information about the tab-bar item which is displayed in GLYPH
+ on frame F. Return in *PROP_IDX the index where tab-bar item
+ properties start in F->tab_bar_items. Value is false if
+ GLYPH doesn't display a tab-bar item. */
+
+static bool
+tab_bar_item_info (struct frame *f, struct glyph *glyph, int *prop_idx, bool *close_p)
+{
+ Lisp_Object prop;
+ int charpos;
+
+ /* This function can be called asynchronously, which means we must
+ exclude any possibility that Fget_text_property signals an
+ error. */
+ charpos = min (SCHARS (f->current_tab_bar_string), glyph->charpos);
+ charpos = max (0, charpos);
+
+ /* Get the text property `menu-item' at pos. The value of that
+ property is the start index of this item's properties in
+ F->tab_bar_items. */
+ prop = Fget_text_property (make_fixnum (charpos),
+ Qmenu_item, f->current_tab_bar_string);
+ if (! FIXNUMP (prop))
+ return false;
+ *prop_idx = XFIXNUM (prop);
+
+ *close_p = !NILP (Fget_text_property (make_fixnum (charpos),
+ Qclose_tab,
+ f->current_tab_bar_string));
+
+ return true;
+}
+
+
+/* Get information about the tab-bar item at position X/Y on frame F.
+ Return in *GLYPH a pointer to the glyph of the tab-bar item in
+ the current matrix of the tab-bar window of F, or NULL if not
+ on a tab-bar item. Return in *PROP_IDX the index of the tab-bar
+ item in F->tab_bar_items. Value is
+
+ -1 if X/Y is not on a tab-bar item
+ 0 if X/Y is on the same item that was highlighted before.
+ 1 otherwise. */
+
+static int
+get_tab_bar_item (struct frame *f, int x, int y, struct glyph **glyph,
+ int *hpos, int *vpos, int *prop_idx, bool *close_p)
+{
+ Mouse_HLInfo *hlinfo = MOUSE_HL_INFO (f);
+ struct window *w = XWINDOW (f->tab_bar_window);
+ int area;
+
+ /* Find the glyph under X/Y. */
+ *glyph = x_y_to_hpos_vpos (w, x, y, hpos, vpos, 0, 0, &area);
+ if (*glyph == NULL)
+ return -1;
+
+ /* Get the start of this tab-bar item's properties in
+ f->tab_bar_items. */
+ if (!tab_bar_item_info (f, *glyph, prop_idx, close_p))
+ return -1;
+
+ /* Is mouse on the highlighted item? */
+ if (EQ (f->tab_bar_window, hlinfo->mouse_face_window)
+ && *vpos >= hlinfo->mouse_face_beg_row
+ && *vpos <= hlinfo->mouse_face_end_row
+ && (*vpos > hlinfo->mouse_face_beg_row
+ || *hpos >= hlinfo->mouse_face_beg_col)
+ && (*vpos < hlinfo->mouse_face_end_row
+ || *hpos < hlinfo->mouse_face_end_col
+ || hlinfo->mouse_face_past_end))
+ return 0;
+
+ return 1;
+}
+
+
+/* EXPORT:
+ Handle mouse button event on the tab-bar of frame F, at
+ frame-relative coordinates X/Y. DOWN_P is true for a button press,
+ false for button release. MODIFIERS is event modifiers for button
+ release. */
+
+void
+handle_tab_bar_click (struct frame *f, int x, int y, bool down_p,
+ int modifiers)
+{
+ Mouse_HLInfo *hlinfo = MOUSE_HL_INFO (f);
+ struct window *w = XWINDOW (f->tab_bar_window);
+ int hpos, vpos, prop_idx;
+ bool close_p;
+ struct glyph *glyph;
+ Lisp_Object enabled_p;
+ int ts;
+
+ /* If not on the highlighted tab-bar item, and mouse-highlight is
+ non-nil, return. This is so we generate the tab-bar button
+ click only when the mouse button is released on the same item as
+ where it was pressed. However, when mouse-highlight is disabled,
+ generate the click when the button is released regardless of the
+ highlight, since tab-bar items are not highlighted in that
+ case. */
+ frame_to_window_pixel_xy (w, &x, &y);
+ ts = get_tab_bar_item (f, x, y, &glyph, &hpos, &vpos, &prop_idx, &close_p);
+ if (ts == -1
+ || (ts != 0 && !NILP (Vmouse_highlight)))
+ return;
+
+ /* When mouse-highlight is off, generate the click for the item
+ where the button was pressed, disregarding where it was
+ released. */
+ if (NILP (Vmouse_highlight) && !down_p)
+ prop_idx = f->last_tab_bar_item;
+
+ /* If item is disabled, do nothing. */
+ enabled_p = AREF (f->tab_bar_items, prop_idx + TAB_BAR_ITEM_ENABLED_P);
+ if (NILP (enabled_p))
+ return;
+
+ if (down_p)
+ {
+ /* Show item in pressed state. */
+ if (!NILP (Vmouse_highlight))
+ show_mouse_face (hlinfo, DRAW_IMAGE_SUNKEN);
+ f->last_tab_bar_item = prop_idx;
+ }
+ else
+ {
+ Lisp_Object key, frame;
+ struct input_event event;
+ EVENT_INIT (event);
+
+ /* Show item in released state. */
+ if (!NILP (Vmouse_highlight))
+ show_mouse_face (hlinfo, DRAW_IMAGE_RAISED);
+
+ key = AREF (f->tab_bar_items, prop_idx + TAB_BAR_ITEM_KEY);
+
+ XSETFRAME (frame, f);
+ event.kind = TAB_BAR_EVENT;
+ event.frame_or_window = frame;
+ event.arg = frame;
+ kbd_buffer_store_event (&event);
+
+ event.kind = TAB_BAR_EVENT;
+ event.frame_or_window = frame;
+ event.arg = key;
+ event.modifiers = close_p ? ctrl_modifier | modifiers : modifiers;
+ kbd_buffer_store_event (&event);
+ f->last_tab_bar_item = -1;
+ }
+}
+
+
+/* Possibly highlight a tab-bar item on frame F when mouse moves to
+ tab-bar window-relative coordinates X/Y. Called from
+ note_mouse_highlight. */
+
+static void
+note_tab_bar_highlight (struct frame *f, int x, int y)
+{
+ Lisp_Object window = f->tab_bar_window;
+ struct window *w = XWINDOW (window);
+ Display_Info *dpyinfo = FRAME_DISPLAY_INFO (f);
+ Mouse_HLInfo *hlinfo = MOUSE_HL_INFO (f);
+ int hpos, vpos;
+ struct glyph *glyph;
+ struct glyph_row *row;
+ int i;
+ Lisp_Object enabled_p;
+ int prop_idx;
+ bool close_p;
+ enum draw_glyphs_face draw = DRAW_IMAGE_RAISED;
+ bool mouse_down_p;
+ int rc;
+
+ /* Function note_mouse_highlight is called with negative X/Y
+ values when mouse moves outside of the frame. */
+ if (x <= 0 || y <= 0)
+ {
+ clear_mouse_face (hlinfo);
+ return;
+ }
+
+ rc = get_tab_bar_item (f, x, y, &glyph, &hpos, &vpos, &prop_idx, &close_p);
+ if (rc < 0)
+ {
+ /* Not on tab-bar item. */
+ clear_mouse_face (hlinfo);
+ return;
+ }
+ else if (rc == 0)
+ /* On same tab-bar item as before. */
+ goto set_help_echo;
+
+ clear_mouse_face (hlinfo);
+
+#ifndef HAVE_NS
+ /* Mouse is down, but on different tab-bar item? */
+ mouse_down_p = (gui_mouse_grabbed (dpyinfo)
+ && f == dpyinfo->last_mouse_frame);
+
+ if (mouse_down_p && f->last_tab_bar_item != prop_idx)
+ return;
+
+ draw = mouse_down_p ? DRAW_IMAGE_SUNKEN : DRAW_IMAGE_RAISED;
+#else
+ draw = DRAW_IMAGE_RAISED;
+#endif /* HAVE_NS */
+
+ /* If tab-bar item is not enabled, don't highlight it. */
+ enabled_p = AREF (f->tab_bar_items, prop_idx + TAB_BAR_ITEM_ENABLED_P);
+ if (!NILP (enabled_p) && !NILP (Vmouse_highlight))
+ {
+ /* Compute the x-position of the glyph. In front and past the
+ image is a space. We include this in the highlighted area. */
+ row = MATRIX_ROW (w->current_matrix, vpos);
+ for (i = x = 0; i < hpos; ++i)
+ x += row->glyphs[TEXT_AREA][i].pixel_width;
+
+ /* Record this as the current active region. */
+ hlinfo->mouse_face_beg_col = hpos;
+ hlinfo->mouse_face_beg_row = vpos;
+ hlinfo->mouse_face_beg_x = x;
+ hlinfo->mouse_face_past_end = false;
+
+ hlinfo->mouse_face_end_col = hpos + 1;
+ hlinfo->mouse_face_end_row = vpos;
+ hlinfo->mouse_face_end_x = x + glyph->pixel_width;
+ hlinfo->mouse_face_window = window;
+ hlinfo->mouse_face_face_id = TAB_BAR_FACE_ID;
+
+ /* Display it as active. */
+ show_mouse_face (hlinfo, draw);
+ }
+
+ set_help_echo:
+
+ /* Set help_echo_string to a help string to display for this tab-bar item.
+ XTread_socket does the rest. */
+ help_echo_object = help_echo_window = Qnil;
+ help_echo_pos = -1;
+ help_echo_string = AREF (f->tab_bar_items, prop_idx + TAB_BAR_ITEM_HELP);
+ if (NILP (help_echo_string))
+ help_echo_string = AREF (f->tab_bar_items, prop_idx + TAB_BAR_ITEM_CAPTION);
+}
+
+#endif /* HAVE_WINDOW_SYSTEM */
+
+
+
+/***********************************************************************
+ Tool-bars
+ ***********************************************************************/
+
+#ifdef HAVE_WINDOW_SYSTEM
+
/* Update the tool-bar item list for frame F. This has to be done
before we start to fill in any display lines. Called from
prepare_menu_bars. If SAVE_MATCH_DATA, we must save
@@ -16265,7 +17182,8 @@ compute_window_start_on_continuation_line (struct window *w)
/* Find the start of the continued line. This should be fast
because find_newline is fast (newline cache). */
- row = w->desired_matrix->rows + window_wants_header_line (w);
+ row = w->desired_matrix->rows + window_wants_tab_line (w)
+ + window_wants_header_line (w);
init_iterator (&it, w, CHARPOS (start_pos), BYTEPOS (start_pos),
row, DEFAULT_FACE_ID);
reseat_at_previous_visible_line_start (&it);
@@ -16428,6 +17346,8 @@ try_cursor_movement (Lisp_Object window, struct text_pos startp,
this_scroll_margin = window_scroll_margin (w, MARGIN_IN_PIXELS);
top_scroll_margin = this_scroll_margin;
+ if (window_wants_tab_line (w))
+ top_scroll_margin += CURRENT_TAB_LINE_HEIGHT (w);
if (window_wants_header_line (w))
top_scroll_margin += CURRENT_HEADER_LINE_HEIGHT (w);
@@ -17197,13 +18117,14 @@ redisplay_window (Lisp_Object window, bool just_this_one_p)
margin, even though this part handles windows that didn't
scroll at all. */
int pixel_margin = margin * frame_line_height;
+ bool tab_line = window_wants_tab_line (w);
bool header_line = window_wants_header_line (w);
/* Note: We add an extra FRAME_LINE_HEIGHT, because the loop
below, which finds the row to move point to, advances by
the Y coordinate of the _next_ row, see the definition of
MATRIX_ROW_BOTTOM_Y. */
- if (w->cursor.vpos < margin + header_line)
+ if (w->cursor.vpos < margin + tab_line + header_line)
{
w->cursor.vpos = -1;
clear_glyph_matrix (w->desired_matrix);
@@ -17213,6 +18134,8 @@ redisplay_window (Lisp_Object window, bool just_this_one_p)
{
int window_height = window_box_height (w);
+ if (tab_line)
+ window_height += CURRENT_TAB_LINE_HEIGHT (w);
if (header_line)
window_height += CURRENT_HEADER_LINE_HEIGHT (w);
if (w->cursor.y >= window_height - pixel_margin)
@@ -17544,7 +18467,7 @@ redisplay_window (Lisp_Object window, bool just_this_one_p)
centering_position -= pt_offset;
centering_position -=
(frame_line_height * (1 + margin + last_line_misfit)
- + WINDOW_HEADER_LINE_HEIGHT (w));
+ + WINDOW_TAB_LINE_HEIGHT (w) + WINDOW_HEADER_LINE_HEIGHT (w));
/* Don't let point enter the scroll margin near top of
the window. */
if (centering_position < margin * frame_line_height)
@@ -17772,7 +18695,8 @@ redisplay_window (Lisp_Object window, bool just_this_one_p)
&& (w->column_number_displayed != current_column ())))
/* This means that the window has a mode line. */
&& (window_wants_mode_line (w)
- || window_wants_header_line (w)))
+ || window_wants_header_line (w)
+ || window_wants_tab_line (w)))
{
display_mode_lines (w);
@@ -17788,6 +18712,17 @@ redisplay_window (Lisp_Object window, bool just_this_one_p)
= DESIRED_MODE_LINE_HEIGHT (w);
}
+ /* If tab line height has changed, arrange for a thorough
+ immediate redisplay using the correct tab line height. */
+ if (window_wants_tab_line (w)
+ && CURRENT_TAB_LINE_HEIGHT (w) != DESIRED_TAB_LINE_HEIGHT (w))
+ {
+ f->fonts_changed = true;
+ w->tab_line_height = -1;
+ MATRIX_TAB_LINE_ROW (w->current_matrix)->height
+ = DESIRED_TAB_LINE_HEIGHT (w);
+ }
+
/* If header line height has changed, arrange for a thorough
immediate redisplay using the correct header line height. */
if (window_wants_header_line (w)
@@ -17835,6 +18770,12 @@ redisplay_window (Lisp_Object window, bool just_this_one_p)
#ifdef HAVE_WINDOW_SYSTEM
if (FRAME_WINDOW_P (f))
{
+ if (WINDOWP (f->tab_bar_window)
+ && (FRAME_TAB_BAR_LINES (f) > 0
+ || !NILP (Vauto_resize_tab_bars))
+ && redisplay_tab_bar (f))
+ ignore_mouse_drag_p = true;
+
#ifdef HAVE_EXT_TOOL_BAR
if (FRAME_EXTERNAL_TOOL_BAR (f))
redisplay_tool_bar (f);
@@ -17846,7 +18787,16 @@ redisplay_window (Lisp_Object window, bool just_this_one_p)
ignore_mouse_drag_p = true;
#endif
}
+ else
+ {
+ if ((FRAME_TAB_BAR_LINES (f) > 0))
+ display_tab_bar (w);
+ }
+
gui_consider_frame_title (w->frame);
+#else
+ if ((FRAME_TAB_BAR_LINES (f) > 0))
+ display_tab_bar (w);
#endif
}
@@ -18058,6 +19008,11 @@ try_window_reusing_current_matrix (struct window *w)
return false;
/* If top-line visibility has changed, give up. */
+ if (window_wants_tab_line (w)
+ != MATRIX_TAB_LINE_ROW (w->current_matrix)->mode_line_p)
+ return false;
+
+ /* If top-line visibility has changed, give up. */
if (window_wants_header_line (w)
!= MATRIX_HEADER_LINE_ROW (w->current_matrix)->mode_line_p)
return false;
@@ -18202,7 +19157,7 @@ try_window_reusing_current_matrix (struct window *w)
(start_row + i)->enabled_p = false;
/* Re-compute Y positions. */
- min_y = WINDOW_HEADER_LINE_HEIGHT (w);
+ min_y = WINDOW_TAB_LINE_HEIGHT (w) + WINDOW_HEADER_LINE_HEIGHT (w);
max_y = it.last_visible_y;
for (row = start_row + nrows_scrolled;
row < bottom_row;
@@ -18229,7 +19184,7 @@ try_window_reusing_current_matrix (struct window *w)
/* Disable lines in the current matrix which are now
below the window. */
for (++row; row < bottom_row; ++row)
- row->enabled_p = row->mode_line_p = false;
+ row->enabled_p = row->mode_line_p = row->tab_line_p = false;
}
/* Update window_end_pos etc.; last_reused_text_row is the last
@@ -18307,7 +19262,7 @@ try_window_reusing_current_matrix (struct window *w)
it.vpos = (MATRIX_ROW_VPOS (first_row_to_display, w->current_matrix)
- nrows_scrolled);
it.current_y = (first_row_to_display->y - first_reusable_row->y
- + WINDOW_HEADER_LINE_HEIGHT (w));
+ + WINDOW_TAB_LINE_HEIGHT (w) + WINDOW_HEADER_LINE_HEIGHT (w));
/* Display lines beginning with first_row_to_display in the
desired matrix. Set last_text_row to the last row displayed
@@ -18340,7 +19295,7 @@ try_window_reusing_current_matrix (struct window *w)
/* Scroll the display. */
run.current_y = first_reusable_row->y;
- run.desired_y = WINDOW_HEADER_LINE_HEIGHT (w);
+ run.desired_y = WINDOW_TAB_LINE_HEIGHT (w) + WINDOW_HEADER_LINE_HEIGHT (w);
run.height = it.last_visible_y - run.current_y;
dy = run.current_y - run.desired_y;
@@ -18358,7 +19313,7 @@ try_window_reusing_current_matrix (struct window *w)
/* Adjust Y positions of reused rows. */
bottom_row = MATRIX_BOTTOM_TEXT_ROW (w->current_matrix, w);
- min_y = WINDOW_HEADER_LINE_HEIGHT (w);
+ min_y = WINDOW_TAB_LINE_HEIGHT (w) + WINDOW_HEADER_LINE_HEIGHT (w);
max_y = it.last_visible_y;
for (row = first_reusable_row; row < first_row_to_display; ++row)
{
@@ -19316,6 +20271,7 @@ try_window_id (struct window *w)
= MATRIX_ROW_VPOS (first_unchanged_at_end_row, w->current_matrix);
int from = WINDOW_TOP_EDGE_LINE (w) + from_vpos;
int end = (WINDOW_TOP_EDGE_LINE (w)
+ + window_wants_tab_line (w)
+ window_wants_header_line (w)
+ window_internal_height (w));
@@ -19494,7 +20450,7 @@ try_window_id (struct window *w)
{
/* Displayed to end of window, but no line containing text was
displayed. Lines were deleted at the end of the window. */
- bool first_vpos = window_wants_header_line (w);
+ int first_vpos = window_wants_tab_line (w) + window_wants_header_line (w);
int vpos = w->window_end_vpos;
struct glyph_row *current_row = current_matrix->rows + vpos;
struct glyph_row *desired_row = desired_matrix->rows + vpos;
@@ -19866,6 +20822,39 @@ GLYPHS > 1 or omitted means dump glyphs in long form. */)
}
+DEFUN ("dump-tab-bar-row", Fdump_tab_bar_row, Sdump_tab_bar_row, 1, 2, "P",
+ doc: /* Dump glyph row ROW of the tab-bar of the current frame to stderr.
+Interactively, ROW is the prefix numeric argument and defaults to zero.
+GLYPHS 0 means don't dump glyphs.
+GLYPHS 1 means dump glyphs in short form.
+GLYPHS > 1 or omitted means dump glyphs in long form.
+
+If there's no tab-bar, or if the tab-bar is not drawn by Emacs,
+do nothing. */)
+ (Lisp_Object row, Lisp_Object glyphs)
+{
+#if defined (HAVE_WINDOW_SYSTEM)
+ struct frame *sf = SELECTED_FRAME ();
+ struct glyph_matrix *m = WINDOWP (sf->tab_bar_window)
+ ? XWINDOW (sf->tab_bar_window)->current_matrix
+ : sf->current_matrix;
+ EMACS_INT vpos;
+
+ if (NILP (row))
+ vpos = WINDOWP (sf->tab_bar_window) ? 0 :
+ FRAME_MENU_BAR_LINES (sf) > 0 ? 1 : 0;
+ else
+ {
+ CHECK_FIXNUM (row);
+ vpos = XFIXNUM (row);
+ }
+ if (vpos >= 0 && vpos < m->nrows)
+ dump_glyph_row (MATRIX_ROW (m, vpos), vpos,
+ TYPE_RANGED_FIXNUMP (int, glyphs) ? XFIXNUM (glyphs) : 2);
+#endif
+ return Qnil;
+}
+
DEFUN ("dump-tool-bar-row", Fdump_tool_bar_row, Sdump_tool_bar_row, 1, 2, "P",
doc: /* Dump glyph row ROW of the tool-bar of the current frame to stderr.
Interactively, ROW is the prefix numeric argument and defaults to zero.
@@ -20233,7 +21222,7 @@ compute_line_metrics (struct it *it)
/* Compute how much of the line is visible. */
row->visible_height = row->height;
- min_y = WINDOW_HEADER_LINE_HEIGHT (it->w);
+ min_y = WINDOW_TAB_LINE_HEIGHT (it->w) + WINDOW_HEADER_LINE_HEIGHT (it->w);
max_y = WINDOW_BOX_HEIGHT_NO_MODE_LINE (it->w);
if (row->y < min_y)
@@ -20547,6 +21536,8 @@ extend_face_to_end_of_line (struct it *it)
/* Mode line and the header line don't have margins, and
likewise the frame's tool-bar window, if there is any. */
if (!(it->glyph_row->mode_line_p
+ || (WINDOWP (f->tab_bar_window)
+ && it->w == XWINDOW (f->tab_bar_window))
#ifndef HAVE_EXT_TOOL_BAR
|| (WINDOWP (f->tool_bar_window)
&& it->w == XWINDOW (f->tool_bar_window))
@@ -21743,9 +22734,10 @@ display_line (struct it *it, int cursor_vpos)
ptrdiff_t min_pos = ZV + 1, max_pos = 0;
ptrdiff_t min_bpos UNINIT, max_bpos UNINIT;
bool pending_handle_line_prefix = false;
+ int tab_line = window_wants_tab_line (it->w);
int header_line = window_wants_header_line (it->w);
bool hscroll_this_line = (cursor_vpos >= 0
- && it->vpos == cursor_vpos - header_line
+ && it->vpos == cursor_vpos - tab_line - header_line
&& hscrolling_current_line_p (it->w));
int first_visible_x = it->first_visible_x;
int last_visible_x = it->last_visible_x;
@@ -23786,6 +24778,18 @@ display_mode_lines (struct window *w)
++n;
}
+ if (window_wants_tab_line (w))
+ {
+ Lisp_Object window_tab_line_format
+ = window_parameter (w, Qtab_line_format);
+
+ display_mode_line (w, TAB_LINE_FACE_ID,
+ NILP (window_tab_line_format)
+ ? BVAR (current_buffer, tab_line_format)
+ : window_tab_line_format);
+ ++n;
+ }
+
if (window_wants_header_line (w))
{
Lisp_Object window_header_line_format
@@ -23807,10 +24811,10 @@ display_mode_lines (struct window *w)
}
-/* Display mode or header line of window W. FACE_ID specifies which
- line to display; it is either MODE_LINE_FACE_ID or
- HEADER_LINE_FACE_ID. FORMAT is the mode/header line format to
- display. Value is the pixel height of the mode/header line
+/* Display mode or header/tab line of window W. FACE_ID specifies which
+ line to display; it is either MODE_LINE_FACE_ID, HEADER_LINE_FACE_ID or
+ TAB_LINE_FACE_ID. FORMAT is the mode/header/tab line format to
+ display. Value is the pixel height of the mode/header/tab line
displayed. */
static int
@@ -23827,6 +24831,8 @@ display_mode_line (struct window *w, enum face_id face_id, Lisp_Object format)
prepare_desired_row (w, it.glyph_row, true);
it.glyph_row->mode_line_p = true;
+ if (face_id == TAB_LINE_FACE_ID)
+ it.glyph_row->tab_line_p = true;
/* FIXME: This should be controlled by a user option. But
supporting such an option is not trivial, since the mode line is
@@ -24544,6 +25550,8 @@ are the selected window and the WINDOW's buffer). */)
: EQ (face, Qmode_line) ? MODE_LINE_FACE_ID
: EQ (face, Qmode_line_inactive) ? MODE_LINE_INACTIVE_FACE_ID
: EQ (face, Qheader_line) ? HEADER_LINE_FACE_ID
+ : EQ (face, Qtab_line) ? TAB_LINE_FACE_ID
+ : EQ (face, Qtab_bar) ? TAB_BAR_FACE_ID
: EQ (face, Qtool_bar) ? TOOL_BAR_FACE_ID
: DEFAULT_FACE_ID;
@@ -29475,7 +30483,7 @@ gui_clear_end_of_line (struct window *w, struct glyph_row *updated_row,
to_x += area_left;
}
- min_y = WINDOW_HEADER_LINE_HEIGHT (w);
+ min_y = WINDOW_TAB_LINE_HEIGHT (w) + WINDOW_HEADER_LINE_HEIGHT (w);
from_y = WINDOW_TO_FRAME_PIXEL_Y (w, max (min_y, w->output_cursor.y));
to_y = WINDOW_TO_FRAME_PIXEL_Y (w, to_y);
@@ -29985,6 +30993,7 @@ erase_phys_cursor (struct window *w)
if (w->phys_cursor_type == HOLLOW_BOX_CURSOR)
{
int x, y;
+ int tab_line_height = WINDOW_TAB_LINE_HEIGHT (w);
int header_line_height = WINDOW_HEADER_LINE_HEIGHT (w);
int width;
@@ -30000,7 +31009,7 @@ erase_phys_cursor (struct window *w)
x = 0;
}
width = min (width, window_box_width (w, TEXT_AREA) - x);
- y = WINDOW_TO_FRAME_PIXEL_Y (w, max (header_line_height, cursor_row->y));
+ y = WINDOW_TO_FRAME_PIXEL_Y (w, max (tab_line_height, max (header_line_height, cursor_row->y)));
x = WINDOW_TEXT_TO_FRAME_PIXEL_X (w, x);
if (width > 0)
@@ -30346,12 +31355,13 @@ show_mouse_face (Mouse_HLInfo *hlinfo, enum draw_glyphs_face draw)
/* Change the mouse cursor. */
if (FRAME_WINDOW_P (f) && NILP (track_mouse))
{
-#ifndef HAVE_EXT_TOOL_BAR
if (draw == DRAW_NORMAL_TEXT
- && !EQ (hlinfo->mouse_face_window, f->tool_bar_window))
+#ifndef HAVE_EXT_TOOL_BAR
+ && !EQ (hlinfo->mouse_face_window, f->tool_bar_window)
+#endif
+ && !EQ (hlinfo->mouse_face_window, f->tab_bar_window))
FRAME_RIF (f)->define_frame_cursor (f, FRAME_OUTPUT_DATA (f)->text_cursor);
else
-#endif
if (draw == DRAW_MOUSE_FACE)
FRAME_RIF (f)->define_frame_cursor (f, FRAME_OUTPUT_DATA (f)->hand_cursor);
else
@@ -31347,7 +32357,7 @@ note_mode_line_or_margin_highlight (Lisp_Object window, int x, int y,
struct glyph * glyph = NULL, * row_start_glyph = NULL;
struct glyph_row *row UNINIT;
- if (area == ON_MODE_LINE || area == ON_HEADER_LINE)
+ if (area == ON_MODE_LINE || area == ON_HEADER_LINE || area == ON_TAB_LINE)
{
int x0;
struct glyph *end;
@@ -31359,7 +32369,9 @@ note_mode_line_or_margin_highlight (Lisp_Object window, int x, int y,
row = (area == ON_MODE_LINE
? MATRIX_MODE_LINE_ROW (w->current_matrix)
- : MATRIX_HEADER_LINE_ROW (w->current_matrix));
+ : (area == ON_TAB_LINE
+ ? MATRIX_TAB_LINE_ROW (w->current_matrix)
+ : MATRIX_HEADER_LINE_ROW (w->current_matrix)));
/* Find the glyph under the mouse pointer. */
if (row->mode_line_p && row->enabled_p)
@@ -31474,7 +32486,8 @@ note_mode_line_or_margin_highlight (Lisp_Object window, int x, int y,
/* Change the mouse pointer according to what is under X/Y. */
if (NILP (pointer)
- && (area == ON_MODE_LINE || area == ON_HEADER_LINE))
+ && (area == ON_MODE_LINE || area == ON_HEADER_LINE
+ || area == ON_TAB_LINE))
{
Lisp_Object map;
@@ -31500,7 +32513,8 @@ note_mode_line_or_margin_highlight (Lisp_Object window, int x, int y,
{
mouse_face = Fget_text_property (pos, Qmouse_face, string);
if (!NILP (Vmouse_highlight) && !NILP (mouse_face)
- && ((area == ON_MODE_LINE) || (area == ON_HEADER_LINE))
+ && ((area == ON_MODE_LINE) || (area == ON_HEADER_LINE)
+ || (area == ON_TAB_LINE))
&& glyph)
{
Lisp_Object b, e;
@@ -31571,7 +32585,11 @@ note_mode_line_or_margin_highlight (Lisp_Object window, int x, int y,
hpos = x - gpos;
vpos = (area == ON_MODE_LINE
? (w->current_matrix)->nrows - 1
- : 0);
+ : (area == ON_TAB_LINE
+ ? 0
+ : (w->current_matrix->tab_line_p
+ ? 1
+ : 0)));
/* If GLYPH's position is included in the region that is
already drawn in mouse face, we have nothing to do. */
@@ -31627,7 +32645,8 @@ note_mode_line_or_margin_highlight (Lisp_Object window, int x, int y,
/* If mouse-face doesn't need to be shown, clear any existing
mouse-face. */
- if ((area == ON_MODE_LINE || area == ON_HEADER_LINE) && !mouse_face_shown)
+ if ((area == ON_MODE_LINE || area == ON_HEADER_LINE
+ || area == ON_TAB_LINE) && !mouse_face_shown)
clear_mouse_face (hlinfo);
define_frame_cursor1 (f, cursor, pointer);
@@ -31671,7 +32690,7 @@ note_mouse_highlight (struct frame *f, int x, int y)
return;
/* Which window is that in? */
- window = window_from_coordinates (f, x, y, &part, true);
+ window = window_from_coordinates (f, x, y, &part, true, true);
/* If displaying active text in another window, clear that. */
if (! EQ (window, hlinfo->mouse_face_window)
@@ -31680,7 +32699,8 @@ note_mouse_highlight (struct frame *f, int x, int y)
&& !NILP (window)
&& part != ON_TEXT
&& part != ON_MODE_LINE
- && part != ON_HEADER_LINE))
+ && part != ON_HEADER_LINE
+ && part != ON_TAB_LINE))
clear_mouse_face (hlinfo);
/* Reset help_echo_string. It will get recomputed below. */
@@ -31748,6 +32768,16 @@ note_mouse_highlight (struct frame *f, int x, int y)
w = XWINDOW (window);
frame_to_window_pixel_xy (w, &x, &y);
+#if defined (HAVE_WINDOW_SYSTEM)
+ /* Handle tab-bar window differently since it doesn't display a
+ buffer. */
+ if (EQ (window, f->tab_bar_window))
+ {
+ note_tab_bar_highlight (f, x, y);
+ return;
+ }
+#endif
+
#if defined (HAVE_WINDOW_SYSTEM) && ! defined (HAVE_EXT_TOOL_BAR)
/* Handle tool-bar window differently since it doesn't display a
buffer. */
@@ -31759,7 +32789,7 @@ note_mouse_highlight (struct frame *f, int x, int y)
#endif
/* Mouse is on the mode, header line or margin? */
- if (part == ON_MODE_LINE || part == ON_HEADER_LINE
+ if (part == ON_MODE_LINE || part == ON_HEADER_LINE || part == ON_TAB_LINE
|| part == ON_LEFT_MARGIN || part == ON_RIGHT_MARGIN)
{
note_mode_line_or_margin_highlight (window, x, y, part);
@@ -32774,6 +33804,10 @@ expose_frame (struct frame *f, int x, int y, int w, int h)
r.x, r.y, r.width, r.height);
mouse_face_overwritten_p = expose_window_tree (XWINDOW (f->root_window), &r);
+ if (WINDOWP (f->tab_bar_window))
+ mouse_face_overwritten_p
+ |= expose_window (XWINDOW (f->tab_bar_window), &r);
+
#ifndef HAVE_EXT_TOOL_BAR
if (WINDOWP (f->tool_bar_window))
mouse_face_overwritten_p
@@ -32914,11 +33948,13 @@ be let-bound around code that needs to disable messages temporarily. */);
defsubr (&Sdump_frame_glyph_matrix);
defsubr (&Sdump_glyph_matrix);
defsubr (&Sdump_glyph_row);
+ defsubr (&Sdump_tab_bar_row);
defsubr (&Sdump_tool_bar_row);
defsubr (&Strace_redisplay);
defsubr (&Strace_to_stderr);
#endif
#ifdef HAVE_WINDOW_SYSTEM
+ defsubr (&Stab_bar_height);
defsubr (&Stool_bar_height);
defsubr (&Slookup_image_map);
#endif
@@ -33304,6 +34340,18 @@ make sure that (1) your window manager has focus follow the mouse and
of your window manager. */);
Vmouse_autoselect_window = Qnil;
+ DEFVAR_LISP ("auto-resize-tab-bars", Vauto_resize_tab_bars,
+ doc: /* Non-nil means automatically resize tab-bars.
+This dynamically changes the tab-bar's height to the minimum height
+that is needed to make all tab-bar items visible.
+If value is `grow-only', the tab-bar's height is only increased
+automatically; to decrease the tab-bar height, use \\[recenter]. */);
+ Vauto_resize_tab_bars = Qt;
+
+ DEFVAR_BOOL ("auto-raise-tab-bar-buttons", auto_raise_tab_bar_buttons_p,
+ doc: /* Non-nil means raise tab-bar buttons when the mouse moves over them. */);
+ auto_raise_tab_bar_buttons_p = true;
+
DEFVAR_LISP ("auto-resize-tool-bars", Vauto_resize_tool_bars,
doc: /* Non-nil means automatically resize tool-bars.
This dynamically changes the tool-bar's height to the minimum height
@@ -33326,6 +34374,27 @@ window, nil if it's okay to leave the cursor partially-visible. */);
Vmake_cursor_line_fully_visible = Qt;
DEFSYM (Qmake_cursor_line_fully_visible, "make-cursor-line-fully-visible");
+ DEFSYM (Qclose_tab, "close-tab");
+ DEFVAR_LISP ("tab-bar-border", Vtab_bar_border,
+ doc: /* Border below tab-bar in pixels.
+If an integer, use it as the height of the border.
+If it is one of `internal-border-width' or `border-width', use the
+value of the corresponding frame parameter.
+Otherwise, no border is added below the tab-bar. */);
+ Vtab_bar_border = Qinternal_border_width;
+
+ DEFVAR_LISP ("tab-bar-button-margin", Vtab_bar_button_margin,
+ doc: /* Margin around tab-bar buttons in pixels.
+If an integer, use that for both horizontal and vertical margins.
+Otherwise, value should be a pair of integers `(HORZ . VERT)' with
+HORZ specifying the horizontal margin, and VERT specifying the
+vertical margin. */);
+ Vtab_bar_button_margin = make_fixnum (DEFAULT_TAB_BAR_BUTTON_MARGIN);
+
+ DEFVAR_INT ("tab-bar-button-relief", tab_bar_button_relief,
+ doc: /* Relief thickness of tab-bar buttons. */);
+ tab_bar_button_relief = DEFAULT_TAB_BAR_BUTTON_RELIEF;
+
DEFVAR_LISP ("tool-bar-border", Vtool_bar_border,
doc: /* Border below tool-bar in pixels.
If an integer, use it as the height of the border.
diff --git a/src/xfaces.c b/src/xfaces.c
index 946401b9e99..0c99eea1567 100644
--- a/src/xfaces.c
+++ b/src/xfaces.c
@@ -4586,6 +4586,8 @@ lookup_basic_face (struct window *w, struct frame *f, int face_id)
case MODE_LINE_FACE_ID: name = Qmode_line; break;
case MODE_LINE_INACTIVE_FACE_ID: name = Qmode_line_inactive; break;
case HEADER_LINE_FACE_ID: name = Qheader_line; break;
+ case TAB_LINE_FACE_ID: name = Qtab_line; break;
+ case TAB_BAR_FACE_ID: name = Qtab_bar; break;
case TOOL_BAR_FACE_ID: name = Qtool_bar; break;
case FRINGE_FACE_ID: name = Qfringe; break;
case SCROLL_BAR_FACE_ID: name = Qscroll_bar; break;
@@ -5293,6 +5295,8 @@ realize_basic_faces (struct frame *f)
realize_named_face (f, Qwindow_divider_last_pixel,
WINDOW_DIVIDER_LAST_PIXEL_FACE_ID);
realize_named_face (f, Qinternal_border, INTERNAL_BORDER_FACE_ID);
+ realize_named_face (f, Qtab_bar, TAB_BAR_FACE_ID);
+ realize_named_face (f, Qtab_line, TAB_LINE_FACE_ID);
/* Reflect changes in the `menu' face in menu bars. */
if (FRAME_FACE_CACHE (f)->menu_face_changed_p)
@@ -6579,7 +6583,9 @@ syms_of_xfaces (void)
/* Names of basic faces. */
DEFSYM (Qdefault, "default");
DEFSYM (Qtool_bar, "tool-bar");
+ DEFSYM (Qtab_bar, "tab-bar");
DEFSYM (Qfringe, "fringe");
+ DEFSYM (Qtab_line, "tab-line");
DEFSYM (Qheader_line, "header-line");
DEFSYM (Qscroll_bar, "scroll-bar");
DEFSYM (Qmenu, "menu");
diff --git a/src/xfns.c b/src/xfns.c
index 31ae4cc225b..20e63a26501 100644
--- a/src/xfns.c
+++ b/src/xfns.c
@@ -1602,6 +1602,94 @@ x_set_menu_bar_lines (struct frame *f, Lisp_Object value, Lisp_Object oldval)
}
+/* Set the number of lines used for the tab bar of frame F to VALUE.
+ VALUE not an integer, or < 0 means set the lines to zero. OLDVAL
+ is the old number of tab bar lines. This function changes the
+ height of all windows on frame F to match the new tab bar height.
+ The frame's height doesn't change. */
+
+static void
+x_set_tab_bar_lines (struct frame *f, Lisp_Object value, Lisp_Object oldval)
+{
+ int nlines;
+
+ /* Treat tab bars like menu bars. */
+ if (FRAME_MINIBUF_ONLY_P (f))
+ return;
+
+ /* Use VALUE only if an int >= 0. */
+ if (RANGED_FIXNUMP (0, value, INT_MAX))
+ nlines = XFIXNAT (value);
+ else
+ nlines = 0;
+
+ x_change_tab_bar_height (f, nlines * FRAME_LINE_HEIGHT (f));
+}
+
+
+/* Set the pixel height of the tab bar of frame F to HEIGHT. */
+void
+x_change_tab_bar_height (struct frame *f, int height)
+{
+ int unit = FRAME_LINE_HEIGHT (f);
+ int old_height = FRAME_TAB_BAR_HEIGHT (f);
+ int lines = (height + unit - 1) / unit;
+ Lisp_Object fullscreen;
+
+ /* Make sure we redisplay all windows in this frame. */
+ fset_redisplay (f);
+
+ /* Recalculate tab bar and frame text sizes. */
+ FRAME_TAB_BAR_HEIGHT (f) = height;
+ FRAME_TAB_BAR_LINES (f) = lines;
+ /* Store the `tab-bar-lines' and `height' frame parameters. */
+ store_frame_param (f, Qtab_bar_lines, make_fixnum (lines));
+ store_frame_param (f, Qheight, make_fixnum (FRAME_LINES (f)));
+
+ /* We also have to make sure that the internal border at the top of
+ the frame, below the menu bar or tab bar, is redrawn when the
+ tab bar disappears. This is so because the internal border is
+ below the tab bar if one is displayed, but is below the menu bar
+ if there isn't a tab bar. The tab bar draws into the area
+ below the menu bar. */
+ if (FRAME_X_WINDOW (f) && FRAME_TAB_BAR_HEIGHT (f) == 0)
+ {
+ clear_frame (f);
+ clear_current_matrices (f);
+ }
+
+ if ((height < old_height) && WINDOWP (f->tab_bar_window))
+ clear_glyph_matrix (XWINDOW (f->tab_bar_window)->current_matrix);
+
+ /* Recalculate tabbar height. */
+ f->n_tab_bar_rows = 0;
+ if (old_height == 0
+ && (!f->after_make_frame
+ || NILP (frame_inhibit_implied_resize)
+ || (CONSP (frame_inhibit_implied_resize)
+ && NILP (Fmemq (Qtab_bar_lines, frame_inhibit_implied_resize)))))
+ f->tab_bar_redisplayed = f->tab_bar_resized = false;
+
+ adjust_frame_size (f, -1, -1,
+ ((!f->tab_bar_resized
+ && (NILP (fullscreen =
+ get_frame_param (f, Qfullscreen))
+ || EQ (fullscreen, Qfullwidth))) ? 1
+ : (old_height == 0 || height == 0) ? 2
+ : 4),
+ false, Qtab_bar_lines);
+
+ f->tab_bar_resized = f->tab_bar_redisplayed;
+
+ /* adjust_frame_size might not have done anything, garbage frame
+ here. */
+ adjust_frame_glyphs (f);
+ SET_FRAME_GARBAGED (f);
+ if (FRAME_X_WINDOW (f))
+ x_clear_under_internal_border (f);
+}
+
+
/* Set the number of lines used for the tool bar of frame F to VALUE.
VALUE not an integer, or < 0 means set the lines to zero. OLDVAL
is the old number of tool bar lines. This function changes the
@@ -3922,6 +4010,10 @@ This function is an internal primitive--use `make-frame' instead. */)
NILP (Vmenu_bar_mode)
? make_fixnum (0) : make_fixnum (1),
NULL, NULL, RES_TYPE_NUMBER);
+ gui_default_parameter (f, parms, Qtab_bar_lines,
+ NILP (Vtab_bar_mode)
+ ? make_fixnum (0) : make_fixnum (1),
+ NULL, NULL, RES_TYPE_NUMBER);
gui_default_parameter (f, parms, Qtool_bar_lines,
NILP (Vtool_bar_mode)
? make_fixnum (0) : make_fixnum (1),
@@ -3941,7 +4033,7 @@ This function is an internal primitive--use `make-frame' instead. */)
RES_TYPE_BOOLEAN);
/* Compute the size of the X window. */
- window_prompting = gui_figure_window_size (f, parms, true,
+ window_prompting = gui_figure_window_size (f, parms, true, true,
&x_width, &x_height);
tem = gui_display_get_arg (dpyinfo, parms, Qunsplittable, 0, 0,
@@ -5060,6 +5152,7 @@ frame_geometry (Lisp_Object frame, Lisp_Object attribute)
int internal_border_width;
bool menu_bar_external = false, tool_bar_external = false;
int menu_bar_height = 0, menu_bar_width = 0;
+ int tab_bar_height = 0, tab_bar_width = 0;
int tool_bar_height = 0, tool_bar_width = 0;
if (FRAME_INITIAL_P (f) || !FRAME_X_P (f) || !FRAME_OUTER_WINDOW (f))
@@ -5130,6 +5223,12 @@ frame_geometry (Lisp_Object frame, Lisp_Object attribute)
#endif
menu_bar_width = menu_bar_height ? native_width : 0;
+ tab_bar_height = FRAME_TAB_BAR_HEIGHT (f);
+ tab_bar_width = (tab_bar_height
+ ? native_width - 2 * internal_border_width
+ : 0);
+ inner_top += tab_bar_height;
+
#ifdef HAVE_EXT_TOOL_BAR
tool_bar_external = true;
if (EQ (FRAME_TOOL_BAR_POSITION (f), Qleft))
@@ -5198,6 +5297,9 @@ frame_geometry (Lisp_Object frame, Lisp_Object attribute)
Fcons (Qmenu_bar_size,
Fcons (make_fixnum (menu_bar_width),
make_fixnum (menu_bar_height))),
+ Fcons (Qtab_bar_size,
+ Fcons (make_fixnum (tab_bar_width),
+ make_fixnum (tab_bar_height))),
Fcons (Qtool_bar_external, tool_bar_external ? Qt : Qnil),
Fcons (Qtool_bar_position, FRAME_TOOL_BAR_POSITION (f)),
Fcons (Qtool_bar_size,
@@ -6331,7 +6433,7 @@ x_create_tip_frame (struct x_display_info *dpyinfo, Lisp_Object parms)
"inhibitDoubleBuffering", "InhibitDoubleBuffering",
RES_TYPE_BOOLEAN);
- gui_figure_window_size (f, parms, false, &x_width, &x_height);
+ gui_figure_window_size (f, parms, false, false, &x_width, &x_height);
{
XSetWindowAttributes attrs;
@@ -7672,6 +7774,7 @@ frame_parm_handler x_frame_parm_handlers[] =
gui_set_vertical_scroll_bars,
gui_set_horizontal_scroll_bars,
gui_set_visibility,
+ x_set_tab_bar_lines,
x_set_tool_bar_lines,
x_set_scroll_bar_foreground,
x_set_scroll_bar_background,
diff --git a/src/xterm.c b/src/xterm.c
index 255b6c62d5a..5baa29a8466 100644
--- a/src/xterm.c
+++ b/src/xterm.c
@@ -3220,9 +3220,11 @@ x_draw_image_relief (struct glyph_string *s)
if (s->hl == DRAW_IMAGE_SUNKEN
|| s->hl == DRAW_IMAGE_RAISED)
{
- thick = (tool_bar_button_relief < 0
- ? DEFAULT_TOOL_BAR_BUTTON_RELIEF
- : min (tool_bar_button_relief, 1000000));
+ thick = (tab_bar_button_relief < 0
+ ? DEFAULT_TAB_BAR_BUTTON_RELIEF
+ : (tool_bar_button_relief < 0
+ ? DEFAULT_TOOL_BAR_BUTTON_RELIEF
+ : min (tool_bar_button_relief, 1000000)));
raised_p = s->hl == DRAW_IMAGE_RAISED;
}
else
@@ -3235,6 +3237,19 @@ x_draw_image_relief (struct glyph_string *s)
y1 = y + s->slice.height - 1;
extra_x = extra_y = 0;
+ if (s->face->id == TAB_BAR_FACE_ID)
+ {
+ if (CONSP (Vtab_bar_button_margin)
+ && FIXNUMP (XCAR (Vtab_bar_button_margin))
+ && FIXNUMP (XCDR (Vtab_bar_button_margin)))
+ {
+ extra_x = XFIXNUM (XCAR (Vtab_bar_button_margin));
+ extra_y = XFIXNUM (XCDR (Vtab_bar_button_margin));
+ }
+ else if (FIXNUMP (Vtab_bar_button_margin))
+ extra_x = extra_y = XFIXNUM (Vtab_bar_button_margin);
+ }
+
if (s->face->id == TOOL_BAR_FACE_ID)
{
if (CONSP (Vtool_bar_button_margin)
@@ -8396,10 +8411,11 @@ handle_one_xevent (struct x_display_info *dpyinfo,
/* If mouse-highlight is an integer, input clears out
mouse highlighting. */
if (!hlinfo->mouse_face_hidden && FIXNUMP (Vmouse_highlight)
-#if ! defined (USE_GTK)
&& (f == 0
- || !EQ (f->tool_bar_window, hlinfo->mouse_face_window))
+#if ! defined (USE_GTK)
+ || !EQ (f->tool_bar_window, hlinfo->mouse_face_window)
#endif
+ || !EQ (f->tab_bar_window, hlinfo->mouse_face_window))
)
{
clear_mouse_face (hlinfo);
@@ -8823,7 +8839,7 @@ handle_one_xevent (struct x_display_info *dpyinfo,
{
static Lisp_Object last_mouse_window;
Lisp_Object window = window_from_coordinates
- (f, event->xmotion.x, event->xmotion.y, 0, false);
+ (f, event->xmotion.x, event->xmotion.y, 0, false, false);
/* A window will be autoselected only when it is not
selected now and the last mouse movement event was
@@ -9034,6 +9050,7 @@ handle_one_xevent (struct x_display_info *dpyinfo,
{
/* If we decide we want to generate an event to be seen
by the rest of Emacs, we put it here. */
+ bool tab_bar_p = false;
bool tool_bar_p = false;
memset (&compose_status, 0, sizeof (compose_status));
@@ -9070,6 +9087,23 @@ handle_one_xevent (struct x_display_info *dpyinfo,
#endif
if (f)
{
+ /* Is this in the tab-bar? */
+ if (WINDOWP (f->tab_bar_window)
+ && WINDOW_TOTAL_LINES (XWINDOW (f->tab_bar_window)))
+ {
+ Lisp_Object window;
+ int x = event->xbutton.x;
+ int y = event->xbutton.y;
+
+ window = window_from_coordinates (f, x, y, 0, true, true);
+ tab_bar_p = EQ (window, f->tab_bar_window);
+
+ if (tab_bar_p && event->xbutton.button < 4)
+ handle_tab_bar_click
+ (f, x, y, event->xbutton.type == ButtonPress,
+ x_x_to_emacs_modifiers (dpyinfo, event->xbutton.state));
+ }
+
#if ! defined (USE_GTK)
/* Is this in the tool-bar? */
if (WINDOWP (f->tool_bar_window)
@@ -9079,7 +9113,7 @@ handle_one_xevent (struct x_display_info *dpyinfo,
int x = event->xbutton.x;
int y = event->xbutton.y;
- window = window_from_coordinates (f, x, y, 0, true);
+ window = window_from_coordinates (f, x, y, 0, true, true);
tool_bar_p = EQ (window, f->tool_bar_window);
if (tool_bar_p && event->xbutton.button < 4)
@@ -9089,7 +9123,7 @@ handle_one_xevent (struct x_display_info *dpyinfo,
}
#endif /* !USE_GTK */
- if (!tool_bar_p)
+ if (!tab_bar_p && !tool_bar_p)
#if defined (USE_X_TOOLKIT) || defined (USE_GTK)
if (! popup_activated ())
#endif
@@ -9136,6 +9170,8 @@ handle_one_xevent (struct x_display_info *dpyinfo,
{
dpyinfo->grabbed |= (1 << event->xbutton.button);
dpyinfo->last_mouse_frame = f;
+ if (f && !tab_bar_p)
+ f->last_tab_bar_item = -1;
#if ! defined (USE_GTK)
if (f && !tool_bar_p)
f->last_tool_bar_item = -1;
@@ -10142,6 +10178,7 @@ x_new_font (struct frame *f, Lisp_Object font_object, int fontset)
int unit, font_ascent, font_descent;
#ifndef USE_X_TOOLKIT
int old_menu_bar_height = FRAME_MENU_BAR_HEIGHT (f);
+ int old_tab_bar_height = FRAME_TAB_BAR_HEIGHT (f);
Lisp_Object fullscreen;
#endif
@@ -10161,6 +10198,7 @@ x_new_font (struct frame *f, Lisp_Object font_object, int fontset)
#ifndef USE_X_TOOLKIT
FRAME_MENU_BAR_HEIGHT (f) = FRAME_MENU_BAR_LINES (f) * FRAME_LINE_HEIGHT (f);
+ FRAME_TAB_BAR_HEIGHT (f) = FRAME_TAB_BAR_LINES (f) * FRAME_LINE_HEIGHT (f);
#endif
/* Compute character columns occupied by scrollbar.
@@ -10185,18 +10223,20 @@ x_new_font (struct frame *f, Lisp_Object font_object, int fontset)
FRAME_LINES (f) * FRAME_LINE_HEIGHT (f), 3,
false, Qfont);
#ifndef USE_X_TOOLKIT
- if (FRAME_MENU_BAR_HEIGHT (f) != old_menu_bar_height
+ if ((FRAME_MENU_BAR_HEIGHT (f) != old_menu_bar_height
+ || FRAME_TAB_BAR_HEIGHT (f) != old_tab_bar_height)
&& !f->after_make_frame
&& (EQ (frame_inhibit_implied_resize, Qt)
|| (CONSP (frame_inhibit_implied_resize)
&& NILP (Fmemq (Qfont, frame_inhibit_implied_resize))))
&& (NILP (fullscreen = get_frame_param (f, Qfullscreen))
|| EQ (fullscreen, Qfullwidth)))
- /* If the menu bar height changes, try to keep text height
+ /* If the menu/tab bar height changes, try to keep text height
constant. */
adjust_frame_size
(f, -1, FRAME_TEXT_HEIGHT (f) + FRAME_MENU_BAR_HEIGHT (f)
- - old_menu_bar_height, 1, false, Qfont);
+ + FRAME_TAB_BAR_HEIGHT (f)
+ - old_menu_bar_height - old_tab_bar_height, 1, false, Qfont);
#endif /* USE_X_TOOLKIT */
}
}
@@ -13466,6 +13506,7 @@ x_create_terminal (struct x_display_info *dpyinfo)
#if defined (USE_X_TOOLKIT) || defined (USE_GTK)
terminal->popup_dialog_hook = xw_popup_dialog;
#endif
+ terminal->change_tab_bar_height_hook = x_change_tab_bar_height;
#ifndef HAVE_EXT_TOOL_BAR
terminal->change_tool_bar_height_hook = x_change_tool_bar_height;
#endif
diff --git a/src/xterm.h b/src/xterm.h
index 985648a1d98..69af552e078 100644
--- a/src/xterm.h
+++ b/src/xterm.h
@@ -1178,6 +1178,7 @@ extern void initial_set_up_x_back_buffer (struct frame *f);
/* Defined in xfns.c. */
extern void x_real_positions (struct frame *, int *, int *);
+extern void x_change_tab_bar_height (struct frame *, int);
extern void x_change_tool_bar_height (struct frame *, int);
extern void x_implicitly_set_name (struct frame *, Lisp_Object, Lisp_Object);
extern void x_set_scroll_bar_default_width (struct frame *);