aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJuri Linkov2019-10-01 23:15:03 +0300
committerJuri Linkov2019-10-01 23:15:03 +0300
commit2698d3dba2e9858b026ed127d4de3f86810a5ef3 (patch)
tree8e9b8f194cfcad8af83a4174a0105bbc691f06d6
parent25f45d710e91a7c1049f056ff27bc3e6968f5624 (diff)
parent3f981a0a89bca47a207fb362485f07e7322bb145 (diff)
downloademacs-2698d3dba2e9858b026ed127d4de3f86810a5ef3.tar.gz
emacs-2698d3dba2e9858b026ed127d4de3f86810a5ef3.zip
Merge branch 'feature/tabs'
-rw-r--r--doc/emacs/custom.texi4
-rw-r--r--doc/emacs/display.texi6
-rw-r--r--doc/emacs/emacs.texi1
-rw-r--r--doc/emacs/frames.texi36
-rw-r--r--doc/emacs/glossary.texi9
-rw-r--r--doc/emacs/modes.texi6
-rw-r--r--doc/lispref/commands.texi43
-rw-r--r--doc/lispref/display.texi1
-rw-r--r--doc/lispref/windows.texi12
-rw-r--r--etc/NEWS28
-rw-r--r--etc/TODO14
-rw-r--r--etc/images/tabs/README8
-rw-r--r--etc/images/tabs/close.xpm16
-rw-r--r--etc/images/tabs/new.xpm16
-rw-r--r--lisp/cus-start.el5
-rw-r--r--lisp/frame.el42
-rw-r--r--lisp/loadup.el1
-rw-r--r--lisp/menu-bar.el10
-rw-r--r--lisp/mouse.el1
-rw-r--r--lisp/startup.el3
-rw-r--r--lisp/subr.el6
-rw-r--r--lisp/tab-bar.el764
-rw-r--r--lisp/tab-line.el362
-rw-r--r--lisp/window.el5
-rw-r--r--lisp/xt-mouse.el8
-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
53 files changed, 3871 insertions, 187 deletions
diff --git a/doc/emacs/custom.texi b/doc/emacs/custom.texi
index 0c2509e1cd6..7bb6142395f 100644
--- a/doc/emacs/custom.texi
+++ b/doc/emacs/custom.texi
@@ -2150,6 +2150,10 @@ The mouse was in a vertical scroll bar. (This is the only kind of
2150scroll bar Emacs currently supports.) 2150scroll bar Emacs currently supports.)
2151@item menu-bar 2151@item menu-bar
2152The mouse was in the menu bar. 2152The mouse was in the menu bar.
2153@item tab-bar
2154The mouse was in a tab bar.
2155@item tab-line
2156The mouse was in a tab line.
2153@item header-line 2157@item header-line
2154The mouse was in a header line. 2158The mouse was in a header line.
2155@ignore 2159@ignore
diff --git a/doc/emacs/display.texi b/doc/emacs/display.texi
index 6fc99bd2716..84363d0f0d2 100644
--- a/doc/emacs/display.texi
+++ b/doc/emacs/display.texi
@@ -722,6 +722,10 @@ Similar to @code{highlight} and @code{mode-line-highlight}, but used
722for mouse-sensitive portions of text on header lines. This is a 722for mouse-sensitive portions of text on header lines. This is a
723separate face because the @code{header-line} face might be customized 723separate face because the @code{header-line} face might be customized
724in a way that does not interact well with @code{highlight}. 724in a way that does not interact well with @code{highlight}.
725@item tab-line
726@cindex @code{tab-line} face
727Similar to @code{mode-line} for a window's tab line, which appears
728at the top of a window with tabs representing window buffers.
725@item vertical-border 729@item vertical-border
726@cindex @code{vertical-border} face 730@cindex @code{vertical-border} face
727This face is used for the vertical divider between windows on text 731This face is used for the vertical divider between windows on text
@@ -763,6 +767,8 @@ This face determines the visual appearance of the scroll bar.
763@xref{Scroll Bars}. 767@xref{Scroll Bars}.
764@item tool-bar 768@item tool-bar
765This face determines the color of tool bar icons. @xref{Tool Bars}. 769This face determines the color of tool bar icons. @xref{Tool Bars}.
770@item tab-bar
771This face determines the color of tab bar icons. @xref{Tab Bars}.
766@item menu 772@item menu
767@cindex menu bar appearance 773@cindex menu bar appearance
768@cindex @code{menu} face, no effect if customized 774@cindex @code{menu} face, no effect if customized
diff --git a/doc/emacs/emacs.texi b/doc/emacs/emacs.texi
index 17aaaea7364..28a0f562f89 100644
--- a/doc/emacs/emacs.texi
+++ b/doc/emacs/emacs.texi
@@ -540,6 +540,7 @@ Frames and Graphical Displays
540* Drag and Drop:: Using drag and drop to open files and insert text. 540* Drag and Drop:: Using drag and drop to open files and insert text.
541* Menu Bars:: Enabling and disabling the menu bar. 541* Menu Bars:: Enabling and disabling the menu bar.
542* Tool Bars:: Enabling and disabling the tool bar. 542* Tool Bars:: Enabling and disabling the tool bar.
543* Tab Bars:: Enabling and disabling the tab bar.
543* Dialog Boxes:: Controlling use of dialog boxes. 544* Dialog Boxes:: Controlling use of dialog boxes.
544* Tooltips:: Displaying information at the current mouse position. 545* Tooltips:: Displaying information at the current mouse position.
545* Mouse Avoidance:: Preventing the mouse pointer from obscuring text. 546* Mouse Avoidance:: Preventing the mouse pointer from obscuring text.
diff --git a/doc/emacs/frames.texi b/doc/emacs/frames.texi
index 367ac43a0a4..0003881fad1 100644
--- a/doc/emacs/frames.texi
+++ b/doc/emacs/frames.texi
@@ -58,6 +58,7 @@ for doing so on MS-DOS). Menus are supported on all text terminals.
58* Drag and Drop:: Using drag and drop to open files and insert text. 58* Drag and Drop:: Using drag and drop to open files and insert text.
59* Menu Bars:: Enabling and disabling the menu bar. 59* Menu Bars:: Enabling and disabling the menu bar.
60* Tool Bars:: Enabling and disabling the tool bar. 60* Tool Bars:: Enabling and disabling the tool bar.
61* Tab Bars:: Enabling and disabling the tab bar.
61* Dialog Boxes:: Controlling use of dialog boxes. 62* Dialog Boxes:: Controlling use of dialog boxes.
62* Tooltips:: Displaying information at the current mouse position. 63* Tooltips:: Displaying information at the current mouse position.
63* Mouse Avoidance:: Preventing the mouse pointer from obscuring text. 64* Mouse Avoidance:: Preventing the mouse pointer from obscuring text.
@@ -1214,6 +1215,41 @@ Parameters,,, elisp, The Emacs Lisp Reference Manual}. On macOS the
1214tool bar is hidden when the frame is put into fullscreen, but can be 1215tool bar is hidden when the frame is put into fullscreen, but can be
1215displayed by moving the mouse pointer to the top of the screen. 1216displayed by moving the mouse pointer to the top of the screen.
1216 1217
1218@node Tab Bars
1219@section Tab Bars
1220@cindex Tab Bar mode
1221@cindex mode, Tab Bar
1222@cindex tabs, tabbar
1223
1224 On graphical displays and on text terminals, Emacs puts a @dfn{tab bar}
1225at the top of each frame, just below the menu bar. This is a row of
1226tabs which you can click on with the mouse to switch window configurations.
1227
1228 Each tab on the tab bar represents a named persistent window
1229configuration. Its name is composed from the names of buffers
1230visible in windows of the window configuration. Clicking on the
1231tab name switches the current window configuration to the previously
1232used configuration of windows and buffers.
1233
1234 If you are using the desktop library to save and restore your
1235sessions, the tabs from the tab bar are recorded in the desktop file,
1236together with their associated window configurations.
1237
1238@findex tab-bar-mode
1239@vindex tab-bar-mode
1240 To toggle the use of tab bars, type @kbd{M-x tab-bar-mode}. This
1241command applies to all frames, including frames yet to be created. To
1242control the use of tab bars at startup, customize the variable
1243@code{tab-bar-mode}.
1244
1245@vindex tab-bar-new-tab-choice
1246@cindex Tab Bar new tab
1247 By default, Emacs follows the same behavior as when creating frames,
1248to start a new tab with the current buffer, i.e. the buffer
1249that was current before calling the command that adds a new tab.
1250To start a new tab with other buffers, customize the variable
1251@code{tab-bar-new-tab-choice}.
1252
1217@node Dialog Boxes 1253@node Dialog Boxes
1218@section Using Dialog Boxes 1254@section Using Dialog Boxes
1219@cindex dialog boxes 1255@cindex dialog boxes
diff --git a/doc/emacs/glossary.texi b/doc/emacs/glossary.texi
index ad16d72ddbf..30ddffab696 100644
--- a/doc/emacs/glossary.texi
+++ b/doc/emacs/glossary.texi
@@ -1360,6 +1360,15 @@ your buffers, unsaved edits, undo history, etc. @xref{Exiting}.
1360@key{TAB} is the tab character. In Emacs it is typically used for 1360@key{TAB} is the tab character. In Emacs it is typically used for
1361indentation or completion. 1361indentation or completion.
1362 1362
1363@item Tab Bar
1364The tab bar is a row of tabs at the top of an Emacs frame.
1365Clicking on one of these tabs switches named persistent window
1366configurations. @xref{Tab Bars}.
1367
1368@item Tab Line
1369The tab line is a line of tabs at the top of an Emacs window.
1370Clicking on one of these tabs switches window buffers.
1371
1363@anchor{Glossary---Tags Table} 1372@anchor{Glossary---Tags Table}
1364@item Tags Table 1373@item Tags Table
1365A tags table is a file that serves as an index to the function 1374A tags table is a file that serves as an index to the function
diff --git a/doc/emacs/modes.texi b/doc/emacs/modes.texi
index e01dfa2677b..64034d71860 100644
--- a/doc/emacs/modes.texi
+++ b/doc/emacs/modes.texi
@@ -296,6 +296,12 @@ but the tool bar is only displayed on graphical terminals. @xref{Tool
296Bars}. 296Bars}.
297 297
298@item 298@item
299Tab Bar mode gives each frame a tab bar. @xref{Tab Bars}.
300
301@item
302Tab Line mode gives each window a tab line.
303
304@item
299Transient Mark mode highlights the region, and makes many Emacs 305Transient Mark mode highlights the region, and makes many Emacs
300commands operate on the region when the mark is active. It is enabled 306commands operate on the region when the mark is active. It is enabled
301by default. @xref{Mark}. 307by default. @xref{Mark}.
diff --git a/doc/lispref/commands.texi b/doc/lispref/commands.texi
index 1fd56d02841..a351917b852 100644
--- a/doc/lispref/commands.texi
+++ b/doc/lispref/commands.texi
@@ -1348,8 +1348,8 @@ button. @xref{Repeat Events}.
1348@var{position} slot of a click event, you should typically use the 1348@var{position} slot of a click event, you should typically use the
1349functions documented in @ref{Accessing Mouse}. The explicit format of 1349functions documented in @ref{Accessing Mouse}. The explicit format of
1350the list depends on where the click occurred. For clicks in the text 1350the list depends on where the click occurred. For clicks in the text
1351area, mode line, header line, or in the fringe or marginal areas, the 1351area, mode line, header line, tab line, or in the fringe or marginal
1352mouse position list has the form 1352areas, the mouse position list has the form
1353 1353
1354@example 1354@example
1355(@var{window} @var{pos-or-area} (@var{x} . @var{y}) @var{timestamp} 1355(@var{window} @var{pos-or-area} (@var{x} . @var{y}) @var{timestamp}
@@ -1368,8 +1368,9 @@ The window in which the click occurred.
1368The buffer position of the character clicked on in the text area; or, 1368The buffer position of the character clicked on in the text area; or,
1369if the click was outside the text area, the window area where it 1369if the click was outside the text area, the window area where it
1370occurred. It is one of the symbols @code{mode-line}, 1370occurred. It is one of the symbols @code{mode-line},
1371@code{header-line}, @code{vertical-line}, @code{left-margin}, 1371@code{header-line}, @code{tab-line}, @code{vertical-line},
1372@code{right-margin}, @code{left-fringe}, or @code{right-fringe}. 1372@code{left-margin}, @code{right-margin}, @code{left-fringe}, or
1373@code{right-fringe}.
1373 1374
1374In one special case, @var{pos-or-area} is a list containing a symbol 1375In one special case, @var{pos-or-area} is a list containing a symbol
1375(one of the symbols listed above) instead of just the symbol. This 1376(one of the symbols listed above) instead of just the symbol. This
@@ -1380,12 +1381,12 @@ by Emacs. @xref{Key Sequence Input}.
1380The relative pixel coordinates of the click. For clicks in the text 1381The relative pixel coordinates of the click. For clicks in the text
1381area of a window, the coordinate origin @code{(0 . 0)} is taken to be 1382area of a window, the coordinate origin @code{(0 . 0)} is taken to be
1382the top left corner of the text area. @xref{Window Sizes}. For 1383the top left corner of the text area. @xref{Window Sizes}. For
1383clicks in a mode line or header line, the coordinate origin is the top 1384clicks in a mode line, header line or tab line, the coordinate origin
1384left corner of the window itself. For fringes, margins, and the 1385is the top left corner of the window itself. For fringes, margins,
1385vertical border, @var{x} does not have meaningful data. For fringes 1386and the vertical border, @var{x} does not have meaningful data.
1386and margins, @var{y} is relative to the bottom edge of the header 1387For fringes and margins, @var{y} is relative to the bottom edge of the
1387line. In all cases, the @var{x} and @var{y} coordinates increase 1388header line. In all cases, the @var{x} and @var{y} coordinates
1388rightward and downward respectively. 1389increase rightward and downward respectively.
1389 1390
1390@item @var{timestamp} 1391@item @var{timestamp}
1391The time at which the event occurred, as an integer number of 1392The time at which the event occurred, as an integer number of
@@ -1407,17 +1408,18 @@ The position in the string where the click occurred.
1407@item @var{text-pos} 1408@item @var{text-pos}
1408For clicks on a marginal area or on a fringe, this is the buffer 1409For clicks on a marginal area or on a fringe, this is the buffer
1409position of the first visible character in the corresponding line in 1410position of the first visible character in the corresponding line in
1410the window. For clicks on the mode line or the header line, this is 1411the window. For clicks on the mode line, the header line or the tab
1411@code{nil}. For other events, it is the buffer position closest to 1412line, this is @code{nil}. For other events, it is the buffer position
1412the click. 1413closest to the click.
1413 1414
1414@item @var{col}, @var{row} 1415@item @var{col}, @var{row}
1415These are the actual column and row coordinate numbers of the glyph 1416These are the actual column and row coordinate numbers of the glyph
1416under the @var{x}, @var{y} position. If @var{x} lies beyond the last 1417under the @var{x}, @var{y} position. If @var{x} lies beyond the last
1417column of actual text on its line, @var{col} is reported by adding 1418column of actual text on its line, @var{col} is reported by adding
1418fictional extra columns that have the default character width. Row 0 1419fictional extra columns that have the default character width.
1419is taken to be the header line if the window has one, or the topmost 1420Row 0 is taken to be the header line if the window has one, or Row 1
1420row of the text area otherwise. Column 0 is taken to be the leftmost 1421if the window also has the tab line, or the topmost row of
1422the text area otherwise. Column 0 is taken to be the leftmost
1421column of the text area for clicks on a window text area, or the 1423column of the text area for clicks on a window text area, or the
1422leftmost mode line or header line column for clicks there. For clicks 1424leftmost mode line or header line column for clicks there. For clicks
1423on fringes or vertical borders, these have no meaningful data. For 1425on fringes or vertical borders, these have no meaningful data. For
@@ -2094,7 +2096,8 @@ computed values.)
2094 2096
2095Note that @var{row} is counted from the top of the text area. If the 2097Note that @var{row} is counted from the top of the text area. If the
2096window given by @var{position} possesses a header line (@pxref{Header 2098window given by @var{position} possesses a header line (@pxref{Header
2097Lines}), it is @emph{not} included in the @var{row} count. 2099Lines}) or a tab line, they are @emph{not} included in the @var{row}
2100count.
2098@end defun 2101@end defun
2099 2102
2100@defun posn-actual-col-row position 2103@defun posn-actual-col-row position
@@ -2452,12 +2455,14 @@ button-down events entirely. It also reshuffles focus events and
2452miscellaneous window events so that they never appear in a key sequence 2455miscellaneous window events so that they never appear in a key sequence
2453with any other events. 2456with any other events.
2454 2457
2458@cindex @code{tab-line} prefix key
2455@cindex @code{header-line} prefix key 2459@cindex @code{header-line} prefix key
2456@cindex @code{mode-line} prefix key 2460@cindex @code{mode-line} prefix key
2457@cindex @code{vertical-line} prefix key 2461@cindex @code{vertical-line} prefix key
2458@cindex @code{horizontal-scroll-bar} prefix key 2462@cindex @code{horizontal-scroll-bar} prefix key
2459@cindex @code{vertical-scroll-bar} prefix key 2463@cindex @code{vertical-scroll-bar} prefix key
2460@cindex @code{menu-bar} prefix key 2464@cindex @code{menu-bar} prefix key
2465@cindex @code{tab-bar} prefix key
2461@cindex mouse events, in special parts of frame 2466@cindex mouse events, in special parts of frame
2462When mouse events occur in special parts of a window, such as a mode 2467When mouse events occur in special parts of a window, such as a mode
2463line or a scroll bar, the event type shows nothing special---it is the 2468line or a scroll bar, the event type shows nothing special---it is the
@@ -2465,8 +2470,8 @@ same symbol that would normally represent that combination of mouse
2465button and modifier keys. The information about the window part is kept 2470button and modifier keys. The information about the window part is kept
2466elsewhere in the event---in the coordinates. But 2471elsewhere in the event---in the coordinates. But
2467@code{read-key-sequence} translates this information into imaginary 2472@code{read-key-sequence} translates this information into imaginary
2468prefix keys, all of which are symbols: @code{header-line}, 2473prefix keys, all of which are symbols: @code{tab-line}, @code{header-line},
2469@code{horizontal-scroll-bar}, @code{menu-bar}, @code{mode-line}, 2474@code{horizontal-scroll-bar}, @code{menu-bar}, @code{tab-bar}, @code{mode-line},
2470@code{vertical-line}, and @code{vertical-scroll-bar}. You can define 2475@code{vertical-line}, and @code{vertical-scroll-bar}. You can define
2471meanings for mouse clicks in special window parts by defining key 2476meanings for mouse clicks in special window parts by defining key
2472sequences using these imaginary prefix keys. 2477sequences using these imaginary prefix keys.
diff --git a/doc/lispref/display.texi b/doc/lispref/display.texi
index 64b24f712ae..494bf0d3f7e 100644
--- a/doc/lispref/display.texi
+++ b/doc/lispref/display.texi
@@ -2944,6 +2944,7 @@ If the text lies within the mode line of the selected window, Emacs
2944applies the @code{mode-line} face. For the mode line of a 2944applies the @code{mode-line} face. For the mode line of a
2945non-selected window, Emacs applies the @code{mode-line-inactive} face. 2945non-selected window, Emacs applies the @code{mode-line-inactive} face.
2946For a header line, Emacs applies the @code{header-line} face. 2946For a header line, Emacs applies the @code{header-line} face.
2947For a tab line, Emacs applies the @code{tab-line} face.
2947 2948
2948@item 2949@item
2949If the text comes from an overlay string via @code{before-string} or 2950If the text comes from an overlay string via @code{before-string} or
diff --git a/doc/lispref/windows.texi b/doc/lispref/windows.texi
index 39d3960c9a2..f05a6db1761 100644
--- a/doc/lispref/windows.texi
+++ b/doc/lispref/windows.texi
@@ -5558,6 +5558,9 @@ The coordinates are in the mode line of @var{window}.
5558@item header-line 5558@item header-line
5559The coordinates are in the header line of @var{window}. 5559The coordinates are in the header line of @var{window}.
5560 5560
5561@item tab-line
5562The coordinates are in the tab line of @var{window}.
5563
5561@item right-divider 5564@item right-divider
5562The coordinates are in the divider separating @var{window} from a 5565The coordinates are in the divider separating @var{window} from a
5563window on the right. 5566window on the right.
@@ -6115,6 +6118,15 @@ to suppress display of a header line for this window. Display and
6115contents of the header line on other windows showing this buffer are not 6118contents of the header line on other windows showing this buffer are not
6116affected. 6119affected.
6117 6120
6121@item tab-line-format
6122@vindex tab-line-format@r{, a window parameter}
6123This parameter replaces the value of the buffer-local variable
6124@code{tab-line-format} (@pxref{Mode Line Basics}) of this window's
6125buffer whenever this window is displayed. The symbol @code{none} means
6126to suppress display of a tab line for this window. Display and
6127contents of the tab line on other windows showing this buffer are not
6128affected.
6129
6118@item min-margins 6130@item min-margins
6119@vindex min-margins@r{, a window parameter} 6131@vindex min-margins@r{, a window parameter}
6120The value of this parameter is a cons cell whose @sc{car} and 6132The value of this parameter is a cons cell whose @sc{car} and
diff --git a/etc/NEWS b/etc/NEWS
index 04e2657e7c6..00a01999a73 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -2030,6 +2030,34 @@ file-local variable, you may need to update the value.
2030 2030
2031* New Modes and Packages in Emacs 27.1 2031* New Modes and Packages in Emacs 27.1
2032 2032
2033** 'tab-bar-mode' enables the tab-bar at the top of each frame,
2034to switch named persistent window configurations in it using tabs.
2035New tab-based keybindings (similar to frame-based commands):
2036'C-x 6 2' creates a new tab;
2037'C-x 6 0' deletes the current tab;
2038'C-x 6 b' switches to buffer in another tab;
2039'C-x 6 f' and 'C-x 6 C-f' edit file in another tab;
2040'C-TAB' switches to the next tab;
2041'S-C-TAB' switches to the previous tab.
2042
2043Also it's possible to switch named persistent window configurations
2044without having graphical access to the tab-bar, even on a tty
2045or when 'tab-bar-mode' is disabled, with these commands:
2046'tab-new' creates a new window configuration;
2047'tab-close' deletes the current window configuration;
2048'tab-select' switches to the window configuration by its name;
2049'tab-previous' switches to the previous window configuration;
2050'tab-next' switches to the next window configuration;
2051'tab-list' displays a list of named window configurations for switching.
2052
2053** 'global-tab-line-mode' enables the tab-line above each window to
2054switch buffers in it to previous/next buffers. Selecting a previous
2055window-local tab is the same as running 'C-x <left>' (previous-buffer),
2056selecting a next tab switches to the tab available by 'C-x <right>'
2057(next-buffer). Clicking on the plus icon adds a new buffer to the
2058window-local tab-line of window buffers. Using the mouse wheel on the
2059tab-line scrolls the window buffers whose names are displayed in tabs.
2060
2033** fileloop.el lets one setup multifile operations like search&replace. 2061** fileloop.el lets one setup multifile operations like search&replace.
2034 2062
2035+++ 2063+++
diff --git a/etc/TODO b/etc/TODO
index 67de79f8a97..2c36f5bfe4b 100644
--- a/etc/TODO
+++ b/etc/TODO
@@ -324,20 +324,6 @@ consistency checks that make sure the new code computes the same results
324as the old code. And once that works well, we can remove the old code 324as the old code. And once that works well, we can remove the old code
325and old fields. 325and old fields.
326 326
327** Having tabs above a window to switch buffers in it.
328
329** "Perspectives" are named persistent window configurations. We have
330had the window configuration mechanism in GNU Emacs since the
331beginning but we have never developed a good user interface to take
332advantage of them. Eclipse's user interface seems to be good.
333
334Perspectives work well even if you do the equivalent of C-x 4 C-f
335because of the distinction between view windows vs file windows. In
336Emacs this is more or less the "dedicated window" feature, but we have
337never really made it work for this.
338
339Perspectives also need to interact with the tabs.
340
341** FFI (foreign function interface) 327** FFI (foreign function interface)
342See eg https://lists.gnu.org/r/emacs-devel/2013-10/msg00246.html 328See eg https://lists.gnu.org/r/emacs-devel/2013-10/msg00246.html
343 329
diff --git a/etc/images/tabs/README b/etc/images/tabs/README
new file mode 100644
index 00000000000..1e9f4e5b595
--- /dev/null
+++ b/etc/images/tabs/README
@@ -0,0 +1,8 @@
1This directory contains icons for the Tabs user interface.
2
3COPYRIGHT AND LICENSE INFORMATION FOR IMAGE FILES
4
5Files: close.xpm new.xpm
6Author: Juri Linkov <juri@linkov.net>
7Copyright (C) 2019 Free Software Foundation, Inc.
8License: GNU General Public License version 3 or later (see COPYING)
diff --git a/etc/images/tabs/close.xpm b/etc/images/tabs/close.xpm
new file mode 100644
index 00000000000..1c3f4d8fd7d
--- /dev/null
+++ b/etc/images/tabs/close.xpm
@@ -0,0 +1,16 @@
1/* XPM */
2static char * close_xpm[] = {
3"9 9 4 1",
4" c None",
5". c #BFBFBF",
6"+ c #000000",
7"@ c #808080",
8" ..... ",
9" ....... ",
10"..+@.@+..",
11"..@+@+@..",
12"...@+@...",
13"..@+@+@..",
14"..+@.@+..",
15" ....... ",
16" ..... "};
diff --git a/etc/images/tabs/new.xpm b/etc/images/tabs/new.xpm
new file mode 100644
index 00000000000..e10a8ef238b
--- /dev/null
+++ b/etc/images/tabs/new.xpm
@@ -0,0 +1,16 @@
1/* XPM */
2static char * new_xpm[] = {
3"9 9 4 1",
4" c None",
5". c #BFBFBF",
6"+ c #808080",
7"@ c #000000",
8".........",
9"....+....",
10"....@....",
11"....@....",
12".+@@@@@+.",
13"....@....",
14"....@....",
15"....+....",
16"........."};
diff --git a/lisp/cus-start.el b/lisp/cus-start.el
index 15d33b43c01..e61c1954a1f 100644
--- a/lisp/cus-start.el
+++ b/lisp/cus-start.el
@@ -324,6 +324,9 @@ Leaving \"Default\" unchecked is equivalent with specifying a default of
324 ;; FIXME? 324 ;; FIXME?
325 ;; :initialize custom-initialize-default 325 ;; :initialize custom-initialize-default
326 :set custom-set-minor-mode) 326 :set custom-set-minor-mode)
327 (tab-bar-mode (frames mouse) boolean nil
328 ;; :initialize custom-initialize-default
329 :set custom-set-minor-mode)
327 (tool-bar-mode (frames mouse) boolean nil 330 (tool-bar-mode (frames mouse) boolean nil
328 ;; :initialize custom-initialize-default 331 ;; :initialize custom-initialize-default
329 :set custom-set-minor-mode) 332 :set custom-set-minor-mode)
@@ -726,6 +729,8 @@ since it could result in memory overflow and make Emacs crash."
726 ;; the condition for loadup.el to preload tool-bar.el. 729 ;; the condition for loadup.el to preload tool-bar.el.
727 ((string-match "tool-bar-" (symbol-name symbol)) 730 ((string-match "tool-bar-" (symbol-name symbol))
728 (fboundp 'x-create-frame)) 731 (fboundp 'x-create-frame))
732 ((string-match "tab-bar-" (symbol-name symbol))
733 (fboundp 'x-create-frame))
729 ((equal "vertical-centering-font-regexp" 734 ((equal "vertical-centering-font-regexp"
730 (symbol-name symbol)) 735 (symbol-name symbol))
731 ;; Any function from fontset.c will do. 736 ;; Any function from fontset.c will do.
diff --git a/lisp/frame.el b/lisp/frame.el
index e9d4b2ebe4c..0c68fc378b9 100644
--- a/lisp/frame.el
+++ b/lisp/frame.el
@@ -363,6 +363,47 @@ there (in decreasing order of priority)."
363 ;; If the initial frame is still around, apply initial-frame-alist 363 ;; If the initial frame is still around, apply initial-frame-alist
364 ;; and default-frame-alist to it. 364 ;; and default-frame-alist to it.
365 (when (frame-live-p frame-initial-frame) 365 (when (frame-live-p frame-initial-frame)
366 ;; When tab-bar has been switched off, correct the frame size
367 ;; by the lines added in x-create-frame for the tab-bar and
368 ;; switch `tab-bar-mode' off.
369 (when (display-graphic-p)
370 (let* ((init-lines
371 (assq 'tab-bar-lines initial-frame-alist))
372 (other-lines
373 (or (assq 'tab-bar-lines window-system-frame-alist)
374 (assq 'tab-bar-lines default-frame-alist)))
375 (lines (or init-lines other-lines))
376 (height (tab-bar-height frame-initial-frame t)))
377 ;; Adjust frame top if either zero (nil) tab bar lines have
378 ;; been requested in the most relevant of the frame's alists
379 ;; or tab bar mode has been explicitly turned off in the
380 ;; user's init file.
381 (when (and (> height 0)
382 (or (and lines
383 (or (null (cdr lines))
384 (eq 0 (cdr lines))))
385 (not tab-bar-mode)))
386 (let* ((initial-top
387 (cdr (assq 'top frame-initial-geometry-arguments)))
388 (top (frame-parameter frame-initial-frame 'top)))
389 (when (and (consp initial-top) (eq '- (car initial-top)))
390 (let ((adjusted-top
391 (cond
392 ((and (consp top) (eq '+ (car top)))
393 (list '+ (+ (cadr top) height)))
394 ((and (consp top) (eq '- (car top)))
395 (list '- (- (cadr top) height)))
396 (t (+ top height)))))
397 (modify-frame-parameters
398 frame-initial-frame `((top . ,adjusted-top))))))
399 ;; Reset `tab-bar-mode' when zero tab bar lines have been
400 ;; requested for the window-system or default frame alists.
401 (when (and tab-bar-mode
402 (and other-lines
403 (or (null (cdr other-lines))
404 (eq 0 (cdr other-lines)))))
405 (tab-bar-mode -1)))))
406
366 ;; When tool-bar has been switched off, correct the frame size 407 ;; When tool-bar has been switched off, correct the frame size
367 ;; by the lines added in x-create-frame for the tool-bar and 408 ;; by the lines added in x-create-frame for the tool-bar and
368 ;; switch `tool-bar-mode' off. 409 ;; switch `tool-bar-mode' off.
@@ -1593,6 +1634,7 @@ and width values are in pixels.
1593 '(tool-bar-external . nil) 1634 '(tool-bar-external . nil)
1594 '(tool-bar-position . nil) 1635 '(tool-bar-position . nil)
1595 '(tool-bar-size 0 . 0) 1636 '(tool-bar-size 0 . 0)
1637 '(tab-bar-size 0 . 0)
1596 (cons 'internal-border-width 1638 (cons 'internal-border-width
1597 (frame-parameter frame 'internal-border-width))))))) 1639 (frame-parameter frame 'internal-border-width)))))))
1598 1640
diff --git a/lisp/loadup.el b/lisp/loadup.el
index 67e8aa7d40a..e60922e380a 100644
--- a/lisp/loadup.el
+++ b/lisp/loadup.el
@@ -267,6 +267,7 @@
267(load "rfn-eshadow") 267(load "rfn-eshadow")
268 268
269(load "menu-bar") 269(load "menu-bar")
270(load "tab-bar")
270(load "emacs-lisp/lisp") 271(load "emacs-lisp/lisp")
271(load "textmodes/page") 272(load "textmodes/page")
272(load "register") 273(load "register")
diff --git a/lisp/menu-bar.el b/lisp/menu-bar.el
index 19122125c53..b7967b858ae 100644
--- a/lisp/menu-bar.el
+++ b/lisp/menu-bar.el
@@ -687,7 +687,7 @@ The selected font will be the default on both the existing and future frames."
687 ;; side-effect that turning them off via X 687 ;; side-effect that turning them off via X
688 ;; resources acts like having customized them, but 688 ;; resources acts like having customized them, but
689 ;; that seems harmless. 689 ;; that seems harmless.
690 menu-bar-mode tool-bar-mode)) 690 menu-bar-mode tab-bar-mode tool-bar-mode))
691 ;; FIXME ? It's a little annoying that running this command 691 ;; FIXME ? It's a little annoying that running this command
692 ;; always loads cua-base, paren, time, and battery, even if they 692 ;; always loads cua-base, paren, time, and battery, even if they
693 ;; have not been customized in any way. (Due to custom-load-symbol.) 693 ;; have not been customized in any way. (Due to custom-load-symbol.)
@@ -1242,6 +1242,14 @@ mail status in mode line"))
1242 (frame-parameter (menu-bar-frame-for-menubar) 1242 (frame-parameter (menu-bar-frame-for-menubar)
1243 'menu-bar-lines))))) 1243 'menu-bar-lines)))))
1244 1244
1245 (bindings--define-key menu [showhide-tab-bar]
1246 '(menu-item "Tab Bar" toggle-tab-bar-mode-from-frame
1247 :help "Turn tab bar on/off"
1248 :button
1249 (:toggle . (menu-bar-positive-p
1250 (frame-parameter (menu-bar-frame-for-menubar)
1251 'tab-bar-lines)))))
1252
1245 (if (and (boundp 'menu-bar-showhide-tool-bar-menu) 1253 (if (and (boundp 'menu-bar-showhide-tool-bar-menu)
1246 (keymapp menu-bar-showhide-tool-bar-menu)) 1254 (keymapp menu-bar-showhide-tool-bar-menu))
1247 (bindings--define-key menu [showhide-tool-bar] 1255 (bindings--define-key menu [showhide-tool-bar]
diff --git a/lisp/mouse.el b/lisp/mouse.el
index 123ce2ca154..76fec507e71 100644
--- a/lisp/mouse.el
+++ b/lisp/mouse.el
@@ -2734,6 +2734,7 @@ is copied instead of being cut."
2734;; versions. 2734;; versions.
2735(global-set-key [header-line down-mouse-1] 'mouse-drag-header-line) 2735(global-set-key [header-line down-mouse-1] 'mouse-drag-header-line)
2736(global-set-key [header-line mouse-1] 'mouse-select-window) 2736(global-set-key [header-line mouse-1] 'mouse-select-window)
2737(global-set-key [tab-line mouse-1] 'mouse-select-window)
2737;; (global-set-key [mode-line drag-mouse-1] 'mouse-select-window) 2738;; (global-set-key [mode-line drag-mouse-1] 'mouse-select-window)
2738(global-set-key [mode-line down-mouse-1] 'mouse-drag-mode-line) 2739(global-set-key [mode-line down-mouse-1] 'mouse-drag-mode-line)
2739(global-set-key [mode-line mouse-1] 'mouse-select-window) 2740(global-set-key [mode-line mouse-1] 'mouse-select-window)
diff --git a/lisp/startup.el b/lisp/startup.el
index 52d4dbb05c8..393d7872560 100644
--- a/lisp/startup.el
+++ b/lisp/startup.el
@@ -769,6 +769,7 @@ It is the default value of the variable `top-level'."
769 ("--background-color" . "-bg") 769 ("--background-color" . "-bg")
770 ("--color" . "-color"))) 770 ("--color" . "-color")))
771 771
772;; FIXME: this var unused?
772(defconst tool-bar-images-pixel-height 24 773(defconst tool-bar-images-pixel-height 24
773 "Height in pixels of images in the tool-bar.") 774 "Height in pixels of images in the tool-bar.")
774 775
@@ -1300,6 +1301,7 @@ please check its value")
1300 (unless (daemonp) 1301 (unless (daemonp)
1301 (if (or noninteractive emacs-basic-display) 1302 (if (or noninteractive emacs-basic-display)
1302 (setq menu-bar-mode nil 1303 (setq menu-bar-mode nil
1304 tab-bar-mode nil
1303 tool-bar-mode nil 1305 tool-bar-mode nil
1304 no-blinking-cursor t)) 1306 no-blinking-cursor t))
1305 (frame-initialize)) 1307 (frame-initialize))
@@ -1515,6 +1517,7 @@ This can set the values of `menu-bar-mode', `tool-bar-mode', and
1515settings will be marked as \"CHANGED outside of Customize\"." 1517settings will be marked as \"CHANGED outside of Customize\"."
1516 (let ((no-vals '("no" "off" "false" "0")) 1518 (let ((no-vals '("no" "off" "false" "0"))
1517 (settings '(("menuBar" "MenuBar" menu-bar-mode nil) 1519 (settings '(("menuBar" "MenuBar" menu-bar-mode nil)
1520 ("tabBar" "TabBar" tab-bar-mode nil)
1518 ("toolBar" "ToolBar" tool-bar-mode nil) 1521 ("toolBar" "ToolBar" tool-bar-mode nil)
1519 ("scrollBar" "ScrollBar" scroll-bar-mode nil) 1522 ("scrollBar" "ScrollBar" scroll-bar-mode nil)
1520 ("cursorBlink" "CursorBlink" no-blinking-cursor t)))) 1523 ("cursorBlink" "CursorBlink" no-blinking-cursor t))))
diff --git a/lisp/subr.el b/lisp/subr.el
index 45b99a82d2b..da619fef147 100644
--- a/lisp/subr.el
+++ b/lisp/subr.el
@@ -2395,8 +2395,12 @@ some sort of escape sequence, the ambiguity is resolved via `read-key-delay'."
2395 (progn 2395 (progn
2396 (use-global-map 2396 (use-global-map
2397 (let ((map (make-sparse-keymap))) 2397 (let ((map (make-sparse-keymap)))
2398 ;; Don't hide the menu-bar and tool-bar entries. 2398 ;; Don't hide the menu-bar, tab-bar and tool-bar entries.
2399 (define-key map [menu-bar] (lookup-key global-map [menu-bar])) 2399 (define-key map [menu-bar] (lookup-key global-map [menu-bar]))
2400 (define-key map [tab-bar]
2401 ;; This hack avoids evaluating the :filter (Bug#9922).
2402 (or (cdr (assq 'tab-bar global-map))
2403 (lookup-key global-map [tab-bar])))
2400 (define-key map [tool-bar] 2404 (define-key map [tool-bar]
2401 ;; This hack avoids evaluating the :filter (Bug#9922). 2405 ;; This hack avoids evaluating the :filter (Bug#9922).
2402 (or (cdr (assq 'tool-bar global-map)) 2406 (or (cdr (assq 'tool-bar global-map))
diff --git a/lisp/tab-bar.el b/lisp/tab-bar.el
new file mode 100644
index 00000000000..42d40a96543
--- /dev/null
+++ b/lisp/tab-bar.el
@@ -0,0 +1,764 @@
1;;; tab-bar.el --- frame-local tabs with named persistent window configurations -*- lexical-binding: t; -*-
2
3;; Copyright (C) 2019 Free Software Foundation, Inc.
4
5;; Author: Juri Linkov <juri@linkov.net>
6;; Keywords: frames tabs
7;; Maintainer: emacs-devel@gnu.org
8
9;; This file is part of GNU Emacs.
10
11;; GNU Emacs is free software: you can redistribute it and/or modify
12;; it under the terms of the GNU General Public License as published by
13;; the Free Software Foundation, either version 3 of the License, or
14;; (at your option) any later version.
15
16;; GNU Emacs is distributed in the hope that it will be useful,
17;; but WITHOUT ANY WARRANTY; without even the implied warranty of
18;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19;; GNU General Public License for more details.
20
21;; You should have received a copy of the GNU General Public License
22;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
23
24;;; Commentary:
25
26;; Provides `tab-bar-mode' to control display of the tab bar and
27;; bindings for the global tab bar.
28
29;; The normal global binding for [tab-bar] (below) uses the value of
30;; `tab-bar-map' as the actual keymap to define the tab bar. Modes
31;; may either bind items under the [tab-bar] prefix key of the local
32;; map to add to the global bar or may set `tab-bar-map'
33;; buffer-locally to override it.
34
35;;; Code:
36
37
38(defgroup tab-bar nil
39 "Frame-local tabs."
40 :group 'convenience
41 :version "27.1")
42
43(defgroup tab-bar-faces nil
44 "Faces used in the tab bar."
45 :group 'tab-bar
46 :group 'faces
47 :version "27.1")
48
49(defface tab-bar
50 '((((type x w32 ns) (class color))
51 :height 1.1
52 :background "grey85"
53 :foreground "black")
54 (((type x) (class mono))
55 :background "grey")
56 (t
57 :inverse-video t))
58 "Tab bar face."
59 :version "27.1"
60 :group 'tab-bar-faces)
61
62(defface tab-bar-tab
63 '((((class color) (min-colors 88))
64 :box (:line-width 1 :style released-button))
65 (t
66 :inverse-video nil))
67 "Tab bar face for selected tab."
68 :version "27.1"
69 :group 'tab-bar-faces)
70
71(defface tab-bar-tab-inactive
72 '((default
73 :inherit tab-bar-tab)
74 (((class color) (min-colors 88))
75 :background "grey75")
76 (t
77 :inverse-video t))
78 "Tab bar face for non-selected tab."
79 :version "27.1"
80 :group 'tab-bar-faces)
81
82
83(define-minor-mode tab-bar-mode
84 "Toggle the tab bar in all graphical frames (Tab Bar mode)."
85 :global t
86 ;; It's defined in C/cus-start, this stops the d-m-m macro defining it again.
87 :variable tab-bar-mode
88 (let ((val (if tab-bar-mode 1 0)))
89 (dolist (frame (frame-list))
90 (set-frame-parameter frame 'tab-bar-lines val))
91 ;; If the user has given `default-frame-alist' a `tab-bar-lines'
92 ;; parameter, replace it.
93 (if (assq 'tab-bar-lines default-frame-alist)
94 (setq default-frame-alist
95 (cons (cons 'tab-bar-lines val)
96 (assq-delete-all 'tab-bar-lines
97 default-frame-alist)))))
98 (when tab-bar-mode
99 (global-set-key [(control shift iso-lefttab)] 'tab-bar-switch-to-prev-tab)
100 (global-set-key [(control shift tab)] 'tab-bar-switch-to-prev-tab)
101 (global-set-key [(control tab)] 'tab-bar-switch-to-next-tab)))
102
103(defun tab-bar-handle-mouse (event)
104 "Text-mode emulation of switching tabs on the tab bar.
105This command is used when you click the mouse in the tab bar
106on a console which has no window system but does have a mouse."
107 (interactive "e")
108 (let* ((x-position (car (posn-x-y (event-start event))))
109 (keymap (lookup-key (cons 'keymap (nreverse (current-active-maps))) [tab-bar]))
110 (column 0))
111 (when x-position
112 (unless (catch 'done
113 (map-keymap
114 (lambda (_key binding)
115 (when (eq (car-safe binding) 'menu-item)
116 (when (> (+ column (length (nth 1 binding))) x-position)
117 ;; TODO: handle close
118 (unless (get-text-property (- x-position column) 'close-tab (nth 1 binding))
119 (call-interactively (nth 2 binding)))
120 (throw 'done t))
121 (setq column (+ column (length (nth 1 binding))))))
122 keymap))
123 ;; Clicking anywhere outside existing tabs will add a new tab
124 (tab-bar-new-tab)))))
125
126;; Used in the Show/Hide menu, to have the toggle reflect the current frame.
127(defun toggle-tab-bar-mode-from-frame (&optional arg)
128 "Toggle tab bar on or off, based on the status of the current frame.
129See `tab-bar-mode' for more information."
130 (interactive (list (or current-prefix-arg 'toggle)))
131 (if (eq arg 'toggle)
132 (tab-bar-mode (if (> (frame-parameter nil 'tab-bar-lines) 0) 0 1))
133 (tab-bar-mode arg)))
134
135(defvar tab-bar-map (make-sparse-keymap)
136 "Keymap for the tab bar.
137Define this locally to override the global tab bar.")
138
139(global-set-key [tab-bar]
140 `(menu-item ,(purecopy "tab bar") ignore
141 :filter tab-bar-make-keymap))
142
143(defconst tab-bar-keymap-cache (make-hash-table :weakness t :test 'equal))
144
145(defun tab-bar-make-keymap (&optional _ignore)
146 "Generate an actual keymap from `tab-bar-map'.
147Its main job is to show tabs in the tab bar."
148 (if (= 1 (length tab-bar-map))
149 (tab-bar-make-keymap-1)
150 (let ((key (cons (frame-terminal) tab-bar-map)))
151 (or (gethash key tab-bar-keymap-cache)
152 (puthash key tab-bar-map tab-bar-keymap-cache)))))
153
154
155(defcustom tab-bar-new-tab-choice t
156 "Defines what to show in a new tab.
157If t, start a new tab with the current buffer, i.e. the buffer
158that was current before calling the command that adds a new tab
159(this is the same what `make-frame' does by default).
160If the value is a string, switch to a buffer if it exists, or switch
161to a buffer visiting the file or directory that the string specifies.
162If the value is a function, call it with no arguments and switch to
163the buffer that it returns.
164If nil, duplicate the contents of the tab that was active
165before calling the command that adds a new tab."
166 :type '(choice (const :tag "Current buffer" t)
167 (directory :tag "Directory" :value "~/")
168 (file :tag "File" :value "~/.emacs")
169 (string :tag "Buffer" "*scratch*")
170 (function :tag "Function")
171 (const :tag "Duplicate tab" nil))
172 :group 'tab-bar
173 :version "27.1")
174
175(defvar tab-bar-new-button
176 (propertize " + "
177 'display `(image :type xpm
178 :file ,(expand-file-name
179 "images/tabs/new.xpm"
180 data-directory)
181 :margin (2 . 0)
182 :ascent center))
183 "Button for creating a new tab.")
184
185(defcustom tab-bar-close-button-show t
186 "Defines where to show the close tab button.
187If t, show the close tab button on all tabs.
188If `selected', show it only on the selected tab.
189If `non-selected', show it only on non-selected tab.
190If nil, don't show it at all."
191 :type '(choice (const :tag "On all tabs" t)
192 (const :tag "On selected tab" selected)
193 (const :tag "On non-selected tabs" non-selected)
194 (const :tag "None" nil))
195 :set (lambda (sym val)
196 (set sym val)
197 (force-mode-line-update))
198 :group 'tab-bar
199 :version "27.1")
200
201(defvar tab-bar-close-button
202 (propertize " x"
203 'display `(image :type xpm
204 :file ,(expand-file-name
205 "images/tabs/close.xpm"
206 data-directory)
207 :margin (2 . 0)
208 :ascent center)
209 'close-tab t
210 :help "Click to close tab")
211 "Button for closing the clicked tab.")
212
213(defvar tab-bar-separator nil)
214
215
216(defvar tab-bar-tab-name-function #'tab-bar-tab-name
217 "Function to get a tab name.
218Function gets no arguments.
219By default, use function `tab-bar-tab-name'.")
220
221(defun tab-bar-tab-name ()
222 "Generate tab name in the context of the selected frame."
223 (mapconcat #'buffer-name
224 (delete-dups (mapcar #'window-buffer
225 (window-list-1 (frame-first-window)
226 'nomini)))
227 ", "))
228
229(defvar tab-bar-tabs-function #'tab-bar-tabs
230 "Function to get a list of tabs to display in the tab bar.
231This function should return a list of alists with parameters
232that include at least the element (name . TAB-NAME).
233For example, '((tab (name . \"Tab 1\")) (current-tab (name . \"Tab 2\")))
234By default, use function `tab-bar-tabs'.")
235
236(defun tab-bar-tabs ()
237 "Return a list of tabs belonging to the selected frame.
238Ensure the frame parameter `tabs' is pre-populated.
239Return its existing value or a new value."
240 (let ((tabs (frame-parameter nil 'tabs)))
241 (if tabs
242 ;; Update current tab name
243 (let ((name (assq 'name (assq 'current-tab tabs))))
244 (when name (setcdr name (funcall tab-bar-tab-name-function))))
245 ;; Create default tabs
246 (setq tabs `((current-tab (name . ,(funcall tab-bar-tab-name-function)))))
247 (set-frame-parameter nil 'tabs tabs))
248 tabs))
249
250(defun tab-bar-make-keymap-1 ()
251 "Generate an actual keymap from `tab-bar-map', without caching."
252 (let ((separator (or tab-bar-separator (if window-system " " "|")))
253 (i 0))
254 (append
255 '(keymap (mouse-1 . tab-bar-handle-mouse))
256 (mapcan
257 (lambda (tab)
258 (setq i (1+ i))
259 (append
260 `((,(intern (format "sep-%i" i)) menu-item ,separator ignore))
261 (cond
262 ((eq (car tab) 'current-tab)
263 `((current-tab
264 menu-item
265 ,(propertize (concat (cdr (assq 'name tab))
266 (or (and tab-bar-close-button-show
267 (not (eq tab-bar-close-button-show
268 'non-selected))
269 tab-bar-close-button) ""))
270 'face 'tab-bar-tab)
271 ignore
272 :help "Current tab")))
273 (t
274 `((,(intern (format "tab-%i" i))
275 menu-item
276 ,(propertize (concat (cdr (assq 'name tab))
277 (or (and tab-bar-close-button-show
278 (not (eq tab-bar-close-button-show
279 'selected))
280 tab-bar-close-button) ""))
281 'face 'tab-bar-tab-inactive)
282 ,(or
283 (cdr (assq 'binding tab))
284 (lambda ()
285 (interactive)
286 (tab-bar-select-tab tab)))
287 :help "Click to visit tab"))))
288 `((,(if (eq (car tab) 'current-tab) 'C-current-tab (intern (format "C-tab-%i" i)))
289 menu-item ""
290 ,(or
291 (cdr (assq 'close-binding tab))
292 (lambda ()
293 (interactive)
294 (tab-bar-close-tab tab)))))))
295 (funcall tab-bar-tabs-function))
296 (when tab-bar-new-button
297 `((sep-add-tab menu-item ,separator ignore)
298 (add-tab menu-item ,tab-bar-new-button tab-bar-new-tab
299 :help "New tab"))))))
300
301
302(defun tab-bar-read-tab-name (prompt)
303 (let* ((tabs (tab-bar-tabs))
304 (tab-name
305 (completing-read prompt
306 (or (delq nil (mapcar (lambda (tab)
307 (cdr (assq 'name tab)))
308 tabs))
309 '("")))))
310 (catch 'done
311 (dolist (tab tabs)
312 (when (equal (cdr (assq 'name tab)) tab-name)
313 (throw 'done tab))))))
314
315(defun tab-bar-tab-default ()
316 (let ((tab `(tab
317 (name . ,(funcall tab-bar-tab-name-function))
318 (time . ,(time-convert nil 'integer))
319 (wc . ,(current-window-configuration))
320 (ws . ,(window-state-get
321 (frame-root-window (selected-frame)) 'writable)))))
322 tab))
323
324(defun tab-bar-find-prev-tab (&optional tabs)
325 (unless tabs
326 (setq tabs (tab-bar-tabs)))
327 (unless (eq (car (car tabs)) 'current-tab)
328 (while (and tabs (not (eq (car (car (cdr tabs))) 'current-tab)))
329 (setq tabs (cdr tabs)))
330 tabs))
331
332
333(defun tab-bar-select-tab (tab)
334 "Switch to the specified TAB."
335 (interactive (list (tab-bar-read-tab-name "Select tab by name: ")))
336 (when (and tab (not (eq (car tab) 'current-tab)))
337 (let* ((tabs (tab-bar-tabs))
338 (new-tab (tab-bar-tab-default))
339 (wc (cdr (assq 'wc tab))))
340 ;; During the same session, use window-configuration to switch
341 ;; tabs, because window-configurations are more reliable
342 ;; (they keep references to live buffers) than window-states.
343 ;; But after restoring tabs from a previously saved session,
344 ;; its value of window-configuration is unreadable,
345 ;; so restore its saved window-state.
346 (if (window-configuration-p wc)
347 (set-window-configuration wc)
348 (window-state-put (cdr (assq 'ws tab))
349 (frame-root-window (selected-frame)) 'safe))
350 (while tabs
351 (cond
352 ((eq (car tabs) tab)
353 (setcar tabs `(current-tab (name . ,(funcall tab-bar-tab-name-function)))))
354 ((eq (car (car tabs)) 'current-tab)
355 (setcar tabs new-tab)))
356 (setq tabs (cdr tabs)))
357 (force-mode-line-update))))
358
359(defun tab-bar-switch-to-prev-tab (&optional _arg)
360 "Switch to ARGth previous tab."
361 (interactive "p")
362 (let ((prev-tab (tab-bar-find-prev-tab)))
363 (when prev-tab
364 (tab-bar-select-tab (car prev-tab)))))
365
366(defun tab-bar-switch-to-next-tab (&optional _arg)
367 "Switch to ARGth next tab."
368 (interactive "p")
369 (let* ((tabs (tab-bar-tabs))
370 (prev-tab (tab-bar-find-prev-tab tabs)))
371 (if prev-tab
372 (tab-bar-select-tab (car (cdr (cdr prev-tab))))
373 (tab-bar-select-tab (car (cdr tabs))))))
374
375
376(defcustom tab-bar-new-tab-to 'right
377 "Defines where to create a new tab.
378If `leftmost', create as the first tab.
379If `left', create to the left from the current tab.
380If `right', create to the right from the current tab.
381If `rightmost', create as the last tab."
382 :type '(choice (const :tag "First tab" leftmost)
383 (const :tag "To the left" left)
384 (const :tag "To the right" right)
385 (const :tag "Last tab" rightmost))
386 :group 'tab-bar
387 :version "27.1")
388
389(defun tab-bar-new-tab ()
390 "Clone the current tab to the position specified by `tab-bar-new-tab-to'."
391 (interactive)
392 (unless tab-bar-mode
393 (tab-bar-mode 1))
394 (let* ((tabs (tab-bar-tabs))
395 ;; (i-tab (- (length tabs) (length (memq tab tabs))))
396 (new-tab (tab-bar-tab-default)))
397 (cond
398 ((eq tab-bar-new-tab-to 'leftmost)
399 (setq tabs (cons new-tab tabs)))
400 ((eq tab-bar-new-tab-to 'rightmost)
401 (setq tabs (append tabs (list new-tab))))
402 (t
403 (let ((prev-tab (tab-bar-find-prev-tab tabs)))
404 (cond
405 ((eq tab-bar-new-tab-to 'left)
406 (if prev-tab
407 (setcdr prev-tab (cons new-tab (cdr prev-tab)))
408 (setq tabs (cons new-tab tabs))))
409 ((eq tab-bar-new-tab-to 'right)
410 (if prev-tab
411 (setq prev-tab (cdr prev-tab))
412 (setq prev-tab tabs))
413 (setcdr prev-tab (cons new-tab (cdr prev-tab))))))))
414 (set-frame-parameter nil 'tabs tabs)
415 (tab-bar-select-tab new-tab)
416 (when tab-bar-new-tab-choice
417 (delete-other-windows)
418 (let ((buffer
419 (if (functionp tab-bar-new-tab-choice)
420 (funcall tab-bar-new-tab-choice)
421 (if (stringp tab-bar-new-tab-choice)
422 (or (get-buffer tab-bar-new-tab-choice)
423 (find-file-noselect tab-bar-new-tab-choice))))))
424 (when (buffer-live-p buffer)
425 (switch-to-buffer buffer))))
426 (unless tab-bar-mode
427 (message "Added new tab with the current window configuration"))))
428
429
430(defcustom tab-bar-close-tab-select 'right
431 "Defines what tab to select after closing the specified tab.
432If `left', select the adjacent left tab.
433If `right', select the adjacent right tab."
434 :type '(choice (const :tag "Select left tab" left)
435 (const :tag "Select right tab" right))
436 :group 'tab-bar
437 :version "27.1")
438
439(defun tab-bar-close-current-tab (&optional tab select-tab)
440 "Close the current TAB.
441After closing the current tab switch to the tab
442specified by `tab-bar-close-tab-select', or to `select-tab'
443if its value is provided."
444 (interactive)
445 (let ((tabs (tab-bar-tabs)))
446 (unless tab
447 (let ((prev-tab (tab-bar-find-prev-tab tabs)))
448 (setq tab (if prev-tab
449 (car (cdr prev-tab))
450 (car tabs)))))
451 (if select-tab
452 (setq tabs (delq tab tabs))
453 (let* ((i-tab (- (length tabs) (length (memq tab tabs))))
454 (i-select
455 (cond
456 ((eq tab-bar-close-tab-select 'left)
457 (1- i-tab))
458 ((eq tab-bar-close-tab-select 'right)
459 ;; Do nothing: the next tab will take
460 ;; the index of the closed tab
461 i-tab)
462 (t 0))))
463 (setq tabs (delq tab tabs)
464 i-select (max 0 (min (1- (length tabs)) i-select))
465 select-tab (nth i-select tabs))))
466 (set-frame-parameter nil 'tabs tabs)
467 (tab-bar-select-tab select-tab)))
468
469(defun tab-bar-close-tab (tab)
470 "Close the specified TAB.
471After closing the current tab switch to the tab
472specified by `tab-bar-close-tab-select'."
473 (interactive (list (tab-bar-read-tab-name "Close tab by name: ")))
474 (when tab
475 (if (eq (car tab) 'current-tab)
476 (tab-bar-close-current-tab tab)
477 ;; Close non-current tab, no need to switch to another tab
478 (set-frame-parameter nil 'tabs (delq tab (tab-bar-tabs)))
479 (force-mode-line-update))))
480
481
482;;; Non-graphical access to frame-local tabs (named window configurations)
483
484(defun tab-new ()
485 "Create a new named window configuration without having to click a tab."
486 (interactive)
487 (tab-bar-new-tab)
488 (unless tab-bar-mode
489 (message "Added new tab with the current window configuration")))
490
491(defun tab-close ()
492 "Delete the current window configuration without clicking a close button."
493 (interactive)
494 (tab-bar-close-current-tab)
495 (unless tab-bar-mode
496 (message "Deleted the current tab")))
497
498;; Short aliases
499;; (defalias 'tab-switch 'tab-bar-switch-to-next-tab)
500(defalias 'tab-select 'tab-bar-select-tab)
501(defalias 'tab-previous 'tab-bar-switch-to-prev-tab)
502(defalias 'tab-next 'tab-bar-switch-to-next-tab)
503(defalias 'tab-list 'tab-bar-list)
504
505(defun tab-bar-list ()
506 "Display a list of named window configurations.
507The list is displayed in the buffer `*Tabs*'.
508
509In this list of window configurations you can delete or select them.
510Type ? after invocation to get help on commands available.
511Type q to remove the list of window configurations from the display.
512
513The first column shows `D' for for a window configuration you have
514marked for deletion."
515 (interactive)
516 (let ((dir default-directory)
517 (minibuf (minibuffer-selected-window)))
518 (let ((tab-bar-mode t)) ; don't enable tab-bar-mode if it's disabled
519 (tab-bar-new-tab))
520 ;; Handle the case when it's called in the active minibuffer.
521 (when minibuf (select-window (minibuffer-selected-window)))
522 (delete-other-windows)
523 ;; Create a new window to replace the existing one, to not break the
524 ;; window parameters (e.g. prev/next buffers) of the window just saved
525 ;; to the window configuration. So when a saved window is restored,
526 ;; its parameters left intact.
527 (split-window) (delete-window)
528 (let ((switch-to-buffer-preserve-window-point nil))
529 (switch-to-buffer (tab-bar-list-noselect)))
530 (setq default-directory dir))
531 (message "Commands: d, x; RET; q to quit; ? for help."))
532
533(defun tab-bar-list-noselect ()
534 "Create and return a buffer with a list of window configurations.
535The list is displayed in a buffer named `*Tabs*'.
536
537For more information, see the function `tab-bar-list'."
538 (let* ((tabs (delq nil (mapcar (lambda (tab) ; remove current tab
539 (unless (eq (car tab) 'current-tab)
540 tab))
541 (tab-bar-tabs))))
542 ;; Sort by recency
543 (tabs (sort tabs (lambda (a b) (< (cdr (assq 'time b))
544 (cdr (assq 'time a)))))))
545 (with-current-buffer (get-buffer-create
546 (format " *Tabs*<%s>" (or (frame-parameter nil 'window-id)
547 (frame-parameter nil 'name))))
548 (erase-buffer)
549 (tab-bar-list-mode)
550 (setq buffer-read-only nil)
551 ;; Vertical alignment to the center of the frame
552 (insert-char ?\n (/ (- (frame-height) (length tabs) 1) 2))
553 ;; Horizontal alignment to the center of the frame
554 (setq tab-bar-list-column (- (/ (frame-width) 2) 15))
555 (dolist (tab tabs)
556 (insert (propertize
557 (format "%s %s\n"
558 (make-string tab-bar-list-column ?\040)
559 (propertize
560 (cdr (assq 'name tab))
561 'mouse-face 'highlight
562 'help-echo "mouse-2: select this window configuration"))
563 'tab tab)))
564 (goto-char (point-min))
565 (goto-char (or (next-single-property-change (point) 'tab) (point-min)))
566 (when (> (length tabs) 1)
567 (tab-bar-list-next-line))
568 (move-to-column tab-bar-list-column)
569 (set-buffer-modified-p nil)
570 (current-buffer))))
571
572(defvar tab-bar-list-column 3)
573(make-variable-buffer-local 'tab-bar-list-column)
574
575(defvar tab-bar-list-mode-map
576 (let ((map (make-keymap)))
577 (suppress-keymap map t)
578 (define-key map "q" 'quit-window)
579 (define-key map "\C-m" 'tab-bar-list-select)
580 (define-key map "d" 'tab-bar-list-delete)
581 (define-key map "k" 'tab-bar-list-delete)
582 (define-key map "\C-d" 'tab-bar-list-delete-backwards)
583 (define-key map "\C-k" 'tab-bar-list-delete)
584 (define-key map "x" 'tab-bar-list-execute)
585 (define-key map " " 'tab-bar-list-next-line)
586 (define-key map "n" 'tab-bar-list-next-line)
587 (define-key map "p" 'tab-bar-list-prev-line)
588 (define-key map "\177" 'tab-bar-list-backup-unmark)
589 (define-key map "?" 'describe-mode)
590 (define-key map "u" 'tab-bar-list-unmark)
591 (define-key map [mouse-2] 'tab-bar-list-mouse-select)
592 (define-key map [follow-link] 'mouse-face)
593 map)
594 "Local keymap for `tab-bar-list-mode' buffers.")
595
596(define-derived-mode tab-bar-list-mode nil "Window Configurations"
597 "Major mode for selecting a window configuration.
598Each line describes one window configuration in Emacs.
599Letters do not insert themselves; instead, they are commands.
600\\<tab-bar-list-mode-map>
601\\[tab-bar-list-mouse-select] -- select window configuration you click on.
602\\[tab-bar-list-select] -- select current line's window configuration.
603\\[tab-bar-list-delete] -- mark that window configuration to be deleted, and move down.
604\\[tab-bar-list-delete-backwards] -- mark that window configuration to be deleted, and move up.
605\\[tab-bar-list-execute] -- delete marked window configurations.
606\\[tab-bar-list-unmark] -- remove all kinds of marks from current line.
607 With prefix argument, also move up one line.
608\\[tab-bar-list-backup-unmark] -- back up a line and remove marks."
609 (setq truncate-lines t)
610 (setq buffer-read-only t))
611
612(defun tab-bar-list-current-tab (error-if-non-existent-p)
613 "Return window configuration described by this line of the list."
614 (let* ((where (save-excursion
615 (beginning-of-line)
616 (+ 2 (point) tab-bar-list-column)))
617 (tab (and (not (eobp)) (get-text-property where 'tab))))
618 (or tab
619 (if error-if-non-existent-p
620 (user-error "No window configuration on this line")
621 nil))))
622
623
624(defun tab-bar-list-next-line (&optional arg)
625 (interactive)
626 (forward-line arg)
627 (beginning-of-line)
628 (move-to-column tab-bar-list-column))
629
630(defun tab-bar-list-prev-line (&optional arg)
631 (interactive)
632 (forward-line (- arg))
633 (beginning-of-line)
634 (move-to-column tab-bar-list-column))
635
636(defun tab-bar-list-unmark (&optional backup)
637 "Cancel all requested operations on window configuration on this line and move down.
638Optional prefix arg means move up."
639 (interactive "P")
640 (beginning-of-line)
641 (move-to-column tab-bar-list-column)
642 (let* ((buffer-read-only nil))
643 (delete-char 1)
644 (insert " "))
645 (forward-line (if backup -1 1))
646 (move-to-column tab-bar-list-column))
647
648(defun tab-bar-list-backup-unmark ()
649 "Move up and cancel all requested operations on window configuration on line above."
650 (interactive)
651 (forward-line -1)
652 (tab-bar-list-unmark)
653 (forward-line -1)
654 (move-to-column tab-bar-list-column))
655
656(defun tab-bar-list-delete (&optional arg)
657 "Mark window configuration on this line to be deleted by \\<tab-bar-list-mode-map>\\[tab-bar-list-execute] command.
658Prefix arg is how many window configurations to delete.
659Negative arg means delete backwards."
660 (interactive "p")
661 (let ((buffer-read-only nil))
662 (if (or (null arg) (= arg 0))
663 (setq arg 1))
664 (while (> arg 0)
665 (delete-char 1)
666 (insert ?D)
667 (forward-line 1)
668 (setq arg (1- arg)))
669 (while (< arg 0)
670 (delete-char 1)
671 (insert ?D)
672 (forward-line -1)
673 (setq arg (1+ arg)))
674 (move-to-column tab-bar-list-column)))
675
676(defun tab-bar-list-delete-backwards (&optional arg)
677 "Mark window configuration on this line to be deleted by \\<tab-bar-list-mode-map>\\[tab-bar-list-execute] command.
678Then move up one line. Prefix arg means move that many lines."
679 (interactive "p")
680 (tab-bar-list-delete (- (or arg 1))))
681
682(defun tab-bar-list-delete-from-list (tab)
683 "Delete the window configuration from both lists."
684 (set-frame-parameter nil 'tabs (delq tab (tab-bar-tabs))))
685
686(defun tab-bar-list-execute ()
687 "Delete window configurations marked with \\<tab-bar-list-mode-map>\\[tab-bar-list-delete] commands."
688 (interactive)
689 (save-excursion
690 (goto-char (point-min))
691 (let ((buffer-read-only nil))
692 (while (re-search-forward
693 (format "^%sD" (make-string tab-bar-list-column ?\040))
694 nil t)
695 (forward-char -1)
696 (let ((tab (tab-bar-list-current-tab nil)))
697 (when tab
698 (tab-bar-list-delete-from-list tab)
699 (beginning-of-line)
700 (delete-region (point) (progn (forward-line 1) (point))))))))
701 (beginning-of-line)
702 (move-to-column tab-bar-list-column)
703 (when tab-bar-mode
704 (force-mode-line-update)))
705
706(defun tab-bar-list-select ()
707 "Select this line's window configuration.
708This command deletes and replaces all the previously existing windows
709in the selected frame."
710 (interactive)
711 (let* ((select-tab (tab-bar-list-current-tab t)))
712 (kill-buffer (current-buffer))
713 ;; Delete the current window configuration
714 (tab-bar-close-current-tab nil select-tab)
715 ;; (tab-bar-select-tab select-tab)
716 ))
717
718(defun tab-bar-list-mouse-select (event)
719 "Select the window configuration whose line you click on."
720 (interactive "e")
721 (set-buffer (window-buffer (posn-window (event-end event))))
722 (goto-char (posn-point (event-end event)))
723 (tab-bar-list-select))
724
725
726(defvar ctl-x-6-map (make-sparse-keymap)
727 "Keymap for tab commands.")
728(defalias 'ctl-x-6-prefix ctl-x-6-map)
729(define-key ctl-x-map "6" 'ctl-x-6-prefix)
730
731(defun switch-to-buffer-other-tab (buffer-or-name &optional norecord)
732 "Switch to buffer BUFFER-OR-NAME in another tab.
733Like \\[switch-to-buffer-other-frame] (which see), but creates a new tab."
734 (interactive
735 (list (read-buffer-to-switch "Switch to buffer in other tab: ")))
736 (tab-bar-new-tab)
737 (delete-other-windows)
738 (switch-to-buffer buffer-or-name norecord))
739
740(defun find-file-other-tab (filename &optional wildcards)
741 "Edit file FILENAME, in another tab.
742Like \\[find-file-other-frame] (which see), but creates a new tab."
743 (interactive
744 (find-file-read-args "Find file in other tab: "
745 (confirm-nonexistent-file-or-buffer)))
746 (let ((value (find-file-noselect filename nil nil wildcards)))
747 (if (listp value)
748 (progn
749 (setq value (nreverse value))
750 (switch-to-buffer-other-tab (car value))
751 (mapc 'switch-to-buffer (cdr value))
752 value)
753 (switch-to-buffer-other-tab value))))
754
755(define-key ctl-x-6-map "2" 'tab-bar-new-tab)
756(define-key ctl-x-6-map "0" 'tab-bar-close-current-tab)
757(define-key ctl-x-6-map "b" 'switch-to-buffer-other-tab)
758(define-key ctl-x-6-map "f" 'find-file-other-tab)
759(define-key ctl-x-6-map "\C-f" 'find-file-other-tab)
760
761
762(provide 'tab-bar)
763
764;;; tab-bar.el ends here
diff --git a/lisp/tab-line.el b/lisp/tab-line.el
new file mode 100644
index 00000000000..62e06a797d5
--- /dev/null
+++ b/lisp/tab-line.el
@@ -0,0 +1,362 @@
1;;; tab-line.el --- window-local tabs with window buffers -*- lexical-binding: t; -*-
2
3;; Copyright (C) 2019 Free Software Foundation, Inc.
4
5;; Author: Juri Linkov <juri@linkov.net>
6;; Keywords: windows tabs
7;; Maintainer: emacs-devel@gnu.org
8
9;; This file is part of GNU Emacs.
10
11;; GNU Emacs is free software: you can redistribute it and/or modify
12;; it under the terms of the GNU General Public License as published by
13;; the Free Software Foundation, either version 3 of the License, or
14;; (at your option) any later version.
15
16;; GNU Emacs is distributed in the hope that it will be useful,
17;; but WITHOUT ANY WARRANTY; without even the implied warranty of
18;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19;; GNU General Public License for more details.
20
21;; You should have received a copy of the GNU General Public License
22;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
23
24;;; Commentary:
25
26;; To enable this mode, run `M-x global-tab-line-mode'.
27
28;;; Code:
29
30(require 'seq) ; tab-line.el is not pre-loaded so it's safe to use it here
31
32
33(defgroup tab-line nil
34 "Window-local tabs."
35 :group 'convenience
36 :version "27.1")
37
38(defgroup tab-line-faces nil
39 "Faces used in the tab line."
40 :group 'tab-line
41 :group 'faces
42 :version "27.1")
43
44(defface tab-line
45 '((((type x w32 ns) (class color))
46 :background "grey85"
47 :foreground "black")
48 (((type x) (class mono))
49 :background "grey")
50 (t
51 :inverse-video t))
52 "Tab line face."
53 :version "27.1"
54 :group 'tab-line-faces)
55
56(defface tab-line-tab
57 '((((class color) (min-colors 88))
58 :box (:line-width 1 :style released-button)
59 :background "grey85")
60 (t
61 :inverse-video nil))
62 "Tab line face for selected tab."
63 :version "27.1"
64 :group 'tab-line-faces)
65
66(defface tab-line-tab-inactive
67 '((default
68 :inherit tab-line-tab)
69 (((class color) (min-colors 88))
70 :background "grey75")
71 (t
72 :inverse-video t))
73 "Tab line face for non-selected tab."
74 :version "27.1"
75 :group 'tab-line-faces)
76
77(defface tab-line-highlight
78 '((default :inherit tab-line-tab))
79 "Tab line face for highlighting."
80 :version "27.1"
81 :group 'tab-line-faces)
82
83(defface tab-line-close-highlight
84 '((t :foreground "red"))
85 "Tab line face for highlighting of the close button."
86 :version "27.1"
87 :group 'tab-line-faces)
88
89
90(defvar tab-line-tab-map
91 (let ((map (make-sparse-keymap)))
92 (define-key map [tab-line mouse-1] 'tab-line-select-tab)
93 (define-key map [tab-line mouse-2] 'tab-line-close-tab)
94 (define-key map [tab-line mouse-4] 'tab-line-switch-to-prev-tab)
95 (define-key map [tab-line mouse-5] 'tab-line-switch-to-next-tab)
96 (define-key map "\C-m" 'tab-line-select-tab)
97 map)
98 "Local keymap for `tab-line-mode' window tabs.")
99
100(defvar tab-line-add-map
101 (let ((map (make-sparse-keymap)))
102 (define-key map [tab-line mouse-1] 'tab-line-new-tab)
103 (define-key map [tab-line mouse-2] 'tab-line-new-tab)
104 (define-key map "\C-m" 'tab-line-new-tab)
105 map)
106 "Local keymap to add `tab-line-mode' window tabs.")
107
108(defvar tab-line-tab-close-map
109 (let ((map (make-sparse-keymap)))
110 (define-key map [tab-line mouse-1] 'tab-line-close-tab)
111 (define-key map [tab-line mouse-2] 'tab-line-close-tab)
112 map)
113 "Local keymap to close `tab-line-mode' window tabs.")
114
115
116(defcustom tab-line-new-tab-choice t
117 "Defines what to show in a new tab.
118If t, display a selection menu with all available buffers.
119If the value is a function, call it with no arguments.
120If nil, don't show the new tab button."
121 :type '(choice (const :tag "Buffer menu" t)
122 (function :tag "Function")
123 (const :tag "No button" nil))
124 :group 'tab-line
125 :version "27.1")
126
127(defvar tab-line-new-button
128 (propertize " + "
129 'display `(image :type xpm
130 :file ,(expand-file-name
131 "images/tabs/new.xpm"
132 data-directory)
133 :margin (2 . 0)
134 :ascent center)
135 'keymap tab-line-add-map
136 'mouse-face 'tab-line-highlight
137 'help-echo "Click to add tab")
138 "Button for creating a new tab.")
139
140(defcustom tab-line-close-button-show t
141 "Defines where to show the close tab button.
142If t, show the close tab button on all tabs.
143If `selected', show it only on the selected tab.
144If `non-selected', show it only on non-selected tab.
145If nil, don't show it at all."
146 :type '(choice (const :tag "On all tabs" t)
147 (const :tag "On selected tab" selected)
148 (const :tag "On non-selected tabs" non-selected)
149 (const :tag "None" nil))
150 :set (lambda (sym val)
151 (set sym val)
152 (force-mode-line-update))
153 :group 'tab-line
154 :version "27.1")
155
156(defvar tab-line-close-button
157 (propertize " x"
158 'display `(image :type xpm
159 :file ,(expand-file-name
160 "images/tabs/close.xpm"
161 data-directory)
162 :margin (2 . 0)
163 :ascent center)
164 'keymap tab-line-tab-close-map
165 'mouse-face 'tab-line-close-highlight
166 'help-echo "Click to close tab")
167 "Button for closing the clicked tab.")
168
169(defvar tab-line-separator nil)
170
171(defvar tab-line-tab-name-ellipsis
172 (if (char-displayable-p ?…) "…" "..."))
173
174
175(defvar tab-line-tab-name-function #'tab-line-tab-name
176 "Function to get a tab name.
177Function gets two arguments: tab to get name for and a list of tabs
178to display. By default, use function `tab-line-tab-name'.")
179
180(defun tab-line-tab-name (buffer &optional buffers)
181 "Generate tab name from BUFFER.
182Reduce tab width proportionally to space taken by other tabs.
183This function can be overridden by changing the default value of the
184variable `tab-line-tab-name-function'."
185 (let ((tab-name (buffer-name buffer))
186 (limit (when buffers
187 (max 1 (- (/ (window-width) (length buffers)) 3)))))
188 (if (or (not limit) (< (length tab-name) limit))
189 tab-name
190 (propertize (truncate-string-to-width tab-name limit nil nil
191 tab-line-tab-name-ellipsis)
192 'help-echo tab-name))))
193
194(defvar tab-line-tabs-limit 15
195 "Maximum number of buffer tabs displayed in the tab line.")
196
197(defvar tab-line-tabs-function #'tab-line-tabs
198 "Function to get a list of tabs to display in the tab line.
199This function should return either a list of buffers whose names will
200be displayed, or just a list of strings to display in the tab line.
201By default, use function `tab-line-tabs'.")
202
203(defun tab-line-tabs ()
204 "Return a list of tabs that should be displayed in the tab line.
205By default returns a list of window buffers, i.e. buffers previously
206shown in the same window where the tab line is displayed.
207This list can be overridden by changing the default value of the
208variable `tab-line-tabs-function'."
209 (let* ((window (selected-window))
210 (buffer (window-buffer window))
211 (next-buffers (seq-remove (lambda (b) (eq b buffer))
212 (window-next-buffers window)))
213 (next-buffers (seq-filter #'buffer-live-p next-buffers))
214 (prev-buffers (seq-remove (lambda (b) (eq b buffer))
215 (mapcar #'car (window-prev-buffers window))))
216 (prev-buffers (seq-filter #'buffer-live-p prev-buffers))
217 ;; Remove next-buffers from prev-buffers
218 (prev-buffers (seq-difference prev-buffers next-buffers))
219 (half-limit (/ tab-line-tabs-limit 2))
220 (prev-buffers-limit
221 (if (> (length prev-buffers) half-limit)
222 (if (> (length next-buffers) half-limit)
223 half-limit
224 (+ half-limit (- half-limit (length next-buffers))))
225 (length prev-buffers)))
226 (next-buffers-limit
227 (- tab-line-tabs-limit prev-buffers-limit))
228 (buffer-tabs
229 (append (reverse (seq-take prev-buffers prev-buffers-limit))
230 (list buffer)
231 (seq-take next-buffers next-buffers-limit))))
232 buffer-tabs))
233
234(defun tab-line-format ()
235 "Template for displaying tab line for selected window."
236 (let* ((window (selected-window))
237 (selected-buffer (window-buffer window))
238 (tabs (funcall tab-line-tabs-function))
239 (separator (or tab-line-separator (if window-system " " "|"))))
240 (append
241 (mapcar
242 (lambda (tab)
243 (concat
244 separator
245 (apply 'propertize (concat (propertize
246 (funcall tab-line-tab-name-function tab tabs)
247 'keymap tab-line-tab-map)
248 (or (and tab-line-close-button-show
249 (not (eq tab-line-close-button-show
250 (if (eq tab selected-buffer)
251 'non-selected
252 'selected)))
253 tab-line-close-button) ""))
254 `(
255 tab ,tab
256 face ,(if (eq tab selected-buffer)
257 'tab-line-tab
258 'tab-line-tab-inactive)
259 mouse-face tab-line-highlight))))
260 tabs)
261 (list (concat separator (when tab-line-new-tab-choice
262 tab-line-new-button))))))
263
264
265(defun tab-line-new-tab (&optional e)
266 "Add a new tab to the tab line.
267Usually is invoked by clicking on the plus-shaped button.
268But any switching to other buffer also adds a new tab
269corresponding to the switched buffer."
270 (interactive "e")
271 (if (functionp tab-line-new-tab-choice)
272 (funcall tab-line-new-tab-choice)
273 (if window-system ; (display-popup-menus-p)
274 (mouse-buffer-menu e) ; like (buffer-menu-open)
275 ;; tty menu doesn't support mouse clicks, so use tmm
276 (tmm-prompt (mouse-buffer-menu-keymap)))))
277
278(defun tab-line-select-tab (&optional e)
279 "Switch to the selected tab.
280This command maintains the original order of prev/next buffers.
281So for example, switching to a previous tab is equivalent to
282using the `previous-buffer' command."
283 (interactive "e")
284 (let* ((posnp (event-start e))
285 (window (posn-window posnp))
286 (buffer (get-pos-property 1 'tab (car (posn-string posnp))))
287 (window-buffer (window-buffer window))
288 (next-buffers (seq-remove (lambda (b) (eq b window-buffer))
289 (window-next-buffers window)))
290 (prev-buffers (seq-remove (lambda (b) (eq b window-buffer))
291 (mapcar #'car (window-prev-buffers window))))
292 ;; Remove next-buffers from prev-buffers
293 (prev-buffers (seq-difference prev-buffers next-buffers)))
294 (cond
295 ((memq buffer next-buffers)
296 (dotimes (_ (1+ (seq-position next-buffers buffer)))
297 (switch-to-next-buffer window)))
298 ((memq buffer prev-buffers)
299 (dotimes (_ (1+ (seq-position prev-buffers buffer)))
300 (switch-to-prev-buffer window)))
301 (t
302 (with-selected-window window
303 (switch-to-buffer buffer))))))
304
305(defun tab-line-switch-to-prev-tab (&optional e)
306 "Switch to the previous tab.
307Its effect is the same as using the `previous-buffer' command
308(\\[previous-buffer])."
309 (interactive "e")
310 (switch-to-prev-buffer (posn-window (event-start e))))
311
312(defun tab-line-switch-to-next-tab (&optional e)
313 "Switch to the next tab.
314Its effect is the same as using the `next-buffer' command
315(\\[next-buffer])."
316 (interactive "e")
317 (switch-to-next-buffer (posn-window (event-start e))))
318
319(defcustom tab-line-close-tab-action 'bury-buffer
320 "Defines what to do on closing the tab.
321If `bury-buffer', put the tab's buffer at the end of the list of all
322buffers that effectively hides the buffer's tab from the tab line.
323If `kill-buffer', kills the tab's buffer."
324 :type '(choice (const :tag "Bury buffer" bury-buffer)
325 (const :tag "Kill buffer" kill-buffer))
326 :group 'tab-line
327 :version "27.1")
328
329(defun tab-line-close-tab (&optional e)
330 "Close the selected tab.
331Usually is invoked by clicking on the close button on the right side
332of the tab. This command buries the buffer, so it goes out of sight
333from the tab line."
334 (interactive "e")
335 (let* ((posnp (event-start e))
336 (window (posn-window posnp))
337 (buffer (get-pos-property 1 'tab (car (posn-string posnp)))))
338 (with-selected-window window
339 (cond
340 ((eq tab-line-close-tab-action 'kill-buffer)
341 (kill-buffer buffer))
342 ((eq tab-line-close-tab-action 'bury-buffer)
343 (if (eq buffer (current-buffer))
344 (bury-buffer)
345 (set-window-prev-buffers nil (assq-delete-all buffer (window-prev-buffers)))
346 (set-window-next-buffers nil (delq buffer (window-next-buffers))))))
347 (force-mode-line-update))))
348
349
350;;;###autoload
351(define-minor-mode global-tab-line-mode
352 "Display window-local tab line."
353 :group 'tab-line
354 :type 'boolean
355 :global t
356 :init-value nil
357 (setq-default tab-line-format (when global-tab-line-mode
358 '(:eval (tab-line-format)))))
359
360
361(provide 'tab-line)
362;;; tab-line.el ends here
diff --git a/lisp/window.el b/lisp/window.el
index 620eacdd290..d93ec0add67 100644
--- a/lisp/window.el
+++ b/lisp/window.el
@@ -1419,7 +1419,10 @@ dumping to it."
1419 (format "frame text pixel: %s x %s cols/lines: %s x %s\n" 1419 (format "frame text pixel: %s x %s cols/lines: %s x %s\n"
1420 (frame-text-width frame) (frame-text-height frame) 1420 (frame-text-width frame) (frame-text-height frame)
1421 (frame-text-cols frame) (frame-text-lines frame)) 1421 (frame-text-cols frame) (frame-text-lines frame))
1422 (format "tool: %s scroll: %s/%s fringe: %s border: %s right: %s bottom: %s\n\n" 1422 (format "tab: %s tool: %s scroll: %s/%s fringe: %s border: %s right: %s bottom: %s\n\n"
1423 (if (fboundp 'tab-bar-height)
1424 (tab-bar-height frame t)
1425 "0")
1423 (if (fboundp 'tool-bar-height) 1426 (if (fboundp 'tool-bar-height)
1424 (tool-bar-height frame t) 1427 (tool-bar-height frame t)
1425 "0") 1428 "0")
diff --git a/lisp/xt-mouse.el b/lisp/xt-mouse.el
index 9e8a32a28ff..308f602b6d0 100644
--- a/lisp/xt-mouse.el
+++ b/lisp/xt-mouse.el
@@ -253,7 +253,13 @@ which is the \"1006\" extension implemented in Xterm >= 277."
253 (top (nth 1 ltrb)) 253 (top (nth 1 ltrb))
254 (posn (if w 254 (posn (if w
255 (posn-at-x-y (- x left) (- y top) w t) 255 (posn-at-x-y (- x left) (- y top) w t)
256 (append (list nil 'menu-bar) 256 (append (list nil (if (and tab-bar-mode
257 (or (not menu-bar-mode)
258 ;; The tab-bar is on the
259 ;; second row below menu-bar
260 (eq y 1)))
261 'tab-bar
262 'menu-bar))
257 (nthcdr 2 (posn-at-x-y x y))))) 263 (nthcdr 2 (posn-at-x-y x y)))))
258 (event (list type posn))) 264 (event (list type posn)))
259 (setcar (nthcdr 3 posn) timestamp) 265 (setcar (nthcdr 3 posn) timestamp)
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)
249 b->header_line_format_ = val; 249 b->header_line_format_ = val;
250} 250}
251static void 251static void
252bset_tab_line_format (struct buffer *b, Lisp_Object val)
253{
254 b->tab_line_format_ = val;
255}
256static void
252bset_indicate_buffer_boundaries (struct buffer *b, Lisp_Object val) 257bset_indicate_buffer_boundaries (struct buffer *b, Lisp_Object val)
253{ 258{
254 b->indicate_buffer_boundaries_ = val; 259 b->indicate_buffer_boundaries_ = val;
@@ -1329,7 +1334,7 @@ No argument or nil as argument means use current buffer as BUFFER. */)
1329DEFUN ("force-mode-line-update", Fforce_mode_line_update, 1334DEFUN ("force-mode-line-update", Fforce_mode_line_update,
1330 Sforce_mode_line_update, 0, 1, 0, 1335 Sforce_mode_line_update, 0, 1, 0,
1331 doc: /* Force redisplay of the current buffer's mode line and header line. 1336 doc: /* Force redisplay of the current buffer's mode line and header line.
1332With optional non-nil ALL, force redisplay of all mode lines and 1337With optional non-nil ALL, force redisplay of all mode lines, tab lines and
1333header lines. This function also forces recomputation of the 1338header lines. This function also forces recomputation of the
1334menu bar menus and the frame title. */) 1339menu bar menus and the frame title. */)
1335 (Lisp_Object all) 1340 (Lisp_Object all)
@@ -5194,6 +5199,7 @@ init_buffer_once (void)
5194 XSETFASTINT (BVAR (&buffer_local_flags, scroll_up_aggressively), idx); ++idx; 5199 XSETFASTINT (BVAR (&buffer_local_flags, scroll_up_aggressively), idx); ++idx;
5195 XSETFASTINT (BVAR (&buffer_local_flags, scroll_down_aggressively), idx); ++idx; 5200 XSETFASTINT (BVAR (&buffer_local_flags, scroll_down_aggressively), idx); ++idx;
5196 XSETFASTINT (BVAR (&buffer_local_flags, header_line_format), idx); ++idx; 5201 XSETFASTINT (BVAR (&buffer_local_flags, header_line_format), idx); ++idx;
5202 XSETFASTINT (BVAR (&buffer_local_flags, tab_line_format), idx); ++idx;
5197 XSETFASTINT (BVAR (&buffer_local_flags, cursor_type), idx); ++idx; 5203 XSETFASTINT (BVAR (&buffer_local_flags, cursor_type), idx); ++idx;
5198 XSETFASTINT (BVAR (&buffer_local_flags, extra_line_spacing), idx); ++idx; 5204 XSETFASTINT (BVAR (&buffer_local_flags, extra_line_spacing), idx); ++idx;
5199 XSETFASTINT (BVAR (&buffer_local_flags, cursor_in_non_selected_windows), idx); ++idx; 5205 XSETFASTINT (BVAR (&buffer_local_flags, cursor_in_non_selected_windows), idx); ++idx;
@@ -5239,6 +5245,7 @@ init_buffer_once (void)
5239 /* real setup is done in bindings.el */ 5245 /* real setup is done in bindings.el */
5240 bset_mode_line_format (&buffer_defaults, build_pure_c_string ("%-")); 5246 bset_mode_line_format (&buffer_defaults, build_pure_c_string ("%-"));
5241 bset_header_line_format (&buffer_defaults, Qnil); 5247 bset_header_line_format (&buffer_defaults, Qnil);
5248 bset_tab_line_format (&buffer_defaults, Qnil);
5242 bset_abbrev_mode (&buffer_defaults, Qnil); 5249 bset_abbrev_mode (&buffer_defaults, Qnil);
5243 bset_overwrite_mode (&buffer_defaults, Qnil); 5250 bset_overwrite_mode (&buffer_defaults, Qnil);
5244 bset_case_fold_search (&buffer_defaults, Qt); 5251 bset_case_fold_search (&buffer_defaults, Qt);
@@ -5510,6 +5517,13 @@ syms_of_buffer (void)
5510 Fput (Qprotected_field, Qerror_message, 5517 Fput (Qprotected_field, Qerror_message,
5511 build_pure_c_string ("Attempt to modify a protected field")); 5518 build_pure_c_string ("Attempt to modify a protected field"));
5512 5519
5520 DEFVAR_PER_BUFFER ("tab-line-format",
5521 &BVAR (current_buffer, tab_line_format),
5522 Qnil,
5523 doc: /* Analogous to `mode-line-format', but controls the tab line.
5524The tab line appears, optionally, at the top of a window;
5525the mode line appears at the bottom. */);
5526
5513 DEFVAR_PER_BUFFER ("header-line-format", 5527 DEFVAR_PER_BUFFER ("header-line-format",
5514 &BVAR (current_buffer, header_line_format), 5528 &BVAR (current_buffer, header_line_format),
5515 Qnil, 5529 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
348 of windows. Nil means don't display that line. */ 348 of windows. Nil means don't display that line. */
349 Lisp_Object header_line_format_; 349 Lisp_Object header_line_format_;
350 350
351 /* Analogous to mode_line_format for the line displayed at the top
352 of windows. Nil means don't display that line. */
353 Lisp_Object tab_line_format_;
354
351 /* Keys that are bound local to this buffer. */ 355 /* Keys that are bound local to this buffer. */
352 Lisp_Object keymap_; 356 Lisp_Object keymap_;
353 357
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
166 ON_MODE_LINE, 166 ON_MODE_LINE,
167 ON_VERTICAL_BORDER, 167 ON_VERTICAL_BORDER,
168 ON_HEADER_LINE, 168 ON_HEADER_LINE,
169 ON_TAB_LINE,
169 ON_LEFT_FRINGE, 170 ON_LEFT_FRINGE,
170 ON_RIGHT_FRINGE, 171 ON_RIGHT_FRINGE,
171 ON_LEFT_MARGIN, 172 ON_LEFT_MARGIN,
@@ -762,6 +763,9 @@ struct glyph_matrix
762 which do their own scrolling. */ 763 which do their own scrolling. */
763 bool_bf no_scrolling_p : 1; 764 bool_bf no_scrolling_p : 1;
764 765
766 /* True means window displayed in this matrix has a tab line. */
767 bool_bf tab_line_p : 1;
768
765 /* True means window displayed in this matrix has a header 769 /* True means window displayed in this matrix has a header
766 line. */ 770 line. */
767 bool_bf header_line_p : 1; 771 bool_bf header_line_p : 1;
@@ -1001,9 +1005,12 @@ struct glyph_row
1001 implies that the row doesn't have marginal areas. */ 1005 implies that the row doesn't have marginal areas. */
1002 bool_bf full_width_p : 1; 1006 bool_bf full_width_p : 1;
1003 1007
1004 /* True means row is a mode or header-line. */ 1008 /* True means row is a mode or header/tab-line. */
1005 bool_bf mode_line_p : 1; 1009 bool_bf mode_line_p : 1;
1006 1010
1011 /* True means row is a tab-line. */
1012 bool_bf tab_line_p : 1;
1013
1007 /* True in a current row means this row is overlapped by another row. */ 1014 /* True in a current row means this row is overlapped by another row. */
1008 bool_bf overlapped_p : 1; 1015 bool_bf overlapped_p : 1;
1009 1016
@@ -1084,16 +1091,25 @@ struct glyph_row *matrix_row (struct glyph_matrix *, int);
1084#define MATRIX_MODE_LINE_ROW(MATRIX) \ 1091#define MATRIX_MODE_LINE_ROW(MATRIX) \
1085 ((MATRIX)->rows + (MATRIX)->nrows - 1) 1092 ((MATRIX)->rows + (MATRIX)->nrows - 1)
1086 1093
1087/* Return a pointer to the row reserved for the header line in MATRIX. 1094/* Return a pointer to the row reserved for the tab line in MATRIX.
1088 This is always the first row in MATRIX because that's the only 1095 This is always the first row in MATRIX because that's the only
1089 way that works in frame-based redisplay. */ 1096 way that works in frame-based redisplay. */
1090 1097
1091#define MATRIX_HEADER_LINE_ROW(MATRIX) (MATRIX)->rows 1098#define MATRIX_TAB_LINE_ROW(MATRIX) (MATRIX)->rows
1099
1100/* Return a pointer to the row reserved for the header line in MATRIX.
1101 This is always the second row in MATRIX because that's the only
1102 way that works in frame-based redisplay. */
1103
1104#define MATRIX_HEADER_LINE_ROW(MATRIX) \
1105 ((MATRIX)->tab_line_p ? ((MATRIX)->rows + 1) : (MATRIX)->rows)
1092 1106
1093/* Return a pointer to first row in MATRIX used for text display. */ 1107/* Return a pointer to first row in MATRIX used for text display. */
1094 1108
1095#define MATRIX_FIRST_TEXT_ROW(MATRIX) \ 1109#define MATRIX_FIRST_TEXT_ROW(MATRIX) \
1096 ((MATRIX)->rows->mode_line_p ? (MATRIX)->rows + 1 : (MATRIX)->rows) 1110 ((MATRIX)->rows->mode_line_p ? \
1111 (((MATRIX)->rows + 1)->mode_line_p ? \
1112 (MATRIX)->rows + 2 : (MATRIX)->rows + 1) : (MATRIX)->rows)
1097 1113
1098/* Return a pointer to the first glyph in the text area of a row. 1114/* Return a pointer to the first glyph in the text area of a row.
1099 MATRIX is the glyph matrix accessed, and ROW is the row index in 1115 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);
1162 ((ROW)->height != (ROW)->visible_height) 1178 ((ROW)->height != (ROW)->visible_height)
1163 1179
1164#define MR_PARTIALLY_VISIBLE_AT_TOP(W, ROW) \ 1180#define MR_PARTIALLY_VISIBLE_AT_TOP(W, ROW) \
1165 ((ROW)->y < WINDOW_HEADER_LINE_HEIGHT ((W))) 1181 ((ROW)->y < (WINDOW_TAB_LINE_HEIGHT ((W)) + WINDOW_HEADER_LINE_HEIGHT ((W))))
1166 1182
1167#define MR_PARTIALLY_VISIBLE_AT_BOTTOM(W, ROW) \ 1183#define MR_PARTIALLY_VISIBLE_AT_BOTTOM(W, ROW) \
1168 (((ROW)->y + (ROW)->height - (ROW)->extra_line_spacing) \ 1184 (((ROW)->y + (ROW)->height - (ROW)->extra_line_spacing) \
@@ -1433,6 +1449,15 @@ struct glyph_string
1433 ? MATRIX_HEADER_LINE_ROW (MATRIX)->height \ 1449 ? MATRIX_HEADER_LINE_ROW (MATRIX)->height \
1434 : 0) 1450 : 0)
1435 1451
1452/* Return the height of the tab line in glyph matrix MATRIX, or zero
1453 if not known. This macro is called under circumstances where
1454 MATRIX might not have been allocated yet. */
1455
1456#define MATRIX_TAB_LINE_HEIGHT(MATRIX) \
1457 ((MATRIX) && (MATRIX)->rows \
1458 ? MATRIX_TAB_LINE_ROW (MATRIX)->height \
1459 : 0)
1460
1436/* Return the desired face id for the mode line of a window, depending 1461/* Return the desired face id for the mode line of a window, depending
1437 on whether the window is selected or not, or if the window is the 1462 on whether the window is selected or not, or if the window is the
1438 scrolling window for the currently active minibuffer window. 1463 scrolling window for the currently active minibuffer window.
@@ -1485,6 +1510,19 @@ struct glyph_string
1485 : estimate_mode_line_height \ 1510 : estimate_mode_line_height \
1486 (XFRAME (W->frame), HEADER_LINE_FACE_ID)))) 1511 (XFRAME (W->frame), HEADER_LINE_FACE_ID))))
1487 1512
1513/* Return the current height of the tab line of window W. If not known
1514 from W->tab_line_height, look at W's current glyph matrix, or return
1515 an estimation based on the height of the font of the face `tab-line'. */
1516
1517#define CURRENT_TAB_LINE_HEIGHT(W) \
1518 (W->tab_line_height >= 0 \
1519 ? W->tab_line_height \
1520 : (W->tab_line_height \
1521 = (MATRIX_TAB_LINE_HEIGHT (W->current_matrix) \
1522 ? MATRIX_TAB_LINE_HEIGHT (W->current_matrix) \
1523 : estimate_mode_line_height \
1524 (XFRAME (W->frame), TAB_LINE_FACE_ID))))
1525
1488/* Return the height of the desired mode line of window W. */ 1526/* Return the height of the desired mode line of window W. */
1489 1527
1490#define DESIRED_MODE_LINE_HEIGHT(W) \ 1528#define DESIRED_MODE_LINE_HEIGHT(W) \
@@ -1495,6 +1533,11 @@ struct glyph_string
1495#define DESIRED_HEADER_LINE_HEIGHT(W) \ 1533#define DESIRED_HEADER_LINE_HEIGHT(W) \
1496 MATRIX_HEADER_LINE_HEIGHT ((W)->desired_matrix) 1534 MATRIX_HEADER_LINE_HEIGHT ((W)->desired_matrix)
1497 1535
1536/* Return the height of the desired tab line of window W. */
1537
1538#define DESIRED_TAB_LINE_HEIGHT(W) \
1539 MATRIX_TAB_LINE_HEIGHT ((W)->desired_matrix)
1540
1498/* Return proper value to be used as baseline offset of font that has 1541/* Return proper value to be used as baseline offset of font that has
1499 ASCENT and DESCENT to draw characters by the font at the vertical 1542 ASCENT and DESCENT to draw characters by the font at the vertical
1500 center of the line of frame F. 1543 center of the line of frame F.
@@ -1780,6 +1823,8 @@ enum face_id
1780 WINDOW_DIVIDER_FIRST_PIXEL_FACE_ID, 1823 WINDOW_DIVIDER_FIRST_PIXEL_FACE_ID,
1781 WINDOW_DIVIDER_LAST_PIXEL_FACE_ID, 1824 WINDOW_DIVIDER_LAST_PIXEL_FACE_ID,
1782 INTERNAL_BORDER_FACE_ID, 1825 INTERNAL_BORDER_FACE_ID,
1826 TAB_BAR_FACE_ID,
1827 TAB_LINE_FACE_ID,
1783 BASIC_FACE_ID_SENTINEL 1828 BASIC_FACE_ID_SENTINEL
1784}; 1829};
1785 1830
@@ -2283,6 +2328,9 @@ struct it
2283 /* True means multibyte characters are enabled. */ 2328 /* True means multibyte characters are enabled. */
2284 bool_bf multibyte_p : 1; 2329 bool_bf multibyte_p : 1;
2285 2330
2331 /* True means window has a tab line at its top. */
2332 bool_bf tab_line_p : 1;
2333
2286 /* True means window has a mode line at its top. */ 2334 /* True means window has a mode line at its top. */
2287 bool_bf header_line_p : 1; 2335 bool_bf header_line_p : 1;
2288 2336
@@ -3130,6 +3178,50 @@ struct image_cache
3130 3178
3131 3179
3132/*********************************************************************** 3180/***********************************************************************
3181 Tab-bars
3182 ***********************************************************************/
3183
3184/* Enumeration defining where to find tab-bar item information in
3185 tab-bar items vectors stored with frames. Each tab-bar item
3186 occupies TAB_BAR_ITEM_NSLOTS elements in such a vector. */
3187
3188enum tab_bar_item_idx
3189{
3190 /* The key of the tab-bar item. Used to remove items when a binding
3191 for `undefined' is found. */
3192 TAB_BAR_ITEM_KEY,
3193
3194 /* Non-nil if item is enabled. */
3195 TAB_BAR_ITEM_ENABLED_P,
3196
3197 /* Non-nil if item is selected (pressed). */
3198 TAB_BAR_ITEM_SELECTED_P,
3199
3200 /* Caption. */
3201 TAB_BAR_ITEM_CAPTION,
3202
3203 /* The binding. */
3204 TAB_BAR_ITEM_BINDING,
3205
3206 /* Help string. */
3207 TAB_BAR_ITEM_HELP,
3208
3209 /* Sentinel = number of slots in tab_bar_items occupied by one
3210 tab-bar item. */
3211 TAB_BAR_ITEM_NSLOTS
3212};
3213
3214/* Default values of the above variables. */
3215
3216#define DEFAULT_TAB_BAR_BUTTON_MARGIN 4
3217#define DEFAULT_TAB_BAR_BUTTON_RELIEF 1
3218
3219/* The height in pixels of the default tab-bar images. */
3220
3221#define DEFAULT_TAB_BAR_IMAGE_HEIGHT 18
3222
3223
3224/***********************************************************************
3133 Tool-bars 3225 Tool-bars
3134 ***********************************************************************/ 3226 ***********************************************************************/
3135 3227
@@ -3285,6 +3377,7 @@ extern bool help_echo_showing_p;
3285extern Lisp_Object help_echo_string, help_echo_window; 3377extern Lisp_Object help_echo_string, help_echo_window;
3286extern Lisp_Object help_echo_object, previous_help_echo_string; 3378extern Lisp_Object help_echo_object, previous_help_echo_string;
3287extern ptrdiff_t help_echo_pos; 3379extern ptrdiff_t help_echo_pos;
3380extern int last_tab_bar_item;
3288extern int last_tool_bar_item; 3381extern int last_tool_bar_item;
3289extern void reseat_at_previous_visible_line_start (struct it *); 3382extern void reseat_at_previous_visible_line_start (struct it *);
3290extern Lisp_Object lookup_glyphless_char_display (int, struct it *); 3383extern Lisp_Object lookup_glyphless_char_display (int, struct it *);
@@ -3332,6 +3425,8 @@ extern void get_glyph_string_clip_rect (struct glyph_string *,
3332 NativeRectangle *nr); 3425 NativeRectangle *nr);
3333extern Lisp_Object find_hot_spot (Lisp_Object, int, int); 3426extern Lisp_Object find_hot_spot (Lisp_Object, int, int);
3334 3427
3428extern void handle_tab_bar_click (struct frame *,
3429 int, int, bool, int);
3335extern void handle_tool_bar_click (struct frame *, 3430extern void handle_tool_bar_click (struct frame *,
3336 int, int, bool, int); 3431 int, int, bool, int);
3337 3432
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 *);
80static void fill_up_glyph_row_with_spaces (struct glyph_row *); 80static void fill_up_glyph_row_with_spaces (struct glyph_row *);
81static void clear_window_matrices (struct window *, bool); 81static void clear_window_matrices (struct window *, bool);
82static void fill_up_glyph_row_area_with_spaces (struct glyph_row *, int); 82static void fill_up_glyph_row_area_with_spaces (struct glyph_row *, int);
83static int scrolling_window (struct window *, bool); 83static int scrolling_window (struct window *, int);
84static bool update_window_line (struct window *, int, bool *); 84static bool update_window_line (struct window *, int, bool *);
85static void mirror_make_current (struct window *, int); 85static void mirror_make_current (struct window *, int);
86#ifdef GLYPH_DEBUG 86#ifdef GLYPH_DEBUG
@@ -366,6 +366,8 @@ adjust_glyph_matrix (struct window *w, struct glyph_matrix *matrix, int x, int y
366 int i; 366 int i;
367 int new_rows; 367 int new_rows;
368 bool marginal_areas_changed_p = 0; 368 bool marginal_areas_changed_p = 0;
369 bool tab_line_changed_p = 0;
370 bool tab_line_p = 0;
369 bool header_line_changed_p = 0; 371 bool header_line_changed_p = 0;
370 bool header_line_p = 0; 372 bool header_line_p = 0;
371 int left = -1, right = -1; 373 int left = -1, right = -1;
@@ -377,9 +379,13 @@ adjust_glyph_matrix (struct window *w, struct glyph_matrix *matrix, int x, int y
377 { 379 {
378 window_box (w, ANY_AREA, 0, 0, &window_width, &window_height); 380 window_box (w, ANY_AREA, 0, 0, &window_width, &window_height);
379 381
382 tab_line_p = window_wants_tab_line (w);
383 tab_line_changed_p = tab_line_p != matrix->tab_line_p;
384
380 header_line_p = window_wants_header_line (w); 385 header_line_p = window_wants_header_line (w);
381 header_line_changed_p = header_line_p != matrix->header_line_p; 386 header_line_changed_p = header_line_p != matrix->header_line_p;
382 } 387 }
388 matrix->tab_line_p = tab_line_p;
383 matrix->header_line_p = header_line_p; 389 matrix->header_line_p = header_line_p;
384 390
385 /* If POOL is null, MATRIX is a window matrix for window-based redisplay. 391 /* 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
397 403
398 if (!marginal_areas_changed_p 404 if (!marginal_areas_changed_p
399 && !XFRAME (w->frame)->fonts_changed 405 && !XFRAME (w->frame)->fonts_changed
406 && !tab_line_changed_p
400 && !header_line_changed_p 407 && !header_line_changed_p
401 && matrix->window_pixel_left == WINDOW_LEFT_PIXEL_EDGE (w) 408 && matrix->window_pixel_left == WINDOW_LEFT_PIXEL_EDGE (w)
402 && matrix->window_pixel_top == WINDOW_TOP_PIXEL_EDGE (w) 409 && 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
448 if (w == NULL 455 if (w == NULL
449 || (row == matrix->rows + dim.height - 1 456 || (row == matrix->rows + dim.height - 1
450 && window_wants_mode_line (w)) 457 && window_wants_mode_line (w))
451 || (row == matrix->rows && matrix->header_line_p)) 458 || (row == matrix->rows && matrix->tab_line_p)
459 || (row == matrix->rows
460 && !matrix->tab_line_p && matrix->header_line_p)
461 || (row == (matrix->rows + 1)
462 && matrix->tab_line_p && matrix->header_line_p))
452 { 463 {
453 row->glyphs[TEXT_AREA] 464 row->glyphs[TEXT_AREA]
454 = row->glyphs[LEFT_MARGIN_AREA]; 465 = row->glyphs[LEFT_MARGIN_AREA];
@@ -478,6 +489,7 @@ adjust_glyph_matrix (struct window *w, struct glyph_matrix *matrix, int x, int y
478 Allocate glyph memory from the heap. */ 489 Allocate glyph memory from the heap. */
479 if (dim.width > matrix->matrix_w 490 if (dim.width > matrix->matrix_w
480 || new_rows 491 || new_rows
492 || tab_line_changed_p
481 || header_line_changed_p 493 || header_line_changed_p
482 || marginal_areas_changed_p) 494 || marginal_areas_changed_p)
483 { 495 {
@@ -493,7 +505,11 @@ adjust_glyph_matrix (struct window *w, struct glyph_matrix *matrix, int x, int y
493 /* The mode line, if displayed, never has marginal areas. */ 505 /* The mode line, if displayed, never has marginal areas. */
494 if ((row == matrix->rows + dim.height - 1 506 if ((row == matrix->rows + dim.height - 1
495 && !(w && window_wants_mode_line (w))) 507 && !(w && window_wants_mode_line (w)))
496 || (row == matrix->rows && matrix->header_line_p)) 508 || (row == matrix->rows && matrix->tab_line_p)
509 || (row == matrix->rows
510 && !matrix->tab_line_p && matrix->header_line_p)
511 || (row == (matrix->rows + 1)
512 && matrix->tab_line_p && matrix->header_line_p))
497 { 513 {
498 row->glyphs[TEXT_AREA] 514 row->glyphs[TEXT_AREA]
499 = row->glyphs[LEFT_MARGIN_AREA]; 515 = row->glyphs[LEFT_MARGIN_AREA];
@@ -539,6 +555,7 @@ adjust_glyph_matrix (struct window *w, struct glyph_matrix *matrix, int x, int y
539 upper window). Invalidate all rows that are no longer part 555 upper window). Invalidate all rows that are no longer part
540 of the window. */ 556 of the window. */
541 if (!marginal_areas_changed_p 557 if (!marginal_areas_changed_p
558 && !tab_line_changed_p
542 && !header_line_changed_p 559 && !header_line_changed_p
543 && new_rows == 0 560 && new_rows == 0
544 && dim.width == matrix->matrix_w 561 && dim.width == matrix->matrix_w
@@ -728,7 +745,7 @@ shift_glyph_matrix (struct window *w, struct glyph_matrix *matrix, int start, in
728 eassert (start >= 0 && start < matrix->nrows); 745 eassert (start >= 0 && start < matrix->nrows);
729 eassert (end >= 0 && end <= matrix->nrows); 746 eassert (end >= 0 && end <= matrix->nrows);
730 747
731 min_y = WINDOW_HEADER_LINE_HEIGHT (w); 748 min_y = WINDOW_TAB_LINE_HEIGHT (w) + WINDOW_HEADER_LINE_HEIGHT (w);
732 max_y = WINDOW_BOX_HEIGHT_NO_MODE_LINE (w); 749 max_y = WINDOW_BOX_HEIGHT_NO_MODE_LINE (w);
733 750
734 for (; start < end; ++start) 751 for (; start < end; ++start)
@@ -767,6 +784,12 @@ clear_current_matrices (register struct frame *f)
767 clear_glyph_matrix (XWINDOW (f->menu_bar_window)->current_matrix); 784 clear_glyph_matrix (XWINDOW (f->menu_bar_window)->current_matrix);
768#endif 785#endif
769 786
787#if defined (HAVE_WINDOW_SYSTEM)
788 /* Clear the matrix of the tab-bar window, if any. */
789 if (WINDOWP (f->tab_bar_window))
790 clear_glyph_matrix (XWINDOW (f->tab_bar_window)->current_matrix);
791#endif
792
770#if defined (HAVE_WINDOW_SYSTEM) && ! defined (HAVE_EXT_TOOL_BAR) 793#if defined (HAVE_WINDOW_SYSTEM) && ! defined (HAVE_EXT_TOOL_BAR)
771 /* Clear the matrix of the tool-bar window, if any. */ 794 /* Clear the matrix of the tool-bar window, if any. */
772 if (WINDOWP (f->tool_bar_window)) 795 if (WINDOWP (f->tool_bar_window))
@@ -792,6 +815,11 @@ clear_desired_matrices (register struct frame *f)
792 clear_glyph_matrix (XWINDOW (f->menu_bar_window)->desired_matrix); 815 clear_glyph_matrix (XWINDOW (f->menu_bar_window)->desired_matrix);
793#endif 816#endif
794 817
818#if defined (HAVE_WINDOW_SYSTEM)
819 if (WINDOWP (f->tab_bar_window))
820 clear_glyph_matrix (XWINDOW (f->tab_bar_window)->desired_matrix);
821#endif
822
795#if defined (HAVE_WINDOW_SYSTEM) && ! defined (HAVE_EXT_TOOL_BAR) 823#if defined (HAVE_WINDOW_SYSTEM) && ! defined (HAVE_EXT_TOOL_BAR)
796 if (WINDOWP (f->tool_bar_window)) 824 if (WINDOWP (f->tool_bar_window))
797 clear_glyph_matrix (XWINDOW (f->tool_bar_window)->desired_matrix); 825 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)
857{ 885{
858 int min_y, max_y; 886 int min_y, max_y;
859 887
860 min_y = WINDOW_HEADER_LINE_HEIGHT (w); 888 min_y = WINDOW_TAB_LINE_HEIGHT (w) + WINDOW_HEADER_LINE_HEIGHT (w);
861 max_y = WINDOW_BOX_HEIGHT_NO_MODE_LINE (w); 889 max_y = WINDOW_BOX_HEIGHT_NO_MODE_LINE (w);
862 890
863 clear_glyph_row (row); 891 clear_glyph_row (row);
@@ -1062,7 +1090,7 @@ find_glyph_row_slice (struct glyph_matrix *window_matrix,
1062 call to this function really clears it. In addition, this function 1090 call to this function really clears it. In addition, this function
1063 makes sure the marginal areas of ROW are in sync with the window's 1091 makes sure the marginal areas of ROW are in sync with the window's
1064 display margins. MODE_LINE_P non-zero means we are preparing a 1092 display margins. MODE_LINE_P non-zero means we are preparing a
1065 glyph row for header line or mode line. */ 1093 glyph row for tab/header line or mode line. */
1066 1094
1067void 1095void
1068prepare_desired_row (struct window *w, struct glyph_row *row, bool mode_line_p) 1096prepare_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)
1077 } 1105 }
1078 if (mode_line_p) 1106 if (mode_line_p)
1079 { 1107 {
1080 /* Mode and header lines, if displayed, never have marginal 1108 /* Mode and header/tab lines, if displayed, never have marginal
1081 areas. If we are called with MODE_LINE_P non-zero, we are 1109 areas. If we are called with MODE_LINE_P non-zero, we are
1082 displaying the mode/header line in this window, and so the 1110 displaying the mode/header/tab line in this window, and so the
1083 marginal areas of this glyph row should be eliminated. This 1111 marginal areas of this glyph row should be eliminated. This
1084 is needed when the mode/header line is switched on in a 1112 is needed when the mode/header/tab line is switched on in a
1085 window that has display margins. */ 1113 window that has display margins. */
1086 if (w->left_margin_cols > 0) 1114 if (w->left_margin_cols > 0)
1087 row->glyphs[TEXT_AREA] = row->glyphs[LEFT_MARGIN_AREA]; 1115 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)
1099 1127
1100 /* Make sure the marginal areas of this row are in sync with 1128 /* Make sure the marginal areas of this row are in sync with
1101 what the window wants, when the row actually displays text 1129 what the window wants, when the row actually displays text
1102 and not header/mode line. */ 1130 and not tab/header/mode line. */
1103 if (w->left_margin_cols > 0 1131 if (w->left_margin_cols > 0
1104 && (left != row->glyphs[TEXT_AREA] - row->glyphs[LEFT_MARGIN_AREA])) 1132 && (left != row->glyphs[TEXT_AREA] - row->glyphs[LEFT_MARGIN_AREA]))
1105 row->glyphs[TEXT_AREA] = row->glyphs[LEFT_MARGIN_AREA] + left; 1133 row->glyphs[TEXT_AREA] = row->glyphs[LEFT_MARGIN_AREA] + left;
@@ -1708,8 +1736,8 @@ required_matrix_height (struct window *w)
1708 /* One partially visible line at the top and 1736 /* One partially visible line at the top and
1709 bottom of the window. */ 1737 bottom of the window. */
1710 + 2 1738 + 2
1711 /* 2 for header and mode line. */ 1739 /* 3 for tab, header and mode line. */
1712 + 2); 1740 + 3);
1713 } 1741 }
1714#endif /* HAVE_WINDOW_SYSTEM */ 1742#endif /* HAVE_WINDOW_SYSTEM */
1715 1743
@@ -1862,6 +1890,7 @@ fake_current_matrices (Lisp_Object window)
1862 - r->used[LEFT_MARGIN_AREA] 1890 - r->used[LEFT_MARGIN_AREA]
1863 - r->used[RIGHT_MARGIN_AREA]); 1891 - r->used[RIGHT_MARGIN_AREA]);
1864 r->mode_line_p = 0; 1892 r->mode_line_p = 0;
1893 r->tab_line_p = 0;
1865 } 1894 }
1866 } 1895 }
1867 } 1896 }
@@ -2106,6 +2135,36 @@ adjust_frame_glyphs_for_window_redisplay (struct frame *f)
2106 } 2135 }
2107#endif 2136#endif
2108 2137
2138#if defined (HAVE_WINDOW_SYSTEM)
2139 {
2140 /* Allocate/ reallocate matrices of the tab bar window. If we
2141 don't have a tab bar window yet, make one. */
2142 struct window *w;
2143 if (NILP (f->tab_bar_window))
2144 {
2145 Lisp_Object frame;
2146 fset_tab_bar_window (f, make_window ());
2147 w = XWINDOW (f->tab_bar_window);
2148 XSETFRAME (frame, f);
2149 wset_frame (w, frame);
2150 w->pseudo_window_p = 1;
2151 }
2152 else
2153 w = XWINDOW (f->tab_bar_window);
2154
2155 w->pixel_left = 0;
2156 w->left_col = 0;
2157 w->pixel_top = FRAME_MENU_BAR_HEIGHT (f);
2158 w->top_line = FRAME_MENU_BAR_LINES (f);
2159 w->total_cols = FRAME_TOTAL_COLS (f);
2160 w->pixel_width = (FRAME_PIXEL_WIDTH (f)
2161 - 2 * FRAME_INTERNAL_BORDER_WIDTH (f));
2162 w->total_lines = FRAME_TAB_BAR_LINES (f);
2163 w->pixel_height = FRAME_TAB_BAR_HEIGHT (f);
2164 allocate_matrices_for_window_redisplay (w);
2165 }
2166#endif
2167
2109#if defined (HAVE_WINDOW_SYSTEM) && ! defined (HAVE_EXT_TOOL_BAR) 2168#if defined (HAVE_WINDOW_SYSTEM) && ! defined (HAVE_EXT_TOOL_BAR)
2110 { 2169 {
2111 /* Allocate/ reallocate matrices of the tool bar window. If we 2170 /* Allocate/ reallocate matrices of the tool bar window. If we
@@ -2125,8 +2184,8 @@ adjust_frame_glyphs_for_window_redisplay (struct frame *f)
2125 2184
2126 w->pixel_left = 0; 2185 w->pixel_left = 0;
2127 w->left_col = 0; 2186 w->left_col = 0;
2128 w->pixel_top = FRAME_MENU_BAR_HEIGHT (f); 2187 w->pixel_top = FRAME_MENU_BAR_HEIGHT (f) + FRAME_TAB_BAR_HEIGHT (f);
2129 w->top_line = FRAME_MENU_BAR_LINES (f); 2188 w->top_line = FRAME_MENU_BAR_LINES (f) + FRAME_TAB_BAR_LINES (f);
2130 w->total_cols = FRAME_TOTAL_COLS (f); 2189 w->total_cols = FRAME_TOTAL_COLS (f);
2131 w->pixel_width = (FRAME_PIXEL_WIDTH (f) 2190 w->pixel_width = (FRAME_PIXEL_WIDTH (f)
2132 - 2 * FRAME_INTERNAL_BORDER_WIDTH (f)); 2191 - 2 * FRAME_INTERNAL_BORDER_WIDTH (f));
@@ -2188,6 +2247,18 @@ free_glyphs (struct frame *f)
2188 } 2247 }
2189#endif 2248#endif
2190 2249
2250#if defined (HAVE_WINDOW_SYSTEM)
2251 /* Free the tab bar window and its glyph matrices. */
2252 if (!NILP (f->tab_bar_window))
2253 {
2254 struct window *w = XWINDOW (f->tab_bar_window);
2255 free_glyph_matrix (w->desired_matrix);
2256 free_glyph_matrix (w->current_matrix);
2257 w->desired_matrix = w->current_matrix = NULL;
2258 fset_tab_bar_window (f, Qnil);
2259 }
2260#endif
2261
2191#if defined (HAVE_WINDOW_SYSTEM) && ! defined (HAVE_EXT_TOOL_BAR) 2262#if defined (HAVE_WINDOW_SYSTEM) && ! defined (HAVE_EXT_TOOL_BAR)
2192 /* Free the tool bar window and its glyph matrices. */ 2263 /* Free the tool bar window and its glyph matrices. */
2193 if (!NILP (f->tool_bar_window)) 2264 if (!NILP (f->tool_bar_window))
@@ -3082,6 +3153,29 @@ update_frame (struct frame *f, bool force_p, bool inhibit_hairy_id_p)
3082 update_window (XWINDOW (f->menu_bar_window), true); 3153 update_window (XWINDOW (f->menu_bar_window), true);
3083#endif 3154#endif
3084 3155
3156#if defined (HAVE_WINDOW_SYSTEM)
3157 /* Update the tab-bar window, if present. */
3158 if (WINDOWP (f->tab_bar_window))
3159 {
3160 struct window *w = XWINDOW (f->tab_bar_window);
3161
3162 /* Update tab-bar window. */
3163 if (w->must_be_updated_p)
3164 {
3165 Lisp_Object tem;
3166
3167 update_window (w, true);
3168 w->must_be_updated_p = false;
3169
3170 /* Swap tab-bar strings. We swap because we want to
3171 reuse strings. */
3172 tem = f->current_tab_bar_string;
3173 fset_current_tab_bar_string (f, f->desired_tab_bar_string);
3174 fset_desired_tab_bar_string (f, tem);
3175 }
3176 }
3177#endif
3178
3085#if defined (HAVE_WINDOW_SYSTEM) && ! defined (HAVE_EXT_TOOL_BAR) 3179#if defined (HAVE_WINDOW_SYSTEM) && ! defined (HAVE_EXT_TOOL_BAR)
3086 /* Update the tool-bar window, if present. */ 3180 /* Update the tool-bar window, if present. */
3087 if (WINDOWP (f->tool_bar_window)) 3181 if (WINDOWP (f->tool_bar_window))
@@ -3408,6 +3502,7 @@ update_window (struct window *w, bool force_p)
3408 { 3502 {
3409 struct glyph_row *row, *end; 3503 struct glyph_row *row, *end;
3410 struct glyph_row *mode_line_row; 3504 struct glyph_row *mode_line_row;
3505 struct glyph_row *tab_line_row;
3411 struct glyph_row *header_line_row; 3506 struct glyph_row *header_line_row;
3412 int yb; 3507 int yb;
3413 bool changed_p = 0, mouse_face_overwritten_p = 0; 3508 bool changed_p = 0, mouse_face_overwritten_p = 0;
@@ -3420,6 +3515,16 @@ update_window (struct window *w, bool force_p)
3420 row = MATRIX_ROW (desired_matrix, 0); 3515 row = MATRIX_ROW (desired_matrix, 0);
3421 end = MATRIX_MODE_LINE_ROW (desired_matrix); 3516 end = MATRIX_MODE_LINE_ROW (desired_matrix);
3422 3517
3518 /* Take note of the tab line, if there is one. We will
3519 update it below, after updating all of the window's lines. */
3520 if (row->mode_line_p && row->tab_line_p)
3521 {
3522 tab_line_row = row;
3523 ++row;
3524 }
3525 else
3526 tab_line_row = NULL;
3527
3423 /* Take note of the header line, if there is one. We will 3528 /* Take note of the header line, if there is one. We will
3424 update it below, after updating all of the window's lines. */ 3529 update it below, after updating all of the window's lines. */
3425 if (row->mode_line_p) 3530 if (row->mode_line_p)
@@ -3449,7 +3554,8 @@ update_window (struct window *w, bool force_p)
3449 /* Try reusing part of the display by copying. */ 3554 /* Try reusing part of the display by copying. */
3450 if (row < end && !desired_matrix->no_scrolling_p) 3555 if (row < end && !desired_matrix->no_scrolling_p)
3451 { 3556 {
3452 int rc = scrolling_window (w, header_line_row != NULL); 3557 int rc = scrolling_window (w, (tab_line_row != NULL ? 1 : 0)
3558 + (header_line_row != NULL ? 1 : 0));
3453 if (rc < 0) 3559 if (rc < 0)
3454 { 3560 {
3455 /* All rows were found to be equal. */ 3561 /* All rows were found to be equal. */
@@ -3501,13 +3607,22 @@ update_window (struct window *w, bool force_p)
3501 3607
3502 set_cursor: 3608 set_cursor:
3503 3609
3610 /* Update the tab line after scrolling because a new tab
3611 line would otherwise overwrite lines at the top of the window
3612 that can be scrolled. */
3613 if (tab_line_row && tab_line_row->enabled_p)
3614 {
3615 tab_line_row->y = 0;
3616 update_window_line (w, 0, &mouse_face_overwritten_p);
3617 }
3618
3504 /* Update the header line after scrolling because a new header 3619 /* Update the header line after scrolling because a new header
3505 line would otherwise overwrite lines at the top of the window 3620 line would otherwise overwrite lines at the top of the window
3506 that can be scrolled. */ 3621 that can be scrolled. */
3507 if (header_line_row && header_line_row->enabled_p) 3622 if (header_line_row && header_line_row->enabled_p)
3508 { 3623 {
3509 header_line_row->y = 0; 3624 header_line_row->y = tab_line_row ? CURRENT_TAB_LINE_HEIGHT (w) : 0;
3510 update_window_line (w, 0, &mouse_face_overwritten_p); 3625 update_window_line (w, tab_line_row ? 1 : 0, &mouse_face_overwritten_p);
3511 } 3626 }
3512 3627
3513 /* Fix the appearance of overlapping/overlapped rows. */ 3628 /* Fix the appearance of overlapping/overlapped rows. */
@@ -4178,7 +4293,7 @@ add_row_entry (struct glyph_row *row)
4178 1 if we did scroll. */ 4293 1 if we did scroll. */
4179 4294
4180static int 4295static int
4181scrolling_window (struct window *w, bool header_line_p) 4296scrolling_window (struct window *w, int tab_line_p)
4182{ 4297{
4183 struct glyph_matrix *desired_matrix = w->desired_matrix; 4298 struct glyph_matrix *desired_matrix = w->desired_matrix;
4184 struct glyph_matrix *current_matrix = w->current_matrix; 4299 struct glyph_matrix *current_matrix = w->current_matrix;
@@ -4191,7 +4306,7 @@ scrolling_window (struct window *w, bool header_line_p)
4191 struct redisplay_interface *rif = FRAME_RIF (XFRAME (WINDOW_FRAME (w))); 4306 struct redisplay_interface *rif = FRAME_RIF (XFRAME (WINDOW_FRAME (w)));
4192 4307
4193 /* Skip over rows equal at the start. */ 4308 /* Skip over rows equal at the start. */
4194 for (i = header_line_p; i < current_matrix->nrows - 1; ++i) 4309 for (i = tab_line_p; i < current_matrix->nrows - 1; ++i)
4195 { 4310 {
4196 struct glyph_row *d = MATRIX_ROW (desired_matrix, i); 4311 struct glyph_row *d = MATRIX_ROW (desired_matrix, i);
4197 struct glyph_row *c = MATRIX_ROW (current_matrix, i); 4312 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
5318 start position, i.e. it excludes the header-line row, but 5433 start position, i.e. it excludes the header-line row, but
5319 MATRIX_ROW includes the header-line row. Adjust for a possible 5434 MATRIX_ROW includes the header-line row. Adjust for a possible
5320 header-line row. */ 5435 header-line row. */
5321 it_vpos = it.vpos + window_wants_header_line (w); 5436 it_vpos = it.vpos + window_wants_header_line (w)
5437 + window_wants_tab_line (w);
5322 if (it_vpos < w->current_matrix->nrows 5438 if (it_vpos < w->current_matrix->nrows
5323 && (row = MATRIX_ROW (w->current_matrix, it_vpos), 5439 && (row = MATRIX_ROW (w->current_matrix, it_vpos),
5324 row->enabled_p)) 5440 row->enabled_p))
@@ -5382,6 +5498,8 @@ mode_line_string (struct window *w, enum window_part part,
5382 5498
5383 if (part == ON_MODE_LINE) 5499 if (part == ON_MODE_LINE)
5384 row = MATRIX_MODE_LINE_ROW (w->current_matrix); 5500 row = MATRIX_MODE_LINE_ROW (w->current_matrix);
5501 else if (part == ON_TAB_LINE)
5502 row = MATRIX_TAB_LINE_ROW (w->current_matrix);
5385 else 5503 else
5386 row = MATRIX_HEADER_LINE_ROW (w->current_matrix); 5504 row = MATRIX_HEADER_LINE_ROW (w->current_matrix);
5387 y0 = *y - row->y; 5505 y0 = *y - row->y;
@@ -5563,7 +5681,8 @@ handle_window_change_signal (int sig)
5563 structures now. Let that be done later outside of the 5681 structures now. Let that be done later outside of the
5564 signal handler. */ 5682 signal handler. */
5565 change_frame_size (XFRAME (frame), width, 5683 change_frame_size (XFRAME (frame), width,
5566 height - FRAME_MENU_BAR_LINES (XFRAME (frame)), 5684 height - FRAME_MENU_BAR_LINES (XFRAME (frame))
5685 - FRAME_TAB_BAR_LINES (XFRAME (frame)),
5567 0, 1, 0, 0); 5686 0, 1, 0, 0);
5568 } 5687 }
5569 } 5688 }
@@ -6243,7 +6362,8 @@ init_display_interactive (void)
6243 change_frame_size (XFRAME (selected_frame), 6362 change_frame_size (XFRAME (selected_frame),
6244 FrameCols (t->display_info.tty), 6363 FrameCols (t->display_info.tty),
6245 FrameRows (t->display_info.tty) 6364 FrameRows (t->display_info.tty)
6246 - FRAME_MENU_BAR_LINES (f), 0, 0, 1, 0); 6365 - FRAME_MENU_BAR_LINES (f)
6366 - FRAME_TAB_BAR_LINES (f), 0, 0, 1, 0);
6247 6367
6248 /* Delete the initial terminal. */ 6368 /* Delete the initial terminal. */
6249 if (--initial_terminal->reference_count == 0 6369 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;
69/* False means there are no visible garbaged frames. */ 69/* False means there are no visible garbaged frames. */
70bool frame_garbaged; 70bool frame_garbaged;
71 71
72/* The default tab bar height for future frames. */
73int frame_default_tab_bar_height;
74
72/* The default tool bar height for future frames. */ 75/* The default tool bar height for future frames. */
73#ifdef HAVE_EXT_TOOL_BAR 76#ifdef HAVE_EXT_TOOL_BAR
74enum { frame_default_tool_bar_height = 0 }; 77enum { frame_default_tool_bar_height = 0 };
@@ -230,6 +233,35 @@ set_menu_bar_lines (struct frame *f, Lisp_Object value, Lisp_Object oldval)
230 0, 1, 0, 0); 233 0, 1, 0, 0);
231 } 234 }
232} 235}
236
237static void
238set_tab_bar_lines (struct frame *f, Lisp_Object value, Lisp_Object oldval)
239{
240 int nlines;
241 int olines = FRAME_TAB_BAR_LINES (f);
242
243 /* Right now, tab bars don't work properly in minibuf-only frames;
244 most of the commands try to apply themselves to the minibuffer
245 frame itself, and get an error because you can't switch buffers
246 in or split the minibuffer window. */
247 if (FRAME_MINIBUF_ONLY_P (f))
248 return;
249
250 if (TYPE_RANGED_FIXNUMP (int, value))
251 nlines = XFIXNUM (value);
252 else
253 nlines = 0;
254
255 if (nlines != olines)
256 {
257 windows_or_buffers_changed = 14;
258 FRAME_TAB_BAR_LINES (f) = nlines;
259 FRAME_TAB_BAR_HEIGHT (f) = nlines * FRAME_LINE_HEIGHT (f);
260 change_frame_size (f, FRAME_COLS (f),
261 FRAME_LINES (f) + olines - nlines,
262 0, 1, 0, 0);
263 }
264}
233 265
234Lisp_Object Vframe_list; 266Lisp_Object Vframe_list;
235 267
@@ -379,6 +411,7 @@ frame_windows_min_size (Lisp_Object frame, Lisp_Object horizontal,
379 if ((FRAME_TERMCAP_P (f) || FRAME_MSDOS_P (f)) && NILP (horizontal)) 411 if ((FRAME_TERMCAP_P (f) || FRAME_MSDOS_P (f)) && NILP (horizontal))
380 { 412 {
381 int min_height = (FRAME_MENU_BAR_LINES (f) 413 int min_height = (FRAME_MENU_BAR_LINES (f)
414 + FRAME_TAB_BAR_LINES (f)
382 + FRAME_WANTS_MODELINE_P (f) 415 + FRAME_WANTS_MODELINE_P (f)
383 + 2); /* one text line and one echo-area line */ 416 + 2); /* one text line and one echo-area line */
384 if (retval < min_height) 417 if (retval < min_height)
@@ -719,6 +752,15 @@ adjust_frame_size (struct frame *f, int new_width, int new_height, int inhibit,
719 if ((FRAME_TERMCAP_P (f) && !pretend) || FRAME_MSDOS_P (f)) 752 if ((FRAME_TERMCAP_P (f) && !pretend) || FRAME_MSDOS_P (f))
720 FrameCols (FRAME_TTY (f)) = new_cols; 753 FrameCols (FRAME_TTY (f)) = new_cols;
721 754
755#if defined (HAVE_WINDOW_SYSTEM)
756 if (WINDOWP (f->tab_bar_window))
757 {
758 XWINDOW (f->tab_bar_window)->pixel_width = new_windows_width;
759 XWINDOW (f->tab_bar_window)->total_cols
760 = new_windows_width / unit_width;
761 }
762#endif
763
722#if defined (HAVE_WINDOW_SYSTEM) && ! defined (HAVE_EXT_TOOL_BAR) 764#if defined (HAVE_WINDOW_SYSTEM) && ! defined (HAVE_EXT_TOOL_BAR)
723 if (WINDOWP (f->tool_bar_window)) 765 if (WINDOWP (f->tool_bar_window))
724 { 766 {
@@ -838,6 +880,8 @@ make_frame (bool mini_p)
838 f->after_make_frame = false; 880 f->after_make_frame = false;
839 f->inhibit_horizontal_resize = false; 881 f->inhibit_horizontal_resize = false;
840 f->inhibit_vertical_resize = false; 882 f->inhibit_vertical_resize = false;
883 f->tab_bar_redisplayed = false;
884 f->tab_bar_resized = false;
841 f->tool_bar_redisplayed = false; 885 f->tool_bar_redisplayed = false;
842 f->tool_bar_resized = false; 886 f->tool_bar_resized = false;
843 f->column_width = 1; /* !FRAME_WINDOW_P value. */ 887 f->column_width = 1; /* !FRAME_WINDOW_P value. */
@@ -856,6 +900,7 @@ make_frame (bool mini_p)
856 f->no_accept_focus = false; 900 f->no_accept_focus = false;
857 f->z_group = z_group_none; 901 f->z_group = z_group_none;
858 f->tooltip = false; 902 f->tooltip = false;
903 f->last_tab_bar_item = -1;
859#ifndef HAVE_EXT_TOOL_BAR 904#ifndef HAVE_EXT_TOOL_BAR
860 f->last_tool_bar_item = -1; 905 f->last_tool_bar_item = -1;
861#endif 906#endif
@@ -1084,6 +1129,9 @@ make_initial_frame (void)
1084 /* The default value of menu-bar-mode is t. */ 1129 /* The default value of menu-bar-mode is t. */
1085 set_menu_bar_lines (f, make_fixnum (1), Qnil); 1130 set_menu_bar_lines (f, make_fixnum (1), Qnil);
1086 1131
1132 /* The default value of tab-bar-mode is nil. */
1133 set_tab_bar_lines (f, make_fixnum (0), Qnil);
1134
1087 /* Allocate glyph matrices. */ 1135 /* Allocate glyph matrices. */
1088 adjust_frame_glyphs (f); 1136 adjust_frame_glyphs (f);
1089 1137
@@ -1142,9 +1190,13 @@ make_terminal_frame (struct terminal *terminal)
1142#endif 1190#endif
1143 1191
1144 FRAME_MENU_BAR_LINES (f) = NILP (Vmenu_bar_mode) ? 0 : 1; 1192 FRAME_MENU_BAR_LINES (f) = NILP (Vmenu_bar_mode) ? 0 : 1;
1145 FRAME_LINES (f) = FRAME_LINES (f) - FRAME_MENU_BAR_LINES (f); 1193 FRAME_TAB_BAR_LINES (f) = NILP (Vtab_bar_mode) ? 0 : 1;
1194 FRAME_LINES (f) = FRAME_LINES (f) - FRAME_MENU_BAR_LINES (f)
1195 - FRAME_TAB_BAR_LINES (f);
1146 FRAME_MENU_BAR_HEIGHT (f) = FRAME_MENU_BAR_LINES (f) * FRAME_LINE_HEIGHT (f); 1196 FRAME_MENU_BAR_HEIGHT (f) = FRAME_MENU_BAR_LINES (f) * FRAME_LINE_HEIGHT (f);
1147 FRAME_TEXT_HEIGHT (f) = FRAME_TEXT_HEIGHT (f) - FRAME_MENU_BAR_HEIGHT (f); 1197 FRAME_TAB_BAR_HEIGHT (f) = FRAME_TAB_BAR_LINES (f) * FRAME_LINE_HEIGHT (f);
1198 FRAME_TEXT_HEIGHT (f) = FRAME_TEXT_HEIGHT (f) - FRAME_MENU_BAR_HEIGHT (f)
1199 - FRAME_TAB_BAR_HEIGHT (f);
1148 1200
1149 /* Set the top frame to the newly created frame. */ 1201 /* Set the top frame to the newly created frame. */
1150 if (FRAMEP (FRAME_TTY (f)->top_frame) 1202 if (FRAMEP (FRAME_TTY (f)->top_frame)
@@ -1266,7 +1318,8 @@ affects all frames on the same terminal device. */)
1266 { 1318 {
1267 int width, height; 1319 int width, height;
1268 get_tty_size (fileno (FRAME_TTY (f)->input), &width, &height); 1320 get_tty_size (fileno (FRAME_TTY (f)->input), &width, &height);
1269 adjust_frame_size (f, width, height - FRAME_MENU_BAR_LINES (f), 1321 adjust_frame_size (f, width, height - FRAME_MENU_BAR_LINES (f)
1322 - FRAME_TAB_BAR_LINES (f),
1270 5, 0, Qterminal_frame); 1323 5, 0, Qterminal_frame);
1271 } 1324 }
1272 1325
@@ -3071,6 +3124,8 @@ store_frame_param (struct frame *f, Lisp_Object prop, Lisp_Object val)
3071 { 3124 {
3072 if (EQ (prop, Qmenu_bar_lines)) 3125 if (EQ (prop, Qmenu_bar_lines))
3073 set_menu_bar_lines (f, val, make_fixnum (FRAME_MENU_BAR_LINES (f))); 3126 set_menu_bar_lines (f, val, make_fixnum (FRAME_MENU_BAR_LINES (f)));
3127 else if (EQ (prop, Qtab_bar_lines))
3128 set_tab_bar_lines (f, val, make_fixnum (FRAME_TAB_BAR_LINES (f)));
3074 else if (EQ (prop, Qname)) 3129 else if (EQ (prop, Qname))
3075 set_term_frame_name (f, val); 3130 set_term_frame_name (f, val);
3076 } 3131 }
@@ -3166,6 +3221,8 @@ If FRAME is omitted or nil, return information on the currently selected frame.
3166 Lisp_Object lines; 3221 Lisp_Object lines;
3167 XSETFASTINT (lines, FRAME_MENU_BAR_LINES (f)); 3222 XSETFASTINT (lines, FRAME_MENU_BAR_LINES (f));
3168 store_in_alist (&alist, Qmenu_bar_lines, lines); 3223 store_in_alist (&alist, Qmenu_bar_lines, lines);
3224 XSETFASTINT (lines, FRAME_TAB_BAR_LINES (f));
3225 store_in_alist (&alist, Qtab_bar_lines, lines);
3169 } 3226 }
3170 3227
3171 return alist; 3228 return alist;
@@ -3695,6 +3752,7 @@ static const struct frame_parm_table frame_parms[] =
3695 {"vertical-scroll-bars", SYMBOL_INDEX (Qvertical_scroll_bars)}, 3752 {"vertical-scroll-bars", SYMBOL_INDEX (Qvertical_scroll_bars)},
3696 {"horizontal-scroll-bars", SYMBOL_INDEX (Qhorizontal_scroll_bars)}, 3753 {"horizontal-scroll-bars", SYMBOL_INDEX (Qhorizontal_scroll_bars)},
3697 {"visibility", SYMBOL_INDEX (Qvisibility)}, 3754 {"visibility", SYMBOL_INDEX (Qvisibility)},
3755 {"tab-bar-lines", SYMBOL_INDEX (Qtab_bar_lines)},
3698 {"tool-bar-lines", SYMBOL_INDEX (Qtool_bar_lines)}, 3756 {"tool-bar-lines", SYMBOL_INDEX (Qtool_bar_lines)},
3699 {"scroll-bar-foreground", SYMBOL_INDEX (Qscroll_bar_foreground)}, 3757 {"scroll-bar-foreground", SYMBOL_INDEX (Qscroll_bar_foreground)},
3700 {"scroll-bar-background", SYMBOL_INDEX (Qscroll_bar_background)}, 3758 {"scroll-bar-background", SYMBOL_INDEX (Qscroll_bar_background)},
@@ -4450,6 +4508,8 @@ gui_set_font (struct frame *f, Lisp_Object arg, Lisp_Object oldval)
4450#ifdef HAVE_X_WINDOWS 4508#ifdef HAVE_X_WINDOWS
4451 store_frame_param (f, Qfont_parameter, font_param); 4509 store_frame_param (f, Qfont_parameter, font_param);
4452#endif 4510#endif
4511 /* Recalculate tabbar height. */
4512 f->n_tab_bar_rows = 0;
4453 /* Recalculate toolbar height. */ 4513 /* Recalculate toolbar height. */
4454 f->n_tool_bar_rows = 0; 4514 f->n_tool_bar_rows = 0;
4455 4515
@@ -5390,8 +5450,8 @@ On Nextstep, this just calls `ns-parse-geometry'. */)
5390#define DEFAULT_COLS 80 5450#define DEFAULT_COLS 80
5391 5451
5392long 5452long
5393gui_figure_window_size (struct frame *f, Lisp_Object parms, bool toolbar_p, 5453gui_figure_window_size (struct frame *f, Lisp_Object parms, bool tabbar_p,
5394 int *x_width, int *x_height) 5454 bool toolbar_p, int *x_width, int *x_height)
5395{ 5455{
5396 Lisp_Object height, width, user_size, top, left, user_position; 5456 Lisp_Object height, width, user_size, top, left, user_position;
5397 long window_prompting = 0; 5457 long window_prompting = 0;
@@ -5411,6 +5471,36 @@ gui_figure_window_size (struct frame *f, Lisp_Object parms, bool toolbar_p,
5411 f->top_pos = 0; 5471 f->top_pos = 0;
5412 f->left_pos = 0; 5472 f->left_pos = 0;
5413 5473
5474 /* Calculate a tab bar height so that the user gets a text display
5475 area of the size he specified with -g or via .Xdefaults. Later
5476 changes of the tab bar height don't change the frame size. This
5477 is done so that users can create tall Emacs frames without having
5478 to guess how tall the tab bar will get. */
5479 if (tabbar_p && FRAME_TAB_BAR_LINES (f))
5480 {
5481 if (frame_default_tab_bar_height)
5482 FRAME_TAB_BAR_HEIGHT (f) = frame_default_tab_bar_height;
5483 else
5484 {
5485 int margin, relief;
5486
5487 relief = (tab_bar_button_relief < 0
5488 ? DEFAULT_TAB_BAR_BUTTON_RELIEF
5489 : min (tab_bar_button_relief, 1000000));
5490
5491 if (RANGED_FIXNUMP (1, Vtab_bar_button_margin, INT_MAX))
5492 margin = XFIXNAT (Vtab_bar_button_margin);
5493 else if (CONSP (Vtab_bar_button_margin)
5494 && RANGED_FIXNUMP (1, XCDR (Vtab_bar_button_margin), INT_MAX))
5495 margin = XFIXNAT (XCDR (Vtab_bar_button_margin));
5496 else
5497 margin = 0;
5498
5499 FRAME_TAB_BAR_HEIGHT (f)
5500 = DEFAULT_TAB_BAR_IMAGE_HEIGHT + 2 * margin + 2 * relief;
5501 }
5502 }
5503
5414 /* Calculate a tool bar height so that the user gets a text display 5504 /* Calculate a tool bar height so that the user gets a text display
5415 area of the size he specified with -g or via .Xdefaults. Later 5505 area of the size he specified with -g or via .Xdefaults. Later
5416 changes of the tool bar height don't change the frame size. This 5506 changes of the tool bar height don't change the frame size. This
@@ -5837,6 +5927,7 @@ syms_of_frame (void)
5837 DEFSYM (Qtitle_bar_size, "title-bar-size"); 5927 DEFSYM (Qtitle_bar_size, "title-bar-size");
5838 DEFSYM (Qmenu_bar_external, "menu-bar-external"); 5928 DEFSYM (Qmenu_bar_external, "menu-bar-external");
5839 DEFSYM (Qmenu_bar_size, "menu-bar-size"); 5929 DEFSYM (Qmenu_bar_size, "menu-bar-size");
5930 DEFSYM (Qtab_bar_size, "tab-bar-size");
5840 DEFSYM (Qtool_bar_external, "tool-bar-external"); 5931 DEFSYM (Qtool_bar_external, "tool-bar-external");
5841 DEFSYM (Qtool_bar_size, "tool-bar-size"); 5932 DEFSYM (Qtool_bar_size, "tool-bar-size");
5842 /* The following are used for frame_size_history. */ 5933 /* The following are used for frame_size_history. */
@@ -5860,7 +5951,9 @@ syms_of_frame (void)
5860 DEFSYM (Qx_net_wm_state, "x-net-wm-state"); 5951 DEFSYM (Qx_net_wm_state, "x-net-wm-state");
5861 DEFSYM (Qx_handle_net_wm_state, "x-handle-net-wm-state"); 5952 DEFSYM (Qx_handle_net_wm_state, "x-handle-net-wm-state");
5862 DEFSYM (Qtb_size_cb, "tb-size-cb"); 5953 DEFSYM (Qtb_size_cb, "tb-size-cb");
5954 DEFSYM (Qupdate_frame_tab_bar, "update-frame-tab-bar");
5863 DEFSYM (Qupdate_frame_tool_bar, "update-frame-tool-bar"); 5955 DEFSYM (Qupdate_frame_tool_bar, "update-frame-tool-bar");
5956 DEFSYM (Qfree_frame_tab_bar, "free-frame-tab-bar");
5864 DEFSYM (Qfree_frame_tool_bar, "free-frame-tool-bar"); 5957 DEFSYM (Qfree_frame_tool_bar, "free-frame-tool-bar");
5865 DEFSYM (Qx_set_menu_bar_lines, "x-set-menu-bar-lines"); 5958 DEFSYM (Qx_set_menu_bar_lines, "x-set-menu-bar-lines");
5866 DEFSYM (Qchange_frame_size, "change-frame-size"); 5959 DEFSYM (Qchange_frame_size, "change-frame-size");
@@ -5910,6 +6003,7 @@ syms_of_frame (void)
5910 DEFSYM (Qscroll_bar_width, "scroll-bar-width"); 6003 DEFSYM (Qscroll_bar_width, "scroll-bar-width");
5911 DEFSYM (Qsticky, "sticky"); 6004 DEFSYM (Qsticky, "sticky");
5912 DEFSYM (Qtitle, "title"); 6005 DEFSYM (Qtitle, "title");
6006 DEFSYM (Qtab_bar_lines, "tab-bar-lines");
5913 DEFSYM (Qtool_bar_lines, "tool-bar-lines"); 6007 DEFSYM (Qtool_bar_lines, "tool-bar-lines");
5914 DEFSYM (Qtool_bar_position, "tool-bar-position"); 6008 DEFSYM (Qtool_bar_position, "tool-bar-position");
5915 DEFSYM (Qunsplittable, "unsplittable"); 6009 DEFSYM (Qunsplittable, "unsplittable");
@@ -6071,6 +6165,14 @@ either customize it (see the info node `Easy Customization')
6071or call the function `menu-bar-mode'. */); 6165or call the function `menu-bar-mode'. */);
6072 Vmenu_bar_mode = Qt; 6166 Vmenu_bar_mode = Qt;
6073 6167
6168 DEFVAR_LISP ("tab-bar-mode", Vtab_bar_mode,
6169 doc: /* Non-nil if Tab-Bar mode is enabled.
6170See the command `tab-bar-mode' for a description of this minor mode.
6171Setting this variable directly does not take effect;
6172either customize it (see the info node `Easy Customization')
6173or call the function `tab-bar-mode'. */);
6174 Vtab_bar_mode = Qnil;
6175
6074 DEFVAR_LISP ("tool-bar-mode", Vtool_bar_mode, 6176 DEFVAR_LISP ("tool-bar-mode", Vtool_bar_mode,
6075 doc: /* Non-nil if Tool-Bar mode is enabled. 6177 doc: /* Non-nil if Tool-Bar mode is enabled.
6076See the command `tool-bar-mode' for a description of this minor mode. 6178See 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
6200even if this option is non-nil. */); 6302even if this option is non-nil. */);
6201#if defined (HAVE_WINDOW_SYSTEM) 6303#if defined (HAVE_WINDOW_SYSTEM)
6202#if defined (USE_LUCID) || defined (USE_MOTIF) || defined (HAVE_NTGUI) 6304#if defined (USE_LUCID) || defined (USE_MOTIF) || defined (HAVE_NTGUI)
6203 frame_inhibit_implied_resize = list1 (Qtool_bar_lines); 6305 frame_inhibit_implied_resize = list2 (Qtab_bar_lines, Qtool_bar_lines);
6204#else 6306#else
6205 frame_inhibit_implied_resize = Qnil; 6307 frame_inhibit_implied_resize = Qnil;
6206#endif 6308#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
181 Lisp_Object menu_bar_window; 181 Lisp_Object menu_bar_window;
182#endif 182#endif
183 183
184#if defined (HAVE_WINDOW_SYSTEM)
185 /* A window used to display the tab-bar of a frame. */
186 Lisp_Object tab_bar_window;
187
188 /* Desired and current contents displayed in that window. */
189 Lisp_Object desired_tab_bar_string;
190 Lisp_Object current_tab_bar_string;
191#endif
192
184#if defined (HAVE_WINDOW_SYSTEM) && ! defined (HAVE_EXT_TOOL_BAR) 193#if defined (HAVE_WINDOW_SYSTEM) && ! defined (HAVE_EXT_TOOL_BAR)
185 /* A window used to display the tool-bar of a frame. */ 194 /* A window used to display the tool-bar of a frame. */
186 Lisp_Object tool_bar_window; 195 Lisp_Object tool_bar_window;
@@ -201,6 +210,9 @@ struct frame
201 Lisp_Object font_data; 210 Lisp_Object font_data;
202#endif 211#endif
203 212
213 /* Desired and current tab-bar items. */
214 Lisp_Object tab_bar_items;
215
204 /* Desired and current tool-bar items. */ 216 /* Desired and current tool-bar items. */
205 Lisp_Object tool_bar_items; 217 Lisp_Object tool_bar_items;
206 /* tool_bar_items should be the last Lisp_Object member. */ 218 /* tool_bar_items should be the last Lisp_Object member. */
@@ -208,6 +220,11 @@ struct frame
208 /* Cache of realized faces. */ 220 /* Cache of realized faces. */
209 struct face_cache *face_cache; 221 struct face_cache *face_cache;
210 222
223#if defined (HAVE_WINDOW_SYSTEM)
224 /* Tab-bar item index of the item on which a mouse button was pressed. */
225 int last_tab_bar_item;
226#endif
227
211#if defined (HAVE_WINDOW_SYSTEM) && ! defined (HAVE_EXT_TOOL_BAR) 228#if defined (HAVE_WINDOW_SYSTEM) && ! defined (HAVE_EXT_TOOL_BAR)
212 /* Tool-bar item index of the item on which a mouse button was pressed. */ 229 /* Tool-bar item index of the item on which a mouse button was pressed. */
213 int last_tool_bar_item; 230 int last_tool_bar_item;
@@ -256,6 +273,12 @@ struct frame
256 /* Set to true when current redisplay has updated frame. */ 273 /* Set to true when current redisplay has updated frame. */
257 bool_bf updated_p : 1; 274 bool_bf updated_p : 1;
258 275
276#if defined (HAVE_WINDOW_SYSTEM)
277 /* Set to true to minimize tab-bar height even when
278 auto-resize-tab-bar is set to grow-only. */
279 bool_bf minimize_tab_bar_window_p : 1;
280#endif
281
259#if defined (HAVE_WINDOW_SYSTEM) && ! defined (HAVE_EXT_TOOL_BAR) 282#if defined (HAVE_WINDOW_SYSTEM) && ! defined (HAVE_EXT_TOOL_BAR)
260 /* Set to true to minimize tool-bar height even when 283 /* Set to true to minimize tool-bar height even when
261 auto-resize-tool-bar is set to grow-only. */ 284 auto-resize-tool-bar is set to grow-only. */
@@ -404,6 +427,10 @@ struct frame
404 /* Set to true after this frame was made by `make-frame'. */ 427 /* Set to true after this frame was made by `make-frame'. */
405 bool_bf after_make_frame : 1; 428 bool_bf after_make_frame : 1;
406 429
430 /* Whether the tab bar height change should be taken into account. */
431 bool_bf tab_bar_redisplayed : 1;
432 bool_bf tab_bar_resized : 1;
433
407 /* Whether the tool bar height change should be taken into account. */ 434 /* Whether the tool bar height change should be taken into account. */
408 bool_bf tool_bar_redisplayed : 1; 435 bool_bf tool_bar_redisplayed : 1;
409 bool_bf tool_bar_resized : 1; 436 bool_bf tool_bar_resized : 1;
@@ -435,6 +462,15 @@ struct frame
435 last time run_window_change_functions was called on it. */ 462 last time run_window_change_functions was called on it. */
436 ptrdiff_t number_of_windows; 463 ptrdiff_t number_of_windows;
437 464
465 /* Number of lines (rounded up) of tab bar. REMOVE THIS */
466 int tab_bar_lines;
467
468 /* Height of frame internal tab bar in pixels. */
469 int tab_bar_height;
470
471 int n_tab_bar_rows;
472 int n_tab_bar_items;
473
438 /* Number of lines (rounded up) of tool bar. REMOVE THIS */ 474 /* Number of lines (rounded up) of tool bar. REMOVE THIS */
439 int tool_bar_lines; 475 int tool_bar_lines;
440 476
@@ -701,6 +737,28 @@ fset_title (struct frame *f, Lisp_Object val)
701 f->title = val; 737 f->title = val;
702} 738}
703INLINE void 739INLINE void
740fset_tab_bar_items (struct frame *f, Lisp_Object val)
741{
742 f->tab_bar_items = val;
743}
744#if defined (HAVE_WINDOW_SYSTEM)
745INLINE void
746fset_tab_bar_window (struct frame *f, Lisp_Object val)
747{
748 f->tab_bar_window = val;
749}
750INLINE void
751fset_current_tab_bar_string (struct frame *f, Lisp_Object val)
752{
753 f->current_tab_bar_string = val;
754}
755INLINE void
756fset_desired_tab_bar_string (struct frame *f, Lisp_Object val)
757{
758 f->desired_tab_bar_string = val;
759}
760#endif /* HAVE_WINDOW_SYSTEM */
761INLINE void
704fset_tool_bar_items (struct frame *f, Lisp_Object val) 762fset_tool_bar_items (struct frame *f, Lisp_Object val)
705{ 763{
706 f->tool_bar_items = val; 764 f->tool_bar_items = val;
@@ -878,6 +936,12 @@ default_pixels_per_inch_y (void)
878/* Pixel height of frame F's menu bar. */ 936/* Pixel height of frame F's menu bar. */
879#define FRAME_MENU_BAR_HEIGHT(f) (f)->menu_bar_height 937#define FRAME_MENU_BAR_HEIGHT(f) (f)->menu_bar_height
880 938
939/* Number of lines of frame F used for the tab-bar. */
940#define FRAME_TAB_BAR_LINES(f) (f)->tab_bar_lines
941
942/* Pixel height of frame F's tab-bar. */
943#define FRAME_TAB_BAR_HEIGHT(f) (f)->tab_bar_height
944
881/* True if this frame should display a tool bar 945/* True if this frame should display a tool bar
882 in a way that does not use any text lines. */ 946 in a way that does not use any text lines. */
883#ifdef HAVE_EXT_TOOL_BAR 947#ifdef HAVE_EXT_TOOL_BAR
@@ -901,11 +965,11 @@ default_pixels_per_inch_y (void)
901 965
902/* Lines above the top-most window in frame F. */ 966/* Lines above the top-most window in frame F. */
903#define FRAME_TOP_MARGIN(F) \ 967#define FRAME_TOP_MARGIN(F) \
904 (FRAME_MENU_BAR_LINES (F) + FRAME_TOOL_BAR_LINES (F)) 968 (FRAME_MENU_BAR_LINES (F) + FRAME_TAB_BAR_LINES (F) + FRAME_TOOL_BAR_LINES (F))
905 969
906/* Pixel height of frame F's top margin. */ 970/* Pixel height of frame F's top margin. */
907#define FRAME_TOP_MARGIN_HEIGHT(F) \ 971#define FRAME_TOP_MARGIN_HEIGHT(F) \
908 (FRAME_MENU_BAR_HEIGHT (F) + FRAME_TOOL_BAR_HEIGHT (F)) 972 (FRAME_MENU_BAR_HEIGHT (F) + FRAME_TAB_BAR_HEIGHT (F) + FRAME_TOOL_BAR_HEIGHT (F))
909 973
910/* True if this frame should display a menu bar 974/* True if this frame should display a menu bar
911 in a way that does not use any text lines. */ 975 in a way that does not use any text lines. */
@@ -1255,6 +1319,8 @@ SET_FRAME_VISIBLE (struct frame *f, int v)
1255extern Lisp_Object selected_frame; 1319extern Lisp_Object selected_frame;
1256extern Lisp_Object old_selected_frame; 1320extern Lisp_Object old_selected_frame;
1257 1321
1322extern int frame_default_tab_bar_height;
1323
1258#ifndef HAVE_EXT_TOOL_BAR 1324#ifndef HAVE_EXT_TOOL_BAR
1259extern int frame_default_tool_bar_height; 1325extern int frame_default_tool_bar_height;
1260#endif 1326#endif
@@ -1566,7 +1632,7 @@ extern void gui_set_horizontal_scroll_bars (struct frame *, Lisp_Object, Lisp_Ob
1566extern void gui_set_scroll_bar_width (struct frame *, Lisp_Object, Lisp_Object); 1632extern void gui_set_scroll_bar_width (struct frame *, Lisp_Object, Lisp_Object);
1567extern void gui_set_scroll_bar_height (struct frame *, Lisp_Object, Lisp_Object); 1633extern void gui_set_scroll_bar_height (struct frame *, Lisp_Object, Lisp_Object);
1568 1634
1569extern long gui_figure_window_size (struct frame *, Lisp_Object, bool, int *, int *); 1635extern long gui_figure_window_size (struct frame *, Lisp_Object, bool, bool, int *, int *);
1570 1636
1571extern void gui_set_alpha (struct frame *, Lisp_Object, Lisp_Object); 1637extern void gui_set_alpha (struct frame *, Lisp_Object, Lisp_Object);
1572extern void gui_set_no_special_glyphs (struct frame *, Lisp_Object, Lisp_Object); 1638extern 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
634 /* Clear left fringe if no bitmap to draw or if bitmap doesn't fill 634 /* Clear left fringe if no bitmap to draw or if bitmap doesn't fill
635 the fringe. */ 635 the fringe. */
636 p.bx = -1; 636 p.bx = -1;
637 header_line_height = WINDOW_HEADER_LINE_HEIGHT (w); 637 header_line_height = WINDOW_TAB_LINE_HEIGHT (w)
638 + WINDOW_HEADER_LINE_HEIGHT (w);
638 p.by = WINDOW_TO_FRAME_PIXEL_Y (w, max (header_line_height, row->y)); 639 p.by = WINDOW_TO_FRAME_PIXEL_Y (w, max (header_line_height, row->y));
639 p.ny = row->visible_height; 640 p.ny = row->visible_height;
640 if (left_p) 641 if (left_p)
@@ -1091,7 +1092,8 @@ update_window_fringes (struct window *w, bool keep_current_p)
1091 struct glyph_row *row1; 1092 struct glyph_row *row1;
1092 int top_ind_max_y; 1093 int top_ind_max_y;
1093 1094
1094 top_ind_min_y = WINDOW_HEADER_LINE_HEIGHT (w); 1095 top_ind_min_y = WINDOW_TAB_LINE_HEIGHT (w)
1096 + WINDOW_HEADER_LINE_HEIGHT (w);
1095 top_ind_max_y = top_ind_min_y + fb->height; 1097 top_ind_max_y = top_ind_min_y + fb->height;
1096 if (top_ind_max_y > yb) 1098 if (top_ind_max_y > yb)
1097 top_ind_max_y = yb; 1099 top_ind_max_y = yb;
@@ -1148,8 +1150,10 @@ update_window_fringes (struct window *w, bool keep_current_p)
1148 1150
1149 bot_ind_max_y = row->y + row->visible_height; 1151 bot_ind_max_y = row->y + row->visible_height;
1150 bot_ind_min_y = bot_ind_max_y - fb->height; 1152 bot_ind_min_y = bot_ind_max_y - fb->height;
1151 if (bot_ind_min_y < WINDOW_HEADER_LINE_HEIGHT (w)) 1153 if (bot_ind_min_y < WINDOW_TAB_LINE_HEIGHT (w)
1152 bot_ind_min_y = WINDOW_HEADER_LINE_HEIGHT (w); 1154 + WINDOW_HEADER_LINE_HEIGHT (w))
1155 bot_ind_min_y = WINDOW_TAB_LINE_HEIGHT (w)
1156 + WINDOW_HEADER_LINE_HEIGHT (w);
1153 1157
1154 for (y = row->y, rn = bot_ind_rn - 1; 1158 for (y = row->y, rn = bot_ind_rn - 1;
1155 y >= bot_ind_min_y && rn >= 0; 1159 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,
2385 if (used_mouse_menu 2385 if (used_mouse_menu
2386 /* Also check was_disabled so last-nonmenu-event won't return 2386 /* Also check was_disabled so last-nonmenu-event won't return
2387 a bad value when submenus are involved. (Bug#447) */ 2387 a bad value when submenus are involved. (Bug#447) */
2388 && (EQ (c, Qtool_bar) || EQ (c, Qmenu_bar) || was_disabled)) 2388 && (EQ (c, Qtool_bar) || EQ (c, Qtab_bar) || EQ (c, Qmenu_bar)
2389 || was_disabled))
2389 *used_mouse_menu = true; 2390 *used_mouse_menu = true;
2390 2391
2391 goto reread_for_input_method; 2392 goto reread_for_input_method;
@@ -2666,6 +2667,7 @@ read_char (int commandflag, Lisp_Object map,
2666 && !NILP (prev_event) 2667 && !NILP (prev_event)
2667 && EVENT_HAS_PARAMETERS (prev_event) 2668 && EVENT_HAS_PARAMETERS (prev_event)
2668 && !EQ (XCAR (prev_event), Qmenu_bar) 2669 && !EQ (XCAR (prev_event), Qmenu_bar)
2670 && !EQ (XCAR (prev_event), Qtab_bar)
2669 && !EQ (XCAR (prev_event), Qtool_bar) 2671 && !EQ (XCAR (prev_event), Qtool_bar)
2670 /* Don't bring up a menu if we already have another event. */ 2672 /* Don't bring up a menu if we already have another event. */
2671 && !CONSP (Vunread_command_events)) 2673 && !CONSP (Vunread_command_events))
@@ -2930,7 +2932,7 @@ read_char (int commandflag, Lisp_Object map,
2930 posn = POSN_POSN (xevent_start (c)); 2932 posn = POSN_POSN (xevent_start (c));
2931 /* Handle menu-bar events: 2933 /* Handle menu-bar events:
2932 insert the dummy prefix event `menu-bar'. */ 2934 insert the dummy prefix event `menu-bar'. */
2933 if (EQ (posn, Qmenu_bar) || EQ (posn, Qtool_bar)) 2935 if (EQ (posn, Qmenu_bar) || EQ (posn, Qtab_bar) || EQ (posn, Qtool_bar))
2934 { 2936 {
2935 /* Change menu-bar to (menu-bar) as the event "position". */ 2937 /* Change menu-bar to (menu-bar) as the event "position". */
2936 POSN_SET_POSN (xevent_start (c), list1 (posn)); 2938 POSN_SET_POSN (xevent_start (c), list1 (posn));
@@ -3974,6 +3976,7 @@ kbd_buffer_get_event (KBOARD **kbp,
3974 if (used_mouse_menu 3976 if (used_mouse_menu
3975 && !EQ (event->ie.frame_or_window, event->ie.arg) 3977 && !EQ (event->ie.frame_or_window, event->ie.arg)
3976 && (event->kind == MENU_BAR_EVENT 3978 && (event->kind == MENU_BAR_EVENT
3979 || event->kind == TAB_BAR_EVENT
3977 || event->kind == TOOL_BAR_EVENT)) 3980 || event->kind == TOOL_BAR_EVENT))
3978 *used_mouse_menu = true; 3981 *used_mouse_menu = true;
3979#endif 3982#endif
@@ -5012,7 +5015,7 @@ make_lispy_position (struct frame *f, Lisp_Object x, Lisp_Object y,
5012 int xret = 0, yret = 0; 5015 int xret = 0, yret = 0;
5013 /* The window or frame under frame pixel coordinates (x,y) */ 5016 /* The window or frame under frame pixel coordinates (x,y) */
5014 Lisp_Object window_or_frame = f 5017 Lisp_Object window_or_frame = f
5015 ? window_from_coordinates (f, XFIXNUM (x), XFIXNUM (y), &part, 0) 5018 ? window_from_coordinates (f, XFIXNUM (x), XFIXNUM (y), &part, 0, 0)
5016 : Qnil; 5019 : Qnil;
5017 5020
5018 if (WINDOWP (window_or_frame)) 5021 if (WINDOWP (window_or_frame))
@@ -5036,17 +5039,21 @@ make_lispy_position (struct frame *f, Lisp_Object x, Lisp_Object y,
5036 if (part == ON_TEXT) 5039 if (part == ON_TEXT)
5037 { 5040 {
5038 xret = XFIXNUM (x) - window_box_left (w, TEXT_AREA); 5041 xret = XFIXNUM (x) - window_box_left (w, TEXT_AREA);
5039 yret = wy - WINDOW_HEADER_LINE_HEIGHT (w); 5042 yret = wy - WINDOW_TAB_LINE_HEIGHT (w) - WINDOW_HEADER_LINE_HEIGHT (w);
5040 } 5043 }
5041 /* For mode line and header line clicks, return X, Y relative to 5044 /* For mode line and header line clicks, return X, Y relative to
5042 the left window edge. Use mode_line_string to look for a 5045 the left window edge. Use mode_line_string to look for a
5043 string on the click position. */ 5046 string on the click position. */
5044 else if (part == ON_MODE_LINE || part == ON_HEADER_LINE) 5047 else if (part == ON_MODE_LINE || part == ON_TAB_LINE
5048 || part == ON_HEADER_LINE)
5045 { 5049 {
5046 Lisp_Object string; 5050 Lisp_Object string;
5047 ptrdiff_t charpos; 5051 ptrdiff_t charpos;
5048 5052
5049 posn = (part == ON_MODE_LINE) ? Qmode_line : Qheader_line; 5053 posn = (part == ON_MODE_LINE ? Qmode_line
5054 : (part == ON_TAB_LINE ? Qtab_line
5055 : Qheader_line));
5056
5050 /* Note that mode_line_string takes COL, ROW as pixels and 5057 /* Note that mode_line_string takes COL, ROW as pixels and
5051 converts them to characters. */ 5058 converts them to characters. */
5052 col = wx; 5059 col = wx;
@@ -5075,7 +5082,7 @@ make_lispy_position (struct frame *f, Lisp_Object x, Lisp_Object y,
5075 if (STRINGP (string)) 5082 if (STRINGP (string))
5076 string_info = Fcons (string, make_fixnum (charpos)); 5083 string_info = Fcons (string, make_fixnum (charpos));
5077 xret = wx; 5084 xret = wx;
5078 yret = wy - WINDOW_HEADER_LINE_HEIGHT (w); 5085 yret = wy - WINDOW_TAB_LINE_HEIGHT (w) - WINDOW_HEADER_LINE_HEIGHT (w);
5079 } 5086 }
5080 else if (part == ON_LEFT_FRINGE) 5087 else if (part == ON_LEFT_FRINGE)
5081 { 5088 {
@@ -5085,7 +5092,7 @@ make_lispy_position (struct frame *f, Lisp_Object x, Lisp_Object y,
5085 dx = wx 5092 dx = wx
5086 - (WINDOW_HAS_FRINGES_OUTSIDE_MARGINS (w) 5093 - (WINDOW_HAS_FRINGES_OUTSIDE_MARGINS (w)
5087 ? 0 : window_box_width (w, LEFT_MARGIN_AREA)); 5094 ? 0 : window_box_width (w, LEFT_MARGIN_AREA));
5088 dy = yret = wy - WINDOW_HEADER_LINE_HEIGHT (w); 5095 dy = yret = wy - WINDOW_TAB_LINE_HEIGHT (w) - WINDOW_HEADER_LINE_HEIGHT (w);
5089 } 5096 }
5090 else if (part == ON_RIGHT_FRINGE) 5097 else if (part == ON_RIGHT_FRINGE)
5091 { 5098 {
@@ -5098,7 +5105,7 @@ make_lispy_position (struct frame *f, Lisp_Object x, Lisp_Object y,
5098 - (WINDOW_HAS_FRINGES_OUTSIDE_MARGINS (w) 5105 - (WINDOW_HAS_FRINGES_OUTSIDE_MARGINS (w)
5099 ? window_box_width (w, RIGHT_MARGIN_AREA) 5106 ? window_box_width (w, RIGHT_MARGIN_AREA)
5100 : 0); 5107 : 0);
5101 dy = yret = wy - WINDOW_HEADER_LINE_HEIGHT (w); 5108 dy = yret = wy - WINDOW_TAB_LINE_HEIGHT (w) - WINDOW_HEADER_LINE_HEIGHT (w);
5102 } 5109 }
5103 else if (part == ON_VERTICAL_BORDER) 5110 else if (part == ON_VERTICAL_BORDER)
5104 { 5111 {
@@ -5953,6 +5960,16 @@ make_lispy_event (struct input_event *event)
5953 /* Make an event (select-window (WINDOW)). */ 5960 /* Make an event (select-window (WINDOW)). */
5954 return list2 (Qselect_window, list1 (event->frame_or_window)); 5961 return list2 (Qselect_window, list1 (event->frame_or_window));
5955 5962
5963 case TAB_BAR_EVENT:
5964 if (EQ (event->arg, event->frame_or_window))
5965 /* This is the prefix key. We translate this to
5966 `(tab_bar)' because the code in keyboard.c for tab bar
5967 events, which we use, relies on this. */
5968 return list1 (Qtab_bar);
5969 else if (SYMBOLP (event->arg))
5970 return apply_modifiers (event->modifiers, event->arg);
5971 return event->arg;
5972
5956 case TOOL_BAR_EVENT: 5973 case TOOL_BAR_EVENT:
5957 if (EQ (event->arg, event->frame_or_window)) 5974 if (EQ (event->arg, event->frame_or_window))
5958 /* This is the prefix key. We translate this to 5975 /* This is the prefix key. We translate this to
@@ -6730,6 +6747,7 @@ lucid_event_type_list_p (Lisp_Object object)
6730 if (EQ (XCAR (object), Qhelp_echo) 6747 if (EQ (XCAR (object), Qhelp_echo)
6731 || EQ (XCAR (object), Qvertical_line) 6748 || EQ (XCAR (object), Qvertical_line)
6732 || EQ (XCAR (object), Qmode_line) 6749 || EQ (XCAR (object), Qmode_line)
6750 || EQ (XCAR (object), Qtab_line)
6733 || EQ (XCAR (object), Qheader_line)) 6751 || EQ (XCAR (object), Qheader_line))
6734 return 0; 6752 return 0;
6735 6753
@@ -7891,6 +7909,389 @@ parse_menu_item (Lisp_Object item, int inmenubar)
7891 7909
7892 7910
7893/*********************************************************************** 7911/***********************************************************************
7912 Tab-bars
7913 ***********************************************************************/
7914
7915/* A vector holding tab bar items while they are parsed in function
7916 tab_bar_items. Each item occupies TAB_BAR_ITEM_NSCLOTS elements
7917 in the vector. */
7918
7919static Lisp_Object tab_bar_items_vector;
7920
7921/* A vector holding the result of parse_tab_bar_item. Layout is like
7922 the one for a single item in tab_bar_items_vector. */
7923
7924static Lisp_Object tab_bar_item_properties;
7925
7926/* Next free index in tab_bar_items_vector. */
7927
7928static int ntab_bar_items;
7929
7930/* Function prototypes. */
7931
7932static void init_tab_bar_items (Lisp_Object);
7933static void process_tab_bar_item (Lisp_Object, Lisp_Object, Lisp_Object,
7934 void *);
7935static bool parse_tab_bar_item (Lisp_Object, Lisp_Object);
7936static void append_tab_bar_item (void);
7937
7938
7939/* Return a vector of tab bar items for keymaps currently in effect.
7940 Reuse vector REUSE if non-nil. Return in *NITEMS the number of
7941 tab bar items found. */
7942
7943Lisp_Object
7944tab_bar_items (Lisp_Object reuse, int *nitems)
7945{
7946 Lisp_Object *maps;
7947 Lisp_Object mapsbuf[3];
7948 ptrdiff_t nmaps, i;
7949 Lisp_Object oquit;
7950 Lisp_Object *tmaps;
7951 USE_SAFE_ALLOCA;
7952
7953 *nitems = 0;
7954
7955 /* In order to build the menus, we need to call the keymap
7956 accessors. They all call maybe_quit. But this function is called
7957 during redisplay, during which a quit is fatal. So inhibit
7958 quitting while building the menus. We do this instead of
7959 specbind because (1) errors will clear it anyway and (2) this
7960 avoids risk of specpdl overflow. */
7961 oquit = Vinhibit_quit;
7962 Vinhibit_quit = Qt;
7963
7964 /* Initialize tab_bar_items_vector and protect it from GC. */
7965 init_tab_bar_items (reuse);
7966
7967 /* Build list of keymaps in maps. Set nmaps to the number of maps
7968 to process. */
7969
7970 /* Should overriding-terminal-local-map and overriding-local-map apply? */
7971 if (!NILP (Voverriding_local_map_menu_flag)
7972 && !NILP (Voverriding_local_map))
7973 {
7974 /* Yes, use them (if non-nil) as well as the global map. */
7975 maps = mapsbuf;
7976 nmaps = 0;
7977 if (!NILP (KVAR (current_kboard, Voverriding_terminal_local_map)))
7978 maps[nmaps++] = KVAR (current_kboard, Voverriding_terminal_local_map);
7979 if (!NILP (Voverriding_local_map))
7980 maps[nmaps++] = Voverriding_local_map;
7981 }
7982 else
7983 {
7984 /* No, so use major and minor mode keymaps and keymap property.
7985 Note that tab-bar bindings in the local-map and keymap
7986 properties may not work reliably, as they are only
7987 recognized when the tab-bar (or mode-line) is updated,
7988 which does not normally happen after every command. */
7989 ptrdiff_t nminor = current_minor_maps (NULL, &tmaps);
7990 SAFE_NALLOCA (maps, 1, nminor + 4);
7991 nmaps = 0;
7992 Lisp_Object tem = KVAR (current_kboard, Voverriding_terminal_local_map);
7993 if (!NILP (tem) && !NILP (Voverriding_local_map_menu_flag))
7994 maps[nmaps++] = tem;
7995 if (tem = get_local_map (PT, current_buffer, Qkeymap), !NILP (tem))
7996 maps[nmaps++] = tem;
7997 if (nminor != 0)
7998 {
7999 memcpy (maps + nmaps, tmaps, nminor * sizeof (maps[0]));
8000 nmaps += nminor;
8001 }
8002 maps[nmaps++] = get_local_map (PT, current_buffer, Qlocal_map);
8003 }
8004
8005 /* Add global keymap at the end. */
8006 maps[nmaps++] = current_global_map;
8007
8008 /* Process maps in reverse order and look up in each map the prefix
8009 key `tab-bar'. */
8010 for (i = nmaps - 1; i >= 0; --i)
8011 if (!NILP (maps[i]))
8012 {
8013 Lisp_Object keymap;
8014
8015 keymap = get_keymap (access_keymap (maps[i], Qtab_bar, 1, 0, 1), 0, 1);
8016 if (CONSP (keymap))
8017 map_keymap (keymap, process_tab_bar_item, Qnil, NULL, 1);
8018 }
8019
8020 Vinhibit_quit = oquit;
8021 *nitems = ntab_bar_items / TAB_BAR_ITEM_NSLOTS;
8022 SAFE_FREE ();
8023 return tab_bar_items_vector;
8024}
8025
8026
8027/* Process the definition of KEY which is DEF. */
8028
8029static void
8030process_tab_bar_item (Lisp_Object key, Lisp_Object def, Lisp_Object data, void *args)
8031{
8032 int i;
8033
8034 if (EQ (def, Qundefined))
8035 {
8036 /* If a map has an explicit `undefined' as definition,
8037 discard any previously made item. */
8038 for (i = 0; i < ntab_bar_items; i += TAB_BAR_ITEM_NSLOTS)
8039 {
8040 Lisp_Object *v = XVECTOR (tab_bar_items_vector)->contents + i;
8041
8042 if (EQ (key, v[TAB_BAR_ITEM_KEY]))
8043 {
8044 if (ntab_bar_items > i + TAB_BAR_ITEM_NSLOTS)
8045 memmove (v, v + TAB_BAR_ITEM_NSLOTS,
8046 ((ntab_bar_items - i - TAB_BAR_ITEM_NSLOTS)
8047 * word_size));
8048 ntab_bar_items -= TAB_BAR_ITEM_NSLOTS;
8049 break;
8050 }
8051 }
8052 }
8053 else if (parse_tab_bar_item (key, def))
8054 /* Append a new tab bar item to tab_bar_items_vector. Accept
8055 more than one definition for the same key. */
8056 append_tab_bar_item ();
8057}
8058
8059/* Access slot with index IDX of vector tab_bar_item_properties. */
8060#define PROP(IDX) AREF (tab_bar_item_properties, (IDX))
8061static void
8062set_prop_tab_bar (ptrdiff_t idx, Lisp_Object val)
8063{
8064 ASET (tab_bar_item_properties, idx, val);
8065}
8066
8067
8068/* Parse a tab bar item specification ITEM for key KEY and return the
8069 result in tab_bar_item_properties. Value is false if ITEM is
8070 invalid.
8071
8072 ITEM is a list `(menu-item CAPTION BINDING PROPS...)'.
8073
8074 CAPTION is the caption of the item, If it's not a string, it is
8075 evaluated to get a string.
8076
8077 BINDING is the tab bar item's binding. Tab-bar items with keymaps
8078 as binding are currently ignored.
8079
8080 The following properties are recognized:
8081
8082 - `:enable FORM'.
8083
8084 FORM is evaluated and specifies whether the tab bar item is
8085 enabled or disabled.
8086
8087 - `:visible FORM'
8088
8089 FORM is evaluated and specifies whether the tab bar item is visible.
8090
8091 - `:filter FUNCTION'
8092
8093 FUNCTION is invoked with one parameter `(quote BINDING)'. Its
8094 result is stored as the new binding.
8095
8096 - `:button (TYPE SELECTED)'
8097
8098 TYPE must be one of `:radio' or `:toggle'. SELECTED is evaluated
8099 and specifies whether the button is selected (pressed) or not.
8100
8101 - `:image IMAGES'
8102
8103 IMAGES is either a single image specification or a vector of four
8104 image specifications. See enum tab_bar_item_images.
8105
8106 - `:help HELP-STRING'.
8107
8108 Gives a help string to display for the tab bar item.
8109
8110 - `:label LABEL-STRING'.
8111
8112 A text label to show with the tab bar button if labels are enabled. */
8113
8114static bool
8115parse_tab_bar_item (Lisp_Object key, Lisp_Object item)
8116{
8117 Lisp_Object filter = Qnil;
8118 Lisp_Object caption;
8119 int i;
8120
8121 /* Definition looks like `(menu-item CAPTION BINDING PROPS...)'.
8122 Rule out items that aren't lists, don't start with
8123 `menu-item' or whose rest following `tab-bar-item' is not a
8124 list. */
8125 if (!CONSP (item))
8126 return 0;
8127
8128 /* As an exception, allow old-style menu separators. */
8129 if (STRINGP (XCAR (item)))
8130 item = list1 (XCAR (item));
8131 else if (!EQ (XCAR (item), Qmenu_item)
8132 || (item = XCDR (item), !CONSP (item)))
8133 return 0;
8134
8135 /* Create tab_bar_item_properties vector if necessary. Reset it to
8136 defaults. */
8137 if (VECTORP (tab_bar_item_properties))
8138 {
8139 for (i = 0; i < TAB_BAR_ITEM_NSLOTS; ++i)
8140 set_prop_tab_bar (i, Qnil);
8141 }
8142 else
8143 tab_bar_item_properties = make_nil_vector (TAB_BAR_ITEM_NSLOTS);
8144
8145 /* Set defaults. */
8146 set_prop_tab_bar (TAB_BAR_ITEM_KEY, key);
8147 set_prop_tab_bar (TAB_BAR_ITEM_ENABLED_P, Qt);
8148
8149 /* Get the caption of the item. If the caption is not a string,
8150 evaluate it to get a string. If we don't get a string, skip this
8151 item. */
8152 caption = XCAR (item);
8153 if (!STRINGP (caption))
8154 {
8155 caption = menu_item_eval_property (caption);
8156 if (!STRINGP (caption))
8157 return 0;
8158 }
8159 set_prop_tab_bar (TAB_BAR_ITEM_CAPTION, caption);
8160
8161 /* If the rest following the caption is not a list, the menu item is
8162 either a separator, or invalid. */
8163 item = XCDR (item);
8164 if (!CONSP (item))
8165 {
8166 if (menu_separator_name_p (SSDATA (caption)))
8167 {
8168 set_prop_tab_bar (TAB_BAR_ITEM_ENABLED_P, Qnil);
8169 set_prop_tab_bar (TAB_BAR_ITEM_SELECTED_P, Qnil);
8170 set_prop_tab_bar (TAB_BAR_ITEM_CAPTION, Qnil);
8171 return 1;
8172 }
8173 return 0;
8174 }
8175
8176 /* Store the binding. */
8177 set_prop_tab_bar (TAB_BAR_ITEM_BINDING, XCAR (item));
8178 item = XCDR (item);
8179
8180 /* Ignore cached key binding, if any. */
8181 if (CONSP (item) && CONSP (XCAR (item)))
8182 item = XCDR (item);
8183
8184 /* Process the rest of the properties. */
8185 for (; CONSP (item) && CONSP (XCDR (item)); item = XCDR (XCDR (item)))
8186 {
8187 Lisp_Object ikey, value;
8188
8189 ikey = XCAR (item);
8190 value = XCAR (XCDR (item));
8191
8192 if (EQ (ikey, QCenable))
8193 {
8194 /* `:enable FORM'. */
8195 if (!NILP (Venable_disabled_menus_and_buttons))
8196 set_prop_tab_bar (TAB_BAR_ITEM_ENABLED_P, Qt);
8197 else
8198 set_prop_tab_bar (TAB_BAR_ITEM_ENABLED_P, value);
8199 }
8200 else if (EQ (ikey, QCvisible))
8201 {
8202 /* `:visible FORM'. If got a visible property and that
8203 evaluates to nil then ignore this item. */
8204 if (NILP (menu_item_eval_property (value)))
8205 return 0;
8206 }
8207 else if (EQ (ikey, QChelp))
8208 /* `:help HELP-STRING'. */
8209 set_prop_tab_bar (TAB_BAR_ITEM_HELP, value);
8210 else if (EQ (ikey, QCfilter))
8211 /* ':filter FORM'. */
8212 filter = value;
8213 else if (EQ (ikey, QCbutton) && CONSP (value))
8214 {
8215 /* `:button (TYPE . SELECTED)'. */
8216 Lisp_Object type, selected;
8217
8218 type = XCAR (value);
8219 selected = XCDR (value);
8220 if (EQ (type, QCtoggle) || EQ (type, QCradio))
8221 {
8222 set_prop_tab_bar (TAB_BAR_ITEM_SELECTED_P, selected);
8223 }
8224 }
8225 }
8226
8227 /* If got a filter apply it on binding. */
8228 if (!NILP (filter))
8229 set_prop_tab_bar (TAB_BAR_ITEM_BINDING,
8230 (menu_item_eval_property
8231 (list2 (filter,
8232 list2 (Qquote,
8233 PROP (TAB_BAR_ITEM_BINDING))))));
8234
8235 /* See if the binding is a keymap. Give up if it is. */
8236 if (CONSP (get_keymap (PROP (TAB_BAR_ITEM_BINDING), 0, 1)))
8237 return 0;
8238
8239 /* Enable or disable selection of item. */
8240 if (!EQ (PROP (TAB_BAR_ITEM_ENABLED_P), Qt))
8241 set_prop_tab_bar (TAB_BAR_ITEM_ENABLED_P,
8242 menu_item_eval_property (PROP (TAB_BAR_ITEM_ENABLED_P)));
8243
8244 /* Handle radio buttons or toggle boxes. */
8245 if (!NILP (PROP (TAB_BAR_ITEM_SELECTED_P)))
8246 set_prop_tab_bar (TAB_BAR_ITEM_SELECTED_P,
8247 menu_item_eval_property (PROP (TAB_BAR_ITEM_SELECTED_P)));
8248
8249 return 1;
8250
8251#undef PROP
8252}
8253
8254
8255/* Initialize tab_bar_items_vector. REUSE, if non-nil, is a vector
8256 that can be reused. */
8257
8258static void
8259init_tab_bar_items (Lisp_Object reuse)
8260{
8261 if (VECTORP (reuse))
8262 tab_bar_items_vector = reuse;
8263 else
8264 tab_bar_items_vector = make_nil_vector (64);
8265 ntab_bar_items = 0;
8266}
8267
8268
8269/* Append parsed tab bar item properties from
8270 tab_bar_item_properties */
8271
8272static void
8273append_tab_bar_item (void)
8274{
8275 ptrdiff_t incr
8276 = (ntab_bar_items
8277 - (ASIZE (tab_bar_items_vector) - TAB_BAR_ITEM_NSLOTS));
8278
8279 /* Enlarge tab_bar_items_vector if necessary. */
8280 if (incr > 0)
8281 tab_bar_items_vector = larger_vector (tab_bar_items_vector, incr, -1);
8282
8283 /* Append entries from tab_bar_item_properties to the end of
8284 tab_bar_items_vector. */
8285 vcopy (tab_bar_items_vector, ntab_bar_items,
8286 XVECTOR (tab_bar_item_properties)->contents, TAB_BAR_ITEM_NSLOTS);
8287 ntab_bar_items += TAB_BAR_ITEM_NSLOTS;
8288}
8289
8290
8291
8292
8293
8294/***********************************************************************
7894 Tool-bars 8295 Tool-bars
7895 ***********************************************************************/ 8296 ***********************************************************************/
7896 8297
@@ -8402,6 +8803,7 @@ read_char_x_menu_prompt (Lisp_Object map,
8402 use a real menu for mouse selection. */ 8803 use a real menu for mouse selection. */
8403 if (EVENT_HAS_PARAMETERS (prev_event) 8804 if (EVENT_HAS_PARAMETERS (prev_event)
8404 && !EQ (XCAR (prev_event), Qmenu_bar) 8805 && !EQ (XCAR (prev_event), Qmenu_bar)
8806 && !EQ (XCAR (prev_event), Qtab_bar)
8405 && !EQ (XCAR (prev_event), Qtool_bar)) 8807 && !EQ (XCAR (prev_event), Qtool_bar))
8406 { 8808 {
8407 /* Display the menu and get the selection. */ 8809 /* Display the menu and get the selection. */
@@ -9377,7 +9779,7 @@ read_key_sequence (Lisp_Object *keybuf, Lisp_Object prompt,
9377 posn = POSN_POSN (xevent_start (key)); 9779 posn = POSN_POSN (xevent_start (key));
9378 /* Handle menu-bar events: 9780 /* Handle menu-bar events:
9379 insert the dummy prefix event `menu-bar'. */ 9781 insert the dummy prefix event `menu-bar'. */
9380 if (EQ (posn, Qmenu_bar) || EQ (posn, Qtool_bar)) 9782 if (EQ (posn, Qmenu_bar) || EQ (posn, Qtab_bar) || EQ (posn, Qtool_bar))
9381 { 9783 {
9382 if (READ_KEY_ELTS - t <= 1) 9784 if (READ_KEY_ELTS - t <= 1)
9383 error ("Key sequence too long"); 9785 error ("Key sequence too long");
@@ -10237,7 +10639,8 @@ On such systems, Emacs starts a subshell instead of suspending. */)
10237 get_tty_size (fileno (CURTTY ()->input), &width, &height); 10639 get_tty_size (fileno (CURTTY ()->input), &width, &height);
10238 if (width != old_width || height != old_height) 10640 if (width != old_width || height != old_height)
10239 change_frame_size (SELECTED_FRAME (), width, 10641 change_frame_size (SELECTED_FRAME (), width,
10240 height - FRAME_MENU_BAR_LINES (SELECTED_FRAME ()), 10642 height - FRAME_MENU_BAR_LINES (SELECTED_FRAME ())
10643 - FRAME_TAB_BAR_LINES (SELECTED_FRAME ()),
10241 0, 0, 0, 0); 10644 0, 0, 0, 0);
10242 10645
10243 run_hook (intern ("suspend-resume-hook")); 10646 run_hook (intern ("suspend-resume-hook"));
@@ -11057,6 +11460,11 @@ syms_of_keyboard (void)
11057 staticpro (&item_properties); 11460 staticpro (&item_properties);
11058 item_properties = Qnil; 11461 item_properties = Qnil;
11059 11462
11463 staticpro (&tab_bar_item_properties);
11464 tab_bar_item_properties = Qnil;
11465 staticpro (&tab_bar_items_vector);
11466 tab_bar_items_vector = Qnil;
11467
11060 staticpro (&tool_bar_item_properties); 11468 staticpro (&tool_bar_item_properties);
11061 tool_bar_item_properties = Qnil; 11469 tool_bar_item_properties = Qnil;
11062 staticpro (&tool_bar_items_vector); 11470 staticpro (&tool_bar_items_vector);
@@ -11610,6 +12018,12 @@ See also `pre-command-hook'. */);
11610The elements of the list are event types that may have menu bar bindings. */); 12018The elements of the list are event types that may have menu bar bindings. */);
11611 Vmenu_bar_final_items = Qnil; 12019 Vmenu_bar_final_items = Qnil;
11612 12020
12021 DEFVAR_LISP ("tab-bar-separator-image-expression", Vtab_bar_separator_image_expression,
12022 doc: /* Expression evaluating to the image spec for a tab-bar separator.
12023This is used internally by graphical displays that do not render
12024tab-bar separators natively. Otherwise it is unused (e.g. on GTK). */);
12025 Vtab_bar_separator_image_expression = Qnil;
12026
11613 DEFVAR_LISP ("tool-bar-separator-image-expression", Vtool_bar_separator_image_expression, 12027 DEFVAR_LISP ("tool-bar-separator-image-expression", Vtool_bar_separator_image_expression,
11614 doc: /* Expression evaluating to the image spec for a tool-bar separator. 12028 doc: /* Expression evaluating to the image spec for a tool-bar separator.
11615This is used internally by graphical displays that do not render 12029This 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. */);
3663 DEFSYM (Qmode_line, "mode-line"); 3663 DEFSYM (Qmode_line, "mode-line");
3664 3664
3665 staticpro (&Vmouse_events); 3665 staticpro (&Vmouse_events);
3666 Vmouse_events = pure_list (Qmenu_bar, Qtool_bar, Qheader_line, Qmode_line, 3666 Vmouse_events = pure_list (Qmenu_bar, Qtab_bar, Qtool_bar,
3667 Qtab_line, Qheader_line, Qmode_line,
3667 intern_c_string ("mouse-1"), 3668 intern_c_string ("mouse-1"),
3668 intern_c_string ("mouse-2"), 3669 intern_c_string ("mouse-2"),
3669 intern_c_string ("mouse-3"), 3670 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;
4391extern sigjmp_buf return_to_command_loop; 4391extern sigjmp_buf return_to_command_loop;
4392#endif 4392#endif
4393extern Lisp_Object menu_bar_items (Lisp_Object); 4393extern Lisp_Object menu_bar_items (Lisp_Object);
4394extern Lisp_Object tab_bar_items (Lisp_Object, int *);
4394extern Lisp_Object tool_bar_items (Lisp_Object, int *); 4395extern Lisp_Object tool_bar_items (Lisp_Object, int *);
4395extern void discard_mouse_events (void); 4396extern void discard_mouse_events (void);
4396#ifdef USABLE_SIGIO 4397#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)
1130 /* Decode the first argument: find the window and the coordinates. */ 1130 /* Decode the first argument: find the window and the coordinates. */
1131 if (EQ (position, Qt) 1131 if (EQ (position, Qt)
1132 || (CONSP (position) && (EQ (XCAR (position), Qmenu_bar) 1132 || (CONSP (position) && (EQ (XCAR (position), Qmenu_bar)
1133 || EQ (XCAR (position), Qtab_bar)
1133 || EQ (XCAR (position), Qtool_bar)))) 1134 || EQ (XCAR (position), Qtool_bar))))
1134 { 1135 {
1135 get_current_pos_p = 1; 1136 get_current_pos_p = 1;
@@ -1506,6 +1507,7 @@ for instance using the window manager, then this produces a quit and
1506 /* Decode the first argument: find the window or frame to use. */ 1507 /* Decode the first argument: find the window or frame to use. */
1507 if (EQ (position, Qt) 1508 if (EQ (position, Qt)
1508 || (CONSP (position) && (EQ (XCAR (position), Qmenu_bar) 1509 || (CONSP (position) && (EQ (XCAR (position), Qmenu_bar)
1510 || EQ (XCAR (position), Qtab_bar)
1509 || EQ (XCAR (position), Qtool_bar)))) 1511 || EQ (XCAR (position), Qtool_bar))))
1510 window = selected_window; 1512 window = selected_window;
1511 else if (CONSP (position)) 1513 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)
2655 static Lisp_Object last_mouse_window; 2655 static Lisp_Object last_mouse_window;
2656 2656
2657 mouse_window = window_from_coordinates 2657 mouse_window = window_from_coordinates
2658 (SELECTED_FRAME (), mouse_last_x, mouse_last_y, 0, 0); 2658 (SELECTED_FRAME (), mouse_last_x, mouse_last_y, 0, 0, 0);
2659 /* A window will be selected only when it is not 2659 /* A window will be selected only when it is not
2660 selected now, and the last mouse movement event was 2660 selected now, and the last mouse movement event was
2661 not in it. A minibuffer window will be selected iff 2661 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)
610} 610}
611 611
612 612
613/* tabbar support */
614static void
615ns_set_tab_bar_lines (struct frame *f, Lisp_Object value, Lisp_Object oldval)
616{
617 /* Currently unimplemented. */
618 NSTRACE ("ns_set_tab_bar_lines");
619}
620
621
613/* toolbar support */ 622/* toolbar support */
614static void 623static void
615ns_set_tool_bar_lines (struct frame *f, Lisp_Object value, Lisp_Object oldval) 624ns_set_tool_bar_lines (struct frame *f, Lisp_Object value, Lisp_Object oldval)
@@ -923,6 +932,7 @@ frame_parm_handler ns_frame_parm_handlers[] =
923 gui_set_vertical_scroll_bars, /* generic OK */ 932 gui_set_vertical_scroll_bars, /* generic OK */
924 gui_set_horizontal_scroll_bars, /* generic OK */ 933 gui_set_horizontal_scroll_bars, /* generic OK */
925 gui_set_visibility, /* generic OK */ 934 gui_set_visibility, /* generic OK */
935 ns_set_tab_bar_lines,
926 ns_set_tool_bar_lines, 936 ns_set_tool_bar_lines,
927 0, /* x_set_scroll_bar_foreground, will ignore (not possible on NS) */ 937 0, /* x_set_scroll_bar_foreground, will ignore (not possible on NS) */
928 0, /* x_set_scroll_bar_background, will ignore (not possible on NS) */ 938 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,
1297 RES_TYPE_STRING); 1307 RES_TYPE_STRING);
1298 1308
1299 parms = get_geometry_from_preferences (dpyinfo, parms); 1309 parms = get_geometry_from_preferences (dpyinfo, parms);
1300 window_prompting = gui_figure_window_size (f, parms, true, 1310 window_prompting = gui_figure_window_size (f, parms, false, true,
1301 &x_width, &x_height); 1311 &x_width, &x_height);
1302 1312
1303 tem = gui_display_get_arg (dpyinfo, parms, Qunsplittable, 0, 0, 1313 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)
6859 NSTRACE_MSG ("mouse_autoselect_window"); 6859 NSTRACE_MSG ("mouse_autoselect_window");
6860 static Lisp_Object last_mouse_window; 6860 static Lisp_Object last_mouse_window;
6861 Lisp_Object window 6861 Lisp_Object window
6862 = window_from_coordinates (emacsframe, pt.x, pt.y, 0, 0); 6862 = window_from_coordinates (emacsframe, pt.x, pt.y, 0, 0, 0);
6863 6863
6864 if (WINDOWP (window) 6864 if (WINDOWP (window)
6865 && !EQ (window, last_mouse_window) 6865 && !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). */)
2341 was suspended. */ 2341 was suspended. */
2342 get_tty_size (fileno (t->display_info.tty->input), &width, &height); 2342 get_tty_size (fileno (t->display_info.tty->input), &width, &height);
2343 if (width != old_width || height != old_height) 2343 if (width != old_width || height != old_height)
2344 change_frame_size (f, width, height - FRAME_MENU_BAR_LINES (f), 2344 change_frame_size (f, width, height - FRAME_MENU_BAR_LINES (f)
2345 - FRAME_TAB_BAR_LINES (f),
2345 0, 0, 0, 0); 2346 0, 0, 0, 0);
2346 SET_FRAME_VISIBLE (XFRAME (t->display_info.tty->top_frame), 1); 2347 SET_FRAME_VISIBLE (XFRAME (t->display_info.tty->top_frame), 1);
2347 } 2348 }
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
194 the help to show. */ 194 the help to show. */
195 HELP_EVENT, 195 HELP_EVENT,
196 196
197 /* An event from a tab-bar. Member `arg' of the input event
198 contains the tab-bar item selected. If `frame_or_window'
199 and `arg' are equal, this is a prefix event. */
200 TAB_BAR_EVENT,
201
197 /* An event from a tool-bar. Member `arg' of the input event 202 /* An event from a tool-bar. Member `arg' of the input event
198 contains the tool-bar item selected. If `frame_or_window' 203 contains the tool-bar item selected. If `frame_or_window'
199 and `arg' are equal, this is a prefix event. */ 204 and `arg' are equal, this is a prefix event. */
@@ -624,6 +629,9 @@ struct terminal
624 Lisp_Object (*popup_dialog_hook) (struct frame *f, Lisp_Object header, 629 Lisp_Object (*popup_dialog_hook) (struct frame *f, Lisp_Object header,
625 Lisp_Object contents); 630 Lisp_Object contents);
626 631
632 /* This hook is called to change the frame's (internal) tab-bar. */
633 void (*change_tab_bar_height_hook) (struct frame *f, int height);
634
627 /* This hook is called to change the frame's (internal) tool-bar. */ 635 /* This hook is called to change the frame's (internal) tool-bar. */
628 void (*change_tool_bar_height_hook) (struct frame *f, int height); 636 void (*change_tool_bar_height_hook) (struct frame *f, int height);
629 637
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)
1773} 1773}
1774 1774
1775 1775
1776/* Set the number of lines used for the tab bar of frame F to VALUE.
1777 VALUE not an integer, or < 0 means set the lines to zero. OLDVAL
1778 is the old number of tab bar lines. This function changes the
1779 height of all windows on frame F to match the new tab bar height.
1780 The frame's height doesn't change. */
1781
1782static void
1783w32_set_tab_bar_lines (struct frame *f, Lisp_Object value, Lisp_Object oldval)
1784{
1785 int nlines;
1786
1787 /* Treat tab bars like menu bars. */
1788 if (FRAME_MINIBUF_ONLY_P (f))
1789 return;
1790
1791 /* Use VALUE only if an int >= 0. */
1792 if (RANGED_FIXNUMP (0, value, INT_MAX))
1793 nlines = XFIXNAT (value);
1794 else
1795 nlines = 0;
1796
1797 w32_change_tab_bar_height (f, nlines * FRAME_LINE_HEIGHT (f));
1798}
1799
1800
1801/* Set the pixel height of the tab bar of frame F to HEIGHT. */
1802void
1803w32_change_tab_bar_height (struct frame *f, int height)
1804{
1805 int unit = FRAME_LINE_HEIGHT (f);
1806 int old_height = FRAME_TAB_BAR_HEIGHT (f);
1807 int lines = (height + unit - 1) / unit;
1808 Lisp_Object fullscreen;
1809
1810 /* Make sure we redisplay all windows in this frame. */
1811 fset_redisplay (f);
1812
1813 /* Recalculate tab bar and frame text sizes. */
1814 FRAME_TAB_BAR_HEIGHT (f) = height;
1815 FRAME_TAB_BAR_LINES (f) = lines;
1816 /* Store the `tab-bar-lines' and `height' frame parameters. */
1817 store_frame_param (f, Qtab_bar_lines, make_fixnum (lines));
1818 store_frame_param (f, Qheight, make_fixnum (FRAME_LINES (f)));
1819
1820 /* We also have to make sure that the internal border at the top of
1821 the frame, below the menu bar or tab bar, is redrawn when the
1822 tab bar disappears. This is so because the internal border is
1823 below the tab bar if one is displayed, but is below the menu bar
1824 if there isn't a tab bar. The tab bar draws into the area
1825 below the menu bar. */
1826 if (FRAME_W32_WINDOW (f) && FRAME_TAB_BAR_HEIGHT (f) == 0)
1827 {
1828 clear_frame (f);
1829 clear_current_matrices (f);
1830 }
1831
1832 if ((height < old_height) && WINDOWP (f->tab_bar_window))
1833 clear_glyph_matrix (XWINDOW (f->tab_bar_window)->current_matrix);
1834
1835 /* Recalculate tabbar height. */
1836 f->n_tab_bar_rows = 0;
1837 if (old_height == 0
1838 && (!f->after_make_frame
1839 || NILP (frame_inhibit_implied_resize)
1840 || (CONSP (frame_inhibit_implied_resize)
1841 && NILP (Fmemq (Qtab_bar_lines, frame_inhibit_implied_resize)))))
1842 f->tab_bar_redisplayed = f->tab_bar_resized = false;
1843
1844 adjust_frame_size (f, -1, -1,
1845 ((!f->tab_bar_resized
1846 && (NILP (fullscreen =
1847 get_frame_param (f, Qfullscreen))
1848 || EQ (fullscreen, Qfullwidth))) ? 1
1849 : (old_height == 0 || height == 0) ? 2
1850 : 4),
1851 false, Qtab_bar_lines);
1852
1853 f->tab_bar_resized = f->tab_bar_redisplayed;
1854
1855 /* adjust_frame_size might not have done anything, garbage frame
1856 here. */
1857 adjust_frame_glyphs (f);
1858 SET_FRAME_GARBAGED (f);
1859 if (FRAME_W32_WINDOW (f))
1860 w32_clear_under_internal_border (f);
1861}
1862
1863
1776/* Set the number of lines used for the tool bar of frame F to VALUE. 1864/* Set the number of lines used for the tool bar of frame F to VALUE.
1777 VALUE not an integer, or < 0 means set the lines to zero. OLDVAL is 1865 VALUE not an integer, or < 0 means set the lines to zero. OLDVAL is
1778 the old number of tool bar lines (and is unused). This function may 1866 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,
5989 /* No menu bar for child frames. */ 6077 /* No menu bar for child frames. */
5990 store_frame_param (f, Qmenu_bar_lines, make_fixnum (0)); 6078 store_frame_param (f, Qmenu_bar_lines, make_fixnum (0));
5991 6079
6080 gui_default_parameter (f, parameters, Qtab_bar_lines,
6081 NILP (Vtab_bar_mode)
6082 ? make_fixnum (0) : make_fixnum (1),
6083 NULL, NULL, RES_TYPE_NUMBER);
6084
5992 gui_default_parameter (f, parameters, Qtool_bar_lines, 6085 gui_default_parameter (f, parameters, Qtool_bar_lines,
5993 NILP (Vtool_bar_mode) 6086 NILP (Vtool_bar_mode)
5994 ? make_fixnum (0) : make_fixnum (1), 6087 ? make_fixnum (0) : make_fixnum (1),
@@ -6018,7 +6111,7 @@ DEFUN ("x-create-frame", Fx_create_frame, Sx_create_frame,
6018 6111
6019 f->output_data.w32->current_cursor = f->output_data.w32->nontext_cursor; 6112 f->output_data.w32->current_cursor = f->output_data.w32->nontext_cursor;
6020 6113
6021 window_prompting = gui_figure_window_size (f, parameters, true, 6114 window_prompting = gui_figure_window_size (f, parameters, true, true,
6022 &x_width, &x_height); 6115 &x_width, &x_height);
6023 6116
6024 tem = gui_display_get_arg (dpyinfo, parameters, Qunsplittable, 0, 0, 6117 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)
6986 f->output_data.w32->parent_desc = FRAME_DISPLAY_INFO (f)->root_window; 7079 f->output_data.w32->parent_desc = FRAME_DISPLAY_INFO (f)->root_window;
6987 f->output_data.w32->explicit_parent = false; 7080 f->output_data.w32->explicit_parent = false;
6988 7081
6989 gui_figure_window_size (f, parms, true, &x_width, &x_height); 7082 gui_figure_window_size (f, parms, true, true, &x_width, &x_height);
6990 7083
6991 /* No fringes on tip frame. */ 7084 /* No fringes on tip frame. */
6992 f->fringe_cols = 0; 7085 f->fringe_cols = 0;
@@ -8739,6 +8832,9 @@ and width values are in pixels.
8739`menu-bar-size' is a cons of the width and height of the menu bar of 8832`menu-bar-size' is a cons of the width and height of the menu bar of
8740 FRAME. 8833 FRAME.
8741 8834
8835`tab-bar-size' is a cons of the width and height of the tab bar of
8836 FRAME.
8837
8742`tool-bar-external', if non-nil, means the tool bar is external (never 8838`tool-bar-external', if non-nil, means the tool bar is external (never
8743 included in the inner edges of FRAME). 8839 included in the inner edges of FRAME).
8744 8840
@@ -8761,6 +8857,7 @@ and width values are in pixels.
8761 unsigned int external_border_width, external_border_height; 8857 unsigned int external_border_width, external_border_height;
8762 int title_bar_width = 0, title_bar_height = 0; 8858 int title_bar_width = 0, title_bar_height = 0;
8763 int single_menu_bar_height, wrapped_menu_bar_height, menu_bar_height; 8859 int single_menu_bar_height, wrapped_menu_bar_height, menu_bar_height;
8860 int tab_bar_height = FRAME_TAB_BAR_HEIGHT (f);
8764 int tool_bar_height = FRAME_TOOL_BAR_HEIGHT (f); 8861 int tool_bar_height = FRAME_TOOL_BAR_HEIGHT (f);
8765 int internal_border_width = FRAME_INTERNAL_BORDER_WIDTH (f); 8862 int internal_border_width = FRAME_INTERNAL_BORDER_WIDTH (f);
8766 8863
@@ -8834,6 +8931,13 @@ and width values are in pixels.
8834 Fcons (make_fixnum 8931 Fcons (make_fixnum
8835 (menu_bar.rcBar.right - menu_bar.rcBar.left), 8932 (menu_bar.rcBar.right - menu_bar.rcBar.left),
8836 make_fixnum (menu_bar_height))), 8933 make_fixnum (menu_bar_height))),
8934 Fcons (Qtab_bar_size,
8935 Fcons (make_fixnum
8936 (tab_bar_height
8937 ? (right - left - 2 * external_border_width
8938 - 2 * internal_border_width)
8939 : 0),
8940 make_fixnum (tab_bar_height))),
8837 Fcons (Qtool_bar_external, Qnil), 8941 Fcons (Qtool_bar_external, Qnil),
8838 Fcons (Qtool_bar_position, tool_bar_height ? Qtop : Qnil), 8942 Fcons (Qtool_bar_position, tool_bar_height ? Qtop : Qnil),
8839 Fcons (Qtool_bar_size, 8943 Fcons (Qtool_bar_size,
@@ -8925,6 +9029,7 @@ menu bar or tool bar of FRAME. */)
8925 9029
8926 return list4 (make_fixnum (left + internal_border_width), 9030 return list4 (make_fixnum (left + internal_border_width),
8927 make_fixnum (top 9031 make_fixnum (top
9032 + FRAME_TAB_BAR_HEIGHT (f)
8928 + FRAME_TOOL_BAR_HEIGHT (f) 9033 + FRAME_TOOL_BAR_HEIGHT (f)
8929 + internal_border_width), 9034 + internal_border_width),
8930 make_fixnum (right - internal_border_width), 9035 make_fixnum (right - internal_border_width),
@@ -10212,6 +10317,7 @@ frame_parm_handler w32_frame_parm_handlers[] =
10212 gui_set_vertical_scroll_bars, 10317 gui_set_vertical_scroll_bars,
10213 gui_set_horizontal_scroll_bars, 10318 gui_set_horizontal_scroll_bars,
10214 gui_set_visibility, 10319 gui_set_visibility,
10320 w32_set_tab_bar_lines,
10215 w32_set_tool_bar_lines, 10321 w32_set_tool_bar_lines,
10216 0, /* x_set_scroll_bar_foreground, */ 10322 0, /* x_set_scroll_bar_foreground, */
10217 0, /* x_set_scroll_bar_background, */ 10323 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,
492 if (!NILP (Vmouse_autoselect_window)) 492 if (!NILP (Vmouse_autoselect_window))
493 { 493 {
494 Lisp_Object mouse_window = window_from_coordinates (f, mx, my, 494 Lisp_Object mouse_window = window_from_coordinates (f, mx, my,
495 0, 0); 495 0, 0, 0);
496 /* A window will be selected only when it is not 496 /* A window will be selected only when it is not
497 selected now, and the last mouse movement event was 497 selected now, and the last mouse movement event was
498 not in it. A minibuffer window will be selected iff 498 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/>. */
36 "emacs.tooltip.attributeBackground:SystemInfoWindow\0" \ 36 "emacs.tooltip.attributeBackground:SystemInfoWindow\0" \
37 "emacs.tool-bar.attributeForeground:SystemButtonText\0" \ 37 "emacs.tool-bar.attributeForeground:SystemButtonText\0" \
38 "emacs.tool-bar.attributeBackground:SystemButtonFace\0" \ 38 "emacs.tool-bar.attributeBackground:SystemButtonFace\0" \
39 "emacs.tab-bar.attributeForeground:SystemButtonText\0" \
40 "emacs.tab-bar.attributeBackground:SystemButtonFace\0" \
39 "emacs.menu.attributeForeground:SystemMenuText\0" \ 41 "emacs.menu.attributeForeground:SystemMenuText\0" \
40 "emacs.menu.attributeBackground:SystemMenu\0" \ 42 "emacs.menu.attributeBackground:SystemMenu\0" \
41 "emacs.scroll-bar.attributeForeground:SystemScrollbar\0" 43 "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;
168int w32_message_fd = -1; 168int w32_message_fd = -1;
169#endif /* CYGWIN */ 169#endif /* CYGWIN */
170 170
171static void w32_handle_tab_bar_click (struct frame *,
172 struct input_event *);
171static void w32_handle_tool_bar_click (struct frame *, 173static void w32_handle_tool_bar_click (struct frame *,
172 struct input_event *); 174 struct input_event *);
173static void w32_define_cursor (Window, Emacs_Cursor); 175static void w32_define_cursor (Window, Emacs_Cursor);
@@ -3604,6 +3606,29 @@ w32_mouse_position (struct frame **fp, int insist, Lisp_Object *bar_window,
3604 3606
3605 3607
3606/*********************************************************************** 3608/***********************************************************************
3609 Tab-bars
3610 ***********************************************************************/
3611
3612/* Handle mouse button event on the tab-bar of frame F, at
3613 frame-relative coordinates X/Y. EVENT_TYPE is either ButtonPress
3614 or ButtonRelease. */
3615
3616static void
3617w32_handle_tab_bar_click (struct frame *f, struct input_event *button_event)
3618{
3619 int x = XFIXNAT (button_event->x);
3620 int y = XFIXNAT (button_event->y);
3621
3622 if (button_event->modifiers & down_modifier)
3623 handle_tab_bar_click (f, x, y, 1, 0);
3624 else
3625 handle_tab_bar_click (f, x, y, 0,
3626 button_event->modifiers & ~up_modifier);
3627}
3628
3629
3630
3631/***********************************************************************
3607 Tool-bars 3632 Tool-bars
3608 ***********************************************************************/ 3633 ***********************************************************************/
3609 3634
@@ -4843,6 +4868,7 @@ w32_read_socket (struct terminal *terminal,
4843 if (f && !FRAME_ICONIFIED_P (f)) 4868 if (f && !FRAME_ICONIFIED_P (f))
4844 { 4869 {
4845 if (!hlinfo->mouse_face_hidden && FIXNUMP (Vmouse_highlight) 4870 if (!hlinfo->mouse_face_hidden && FIXNUMP (Vmouse_highlight)
4871 && !EQ (f->tab_bar_window, hlinfo->mouse_face_window)
4846 && !EQ (f->tool_bar_window, hlinfo->mouse_face_window)) 4872 && !EQ (f->tool_bar_window, hlinfo->mouse_face_window))
4847 { 4873 {
4848 clear_mouse_face (hlinfo); 4874 clear_mouse_face (hlinfo);
@@ -4868,6 +4894,7 @@ w32_read_socket (struct terminal *terminal,
4868 if (f && !FRAME_ICONIFIED_P (f)) 4894 if (f && !FRAME_ICONIFIED_P (f))
4869 { 4895 {
4870 if (!hlinfo->mouse_face_hidden && FIXNUMP (Vmouse_highlight) 4896 if (!hlinfo->mouse_face_hidden && FIXNUMP (Vmouse_highlight)
4897 && !EQ (f->tab_bar_window, hlinfo->mouse_face_window)
4871 && !EQ (f->tool_bar_window, hlinfo->mouse_face_window)) 4898 && !EQ (f->tool_bar_window, hlinfo->mouse_face_window))
4872 { 4899 {
4873 clear_mouse_face (hlinfo); 4900 clear_mouse_face (hlinfo);
@@ -4946,6 +4973,7 @@ w32_read_socket (struct terminal *terminal,
4946 if (f && !FRAME_ICONIFIED_P (f)) 4973 if (f && !FRAME_ICONIFIED_P (f))
4947 { 4974 {
4948 if (!hlinfo->mouse_face_hidden && FIXNUMP (Vmouse_highlight) 4975 if (!hlinfo->mouse_face_hidden && FIXNUMP (Vmouse_highlight)
4976 && !EQ (f->tab_bar_window, hlinfo->mouse_face_window)
4949 && !EQ (f->tool_bar_window, hlinfo->mouse_face_window)) 4977 && !EQ (f->tool_bar_window, hlinfo->mouse_face_window))
4950 { 4978 {
4951 clear_mouse_face (hlinfo); 4979 clear_mouse_face (hlinfo);
@@ -4998,7 +5026,7 @@ w32_read_socket (struct terminal *terminal,
4998 { 5026 {
4999 static Lisp_Object last_mouse_window; 5027 static Lisp_Object last_mouse_window;
5000 Lisp_Object window = window_from_coordinates 5028 Lisp_Object window = window_from_coordinates
5001 (f, LOWORD (msg.msg.lParam), HIWORD (msg.msg.lParam), 0, 0); 5029 (f, LOWORD (msg.msg.lParam), HIWORD (msg.msg.lParam), 0, 0, 0);
5002 5030
5003 /* Window will be selected only when it is not 5031 /* Window will be selected only when it is not
5004 selected now and last mouse movement event was 5032 selected now and last mouse movement event was
@@ -5051,6 +5079,7 @@ w32_read_socket (struct terminal *terminal,
5051 { 5079 {
5052 /* If we decide we want to generate an event to be seen 5080 /* If we decide we want to generate an event to be seen
5053 by the rest of Emacs, we put it here. */ 5081 by the rest of Emacs, we put it here. */
5082 bool tab_bar_p = 0;
5054 bool tool_bar_p = 0; 5083 bool tool_bar_p = 0;
5055 int button = 0; 5084 int button = 0;
5056 int up = 0; 5085 int up = 0;
@@ -5060,6 +5089,31 @@ w32_read_socket (struct terminal *terminal,
5060 { 5089 {
5061 w32_construct_mouse_click (&inev, &msg, f); 5090 w32_construct_mouse_click (&inev, &msg, f);
5062 5091
5092 /* Is this in the tab-bar? */
5093 if (WINDOWP (f->tab_bar_window)
5094 && WINDOW_TOTAL_LINES (XWINDOW (f->tab_bar_window)))
5095 {
5096 Lisp_Object window;
5097 int x = XFIXNAT (inev.x);
5098 int y = XFIXNAT (inev.y);
5099
5100 window = window_from_coordinates (f, x, y, 0, 1, 1);
5101
5102 if (EQ (window, f->tab_bar_window))
5103 {
5104 w32_handle_tab_bar_click (f, &inev);
5105 tab_bar_p = 1;
5106 }
5107 }
5108
5109 if (tab_bar_p
5110 || (dpyinfo->w32_focus_frame
5111 && f != dpyinfo->w32_focus_frame
5112 /* This does not help when the click happens in
5113 a grand-parent frame. */
5114 && !frame_ancestor_p (f, dpyinfo->w32_focus_frame)))
5115 inev.kind = NO_EVENT;
5116
5063 /* Is this in the tool-bar? */ 5117 /* Is this in the tool-bar? */
5064 if (WINDOWP (f->tool_bar_window) 5118 if (WINDOWP (f->tool_bar_window)
5065 && WINDOW_TOTAL_LINES (XWINDOW (f->tool_bar_window))) 5119 && WINDOW_TOTAL_LINES (XWINDOW (f->tool_bar_window)))
@@ -5068,7 +5122,7 @@ w32_read_socket (struct terminal *terminal,
5068 int x = XFIXNAT (inev.x); 5122 int x = XFIXNAT (inev.x);
5069 int y = XFIXNAT (inev.y); 5123 int y = XFIXNAT (inev.y);
5070 5124
5071 window = window_from_coordinates (f, x, y, 0, 1); 5125 window = window_from_coordinates (f, x, y, 0, 1, 1);
5072 5126
5073 if (EQ (window, f->tool_bar_window)) 5127 if (EQ (window, f->tool_bar_window))
5074 { 5128 {
@@ -5104,6 +5158,8 @@ w32_read_socket (struct terminal *terminal,
5104 if (f != 0) 5158 if (f != 0)
5105 { 5159 {
5106 f->mouse_moved = false; 5160 f->mouse_moved = false;
5161 if (!tab_bar_p)
5162 f->last_tab_bar_item = -1;
5107 if (!tool_bar_p) 5163 if (!tool_bar_p)
5108 f->last_tool_bar_item = -1; 5164 f->last_tool_bar_item = -1;
5109 } 5165 }
@@ -5127,6 +5183,7 @@ w32_read_socket (struct terminal *terminal,
5127 event; any subsequent mouse-movement Emacs events 5183 event; any subsequent mouse-movement Emacs events
5128 should reflect only motion after the ButtonPress. */ 5184 should reflect only motion after the ButtonPress. */
5129 f->mouse_moved = false; 5185 f->mouse_moved = false;
5186 f->last_tab_bar_item = -1;
5130 f->last_tool_bar_item = -1; 5187 f->last_tool_bar_item = -1;
5131 dpyinfo->last_mouse_frame = f; 5188 dpyinfo->last_mouse_frame = f;
5132 } 5189 }
@@ -5140,6 +5197,7 @@ w32_read_socket (struct terminal *terminal,
5140 { 5197 {
5141 w32_construct_mouse_wheel (&inev, &msg, f1); 5198 w32_construct_mouse_wheel (&inev, &msg, f1);
5142 f1->mouse_moved = false; 5199 f1->mouse_moved = false;
5200 f1->last_tab_bar_item = -1;
5143 f1->last_tool_bar_item = -1; 5201 f1->last_tool_bar_item = -1;
5144 dpyinfo->last_mouse_frame = f1; 5202 dpyinfo->last_mouse_frame = f1;
5145 } 5203 }
@@ -5936,7 +5994,8 @@ w32_draw_window_cursor (struct window *w, struct glyph_row *glyph_row,
5936 = (WINDOW_TO_FRAME_PIXEL_Y (w, w->phys_cursor.y) 5994 = (WINDOW_TO_FRAME_PIXEL_Y (w, w->phys_cursor.y)
5937 + glyph_row->ascent - w->phys_cursor_ascent); 5995 + glyph_row->ascent - w->phys_cursor_ascent);
5938 w32_system_caret_window = w; 5996 w32_system_caret_window = w;
5939 w32_system_caret_hdr_height = WINDOW_HEADER_LINE_HEIGHT (w); 5997 w32_system_caret_hdr_height = WINDOW_TAB_LINE_HEIGHT (w)
5998 + WINDOW_HEADER_LINE_HEIGHT (w);
5940 w32_system_caret_mode_height = WINDOW_MODE_LINE_HEIGHT (w); 5999 w32_system_caret_mode_height = WINDOW_MODE_LINE_HEIGHT (w);
5941 6000
5942 PostMessage (hwnd, WM_IME_STARTCOMPOSITION, 0, 0); 6001 PostMessage (hwnd, WM_IME_STARTCOMPOSITION, 0, 0);
@@ -7192,6 +7251,7 @@ w32_create_terminal (struct w32_display_info *dpyinfo)
7192 terminal->menu_show_hook = w32_menu_show; 7251 terminal->menu_show_hook = w32_menu_show;
7193 terminal->activate_menubar_hook = w32_activate_menubar; 7252 terminal->activate_menubar_hook = w32_activate_menubar;
7194 terminal->popup_dialog_hook = w32_popup_dialog; 7253 terminal->popup_dialog_hook = w32_popup_dialog;
7254 terminal->change_tab_bar_height_hook = w32_change_tab_bar_height;
7195 terminal->change_tool_bar_height_hook = w32_change_tool_bar_height; 7255 terminal->change_tool_bar_height_hook = w32_change_tool_bar_height;
7196 terminal->set_vertical_scroll_bar_hook = w32_set_vertical_scroll_bar; 7256 terminal->set_vertical_scroll_bar_hook = w32_set_vertical_scroll_bar;
7197 terminal->set_horizontal_scroll_bar_hook = w32_set_horizontal_scroll_bar; 7257 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);
233 233
234extern void w32_clear_under_internal_border (struct frame *); 234extern void w32_clear_under_internal_border (struct frame *);
235 235
236extern void w32_change_tab_bar_height (struct frame *, int);
236extern void w32_change_tool_bar_height (struct frame *, int); 237extern void w32_change_tool_bar_height (struct frame *, int);
237extern void w32_implicitly_set_name (struct frame *, Lisp_Object, Lisp_Object); 238extern void w32_implicitly_set_name (struct frame *, Lisp_Object, Lisp_Object);
238extern void w32_set_scroll_bar_default_width (struct frame *); 239extern 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
1002window_body_height (struct window *w, bool pixelwise) 1002window_body_height (struct window *w, bool pixelwise)
1003{ 1003{
1004 int height = (w->pixel_height 1004 int height = (w->pixel_height
1005 - WINDOW_TAB_LINE_HEIGHT (w)
1005 - WINDOW_HEADER_LINE_HEIGHT (w) 1006 - WINDOW_HEADER_LINE_HEIGHT (w)
1006 - (WINDOW_HAS_HORIZONTAL_SCROLL_BAR (w) 1007 - (WINDOW_HAS_HORIZONTAL_SCROLL_BAR (w)
1007 ? WINDOW_SCROLL_BAR_AREA_HEIGHT (w) 1008 ? WINDOW_SCROLL_BAR_AREA_HEIGHT (w)
@@ -1131,6 +1132,15 @@ WINDOW must be a live window and defaults to the selected one. */)
1131 return (make_fixnum (WINDOW_HEADER_LINE_HEIGHT (decode_live_window (window)))); 1132 return (make_fixnum (WINDOW_HEADER_LINE_HEIGHT (decode_live_window (window))));
1132} 1133}
1133 1134
1135DEFUN ("window-tab-line-height", Fwindow_tab_line_height,
1136 Swindow_tab_line_height, 0, 1, 0,
1137 doc: /* Return the height in pixels of WINDOW's tab-line.
1138WINDOW must be a live window and defaults to the selected one. */)
1139 (Lisp_Object window)
1140{
1141 return (make_fixnum (WINDOW_TAB_LINE_HEIGHT (decode_live_window (window))));
1142}
1143
1134DEFUN ("window-right-divider-width", Fwindow_right_divider_width, 1144DEFUN ("window-right-divider-width", Fwindow_right_divider_width,
1135 Swindow_right_divider_width, 0, 1, 0, 1145 Swindow_right_divider_width, 0, 1, 0,
1136 doc: /* Return the width in pixels of WINDOW's right divider. 1146 doc: /* Return the width in pixels of WINDOW's right divider.
@@ -1249,7 +1259,8 @@ end-trigger value is reset to nil. */)
1249 if it is on the border between the window and its right sibling, 1259 if it is on the border between the window and its right sibling,
1250 return ON_VERTICAL_BORDER; 1260 return ON_VERTICAL_BORDER;
1251 if it is on a scroll bar, return ON_SCROLL_BAR; 1261 if it is on a scroll bar, return ON_SCROLL_BAR;
1252 if it is on the window's top line, return ON_HEADER_LINE; 1262 if it is on the window's top line, return ON_TAB_LINE;
1263 if it is on the window's header line, return ON_HEADER_LINE;
1253 if it is in left or right fringe of the window, 1264 if it is in left or right fringe of the window,
1254 return ON_LEFT_FRINGE or ON_RIGHT_FRINGE; 1265 return ON_LEFT_FRINGE or ON_RIGHT_FRINGE;
1255 if it is in the marginal area to the left/right of the window, 1266 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)
1299 - CURRENT_MODE_LINE_HEIGHT (w) 1310 - CURRENT_MODE_LINE_HEIGHT (w)
1300 - WINDOW_BOTTOM_DIVIDER_WIDTH (w)))) 1311 - WINDOW_BOTTOM_DIVIDER_WIDTH (w))))
1301 return ON_HORIZONTAL_SCROLL_BAR; 1312 return ON_HORIZONTAL_SCROLL_BAR;
1302 /* On the mode or header line? */ 1313 /* On the mode or header/tab line? */
1303 else if ((window_wants_mode_line (w) 1314 else if ((window_wants_mode_line (w)
1304 && y >= (bottom_y 1315 && y >= (bottom_y
1305 - CURRENT_MODE_LINE_HEIGHT (w) 1316 - CURRENT_MODE_LINE_HEIGHT (w)
1306 - WINDOW_BOTTOM_DIVIDER_WIDTH (w)) 1317 - WINDOW_BOTTOM_DIVIDER_WIDTH (w))
1307 && y <= bottom_y - WINDOW_BOTTOM_DIVIDER_WIDTH (w) 1318 && y <= bottom_y - WINDOW_BOTTOM_DIVIDER_WIDTH (w)
1308 && (part = ON_MODE_LINE)) 1319 && (part = ON_MODE_LINE))
1320 || (window_wants_tab_line (w)
1321 && y < top_y + CURRENT_TAB_LINE_HEIGHT (w)
1322 && (part = ON_TAB_LINE))
1309 || (window_wants_header_line (w) 1323 || (window_wants_header_line (w)
1310 && y < top_y + CURRENT_HEADER_LINE_HEIGHT (w) 1324 && y < top_y + CURRENT_TAB_LINE_HEIGHT (w)
1325 + CURRENT_HEADER_LINE_HEIGHT (w)
1311 && (part = ON_HEADER_LINE))) 1326 && (part = ON_HEADER_LINE)))
1312 { 1327 {
1313 /* If it's under/over the scroll bar portion of the mode/header 1328 /* 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)
1407 case ON_TEXT: 1422 case ON_TEXT:
1408 return x - window_box_left (w, TEXT_AREA); 1423 return x - window_box_left (w, TEXT_AREA);
1409 1424
1425 case ON_TAB_LINE:
1410 case ON_HEADER_LINE: 1426 case ON_HEADER_LINE:
1411 case ON_MODE_LINE: 1427 case ON_MODE_LINE:
1412 case ON_LEFT_FRINGE: 1428 case ON_LEFT_FRINGE:
@@ -1457,6 +1473,7 @@ If they are in the bottom divider of WINDOW, `bottom-divider' is returned.
1457If they are in the right divider of WINDOW, `right-divider' is returned. 1473If they are in the right divider of WINDOW, `right-divider' is returned.
1458If they are in the mode line of WINDOW, `mode-line' is returned. 1474If they are in the mode line of WINDOW, `mode-line' is returned.
1459If they are in the header line of WINDOW, `header-line' is returned. 1475If they are in the header line of WINDOW, `header-line' is returned.
1476If they are in the tab line of WINDOW, `tab-line' is returned.
1460If they are in the left fringe of WINDOW, `left-fringe' is returned. 1477If they are in the left fringe of WINDOW, `left-fringe' is returned.
1461If they are in the right fringe of WINDOW, `right-fringe' is returned. 1478If they are in the right fringe of WINDOW, `right-fringe' is returned.
1462If they are on the border between WINDOW and its right sibling, 1479If 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\
1502 case ON_HEADER_LINE: 1519 case ON_HEADER_LINE:
1503 return Qheader_line; 1520 return Qheader_line;
1504 1521
1522 case ON_TAB_LINE:
1523 return Qtab_line;
1524
1505 case ON_LEFT_FRINGE: 1525 case ON_LEFT_FRINGE:
1506 return Qleft_fringe; 1526 return Qleft_fringe;
1507 1527
@@ -1585,7 +1605,7 @@ check_window_containing (struct window *w, void *user_data)
1585 1605
1586Lisp_Object 1606Lisp_Object
1587window_from_coordinates (struct frame *f, int x, int y, 1607window_from_coordinates (struct frame *f, int x, int y,
1588 enum window_part *part, bool tool_bar_p) 1608 enum window_part *part, bool tab_bar_p, bool tool_bar_p)
1589{ 1609{
1590 Lisp_Object window; 1610 Lisp_Object window;
1591 struct check_window_data cw; 1611 struct check_window_data cw;
@@ -1598,6 +1618,21 @@ window_from_coordinates (struct frame *f, int x, int y,
1598 cw.window = &window, cw.x = x, cw.y = y; cw.part = part; 1618 cw.window = &window, cw.x = x, cw.y = y; cw.part = part;
1599 foreach_window (f, check_window_containing, &cw); 1619 foreach_window (f, check_window_containing, &cw);
1600 1620
1621#if defined (HAVE_WINDOW_SYSTEM)
1622 /* If not found above, see if it's in the tab bar window, if a tab
1623 bar exists. */
1624 if (NILP (window)
1625 && tab_bar_p
1626 && WINDOWP (f->tab_bar_window)
1627 && WINDOW_TOTAL_LINES (XWINDOW (f->tab_bar_window)) > 0
1628 && (coordinates_in_window (XWINDOW (f->tab_bar_window), x, y)
1629 != ON_NOTHING))
1630 {
1631 *part = ON_TEXT;
1632 window = f->tab_bar_window;
1633 }
1634#endif
1635
1601#if defined (HAVE_WINDOW_SYSTEM) && ! defined (HAVE_EXT_TOOL_BAR) 1636#if defined (HAVE_WINDOW_SYSTEM) && ! defined (HAVE_EXT_TOOL_BAR)
1602 /* If not found above, see if it's in the tool bar window, if a tool 1637 /* If not found above, see if it's in the tool bar window, if a tool
1603 bar exists. */ 1638 bar exists. */
@@ -1633,7 +1668,7 @@ column 0. */)
1633 + FRAME_INTERNAL_BORDER_WIDTH (f)), 1668 + FRAME_INTERNAL_BORDER_WIDTH (f)),
1634 (FRAME_PIXEL_Y_FROM_CANON_Y (f, y) 1669 (FRAME_PIXEL_Y_FROM_CANON_Y (f, y)
1635 + FRAME_INTERNAL_BORDER_WIDTH (f)), 1670 + FRAME_INTERNAL_BORDER_WIDTH (f)),
1636 0, false); 1671 0, false, false);
1637} 1672}
1638 1673
1639DEFUN ("window-point", Fwindow_point, Swindow_point, 0, 1, 0, 1674DEFUN ("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
1945 goto found_row; 1980 goto found_row;
1946 } 1981 }
1947 1982
1983 if (EQ (line, Qtab_line))
1984 {
1985 if (!window_wants_tab_line (w))
1986 return Qnil;
1987 row = MATRIX_TAB_LINE_ROW (w->current_matrix);
1988 return row->enabled_p ? list4i (row->height, 0, 0, 0) : Qnil;
1989 }
1990
1948 if (EQ (line, Qheader_line)) 1991 if (EQ (line, Qheader_line))
1949 { 1992 {
1950 if (!window_wants_header_line (w)) 1993 if (!window_wants_header_line (w))
@@ -1959,7 +2002,8 @@ Return nil if window display is not up-to-date. In that case, use
1959 return (row->enabled_p ? 2002 return (row->enabled_p ?
1960 list4i (row->height, 2003 list4i (row->height,
1961 0, /* not accurate */ 2004 0, /* not accurate */
1962 (WINDOW_HEADER_LINE_HEIGHT (w) 2005 (WINDOW_TAB_LINE_HEIGHT (w)
2006 + WINDOW_HEADER_LINE_HEIGHT (w)
1963 + window_text_bottom_y (w)), 2007 + window_text_bottom_y (w)),
1964 0) 2008 0)
1965 : Qnil); 2009 : Qnil);
@@ -2045,8 +2089,9 @@ though when run from an idle timer with a delay of zero seconds. */)
2045 int max_y = NILP (body) ? WINDOW_PIXEL_HEIGHT (w) : window_text_bottom_y (w); 2089 int max_y = NILP (body) ? WINDOW_PIXEL_HEIGHT (w) : window_text_bottom_y (w);
2046 Lisp_Object rows = Qnil; 2090 Lisp_Object rows = Qnil;
2047 int window_width = NILP (body) ? w->pixel_width : window_body_width (w, true); 2091 int window_width = NILP (body) ? w->pixel_width : window_body_width (w, true);
2092 int tab_line_height = WINDOW_TAB_LINE_HEIGHT (w);
2048 int header_line_height = WINDOW_HEADER_LINE_HEIGHT (w); 2093 int header_line_height = WINDOW_HEADER_LINE_HEIGHT (w);
2049 int subtract = NILP (body) ? 0 : header_line_height; 2094 int subtract = NILP (body) ? 0 : (tab_line_height + header_line_height);
2050 bool invert = !NILP (inverse); 2095 bool invert = !NILP (inverse);
2051 bool left_flag = !NILP (left); 2096 bool left_flag = !NILP (left);
2052 2097
@@ -4246,7 +4291,7 @@ make_window (void)
4246 non-Lisp data, so do it only for slots which should not be zero. */ 4291 non-Lisp data, so do it only for slots which should not be zero. */
4247 w->nrows_scale_factor = w->ncols_scale_factor = 1; 4292 w->nrows_scale_factor = w->ncols_scale_factor = 1;
4248 w->left_fringe_width = w->right_fringe_width = -1; 4293 w->left_fringe_width = w->right_fringe_width = -1;
4249 w->mode_line_height = w->header_line_height = -1; 4294 w->mode_line_height = w->tab_line_height = w->header_line_height = -1;
4250#ifdef HAVE_WINDOW_SYSTEM 4295#ifdef HAVE_WINDOW_SYSTEM
4251 w->phys_cursor_type = NO_CURSOR; 4296 w->phys_cursor_type = NO_CURSOR;
4252 w->phys_cursor_width = -1; 4297 w->phys_cursor_width = -1;
@@ -4772,7 +4817,7 @@ Third argument SIDE nil (or `below') specifies that the new window shall
4772be located below WINDOW. SIDE `above' means the new window shall be 4817be located below WINDOW. SIDE `above' means the new window shall be
4773located above WINDOW. In both cases PIXEL-SIZE specifies the pixel 4818located above WINDOW. In both cases PIXEL-SIZE specifies the pixel
4774height of the new window including space reserved for the mode and/or 4819height of the new window including space reserved for the mode and/or
4775header line. 4820header/tab line.
4776 4821
4777SIDE t (or `right') specifies that the new window shall be located on 4822SIDE t (or `right') specifies that the new window shall be located on
4778the right side of WINDOW. SIDE `left' means the new window shall be 4823the right side of WINDOW. SIDE `left' means the new window shall be
@@ -5350,6 +5395,41 @@ window_wants_header_line (struct window *w)
5350 : 0); 5395 : 0);
5351} 5396}
5352 5397
5398
5399/**
5400 * window_wants_tab_line:
5401 *
5402 * Return 1 if window W wants a tab line and is high enough to
5403 * accommodate it, 0 otherwise.
5404 *
5405 * W wants a tab line if it's a leaf window and neither a minibuffer
5406 * nor a pseudo window. Moreover, its 'window-mode-line-format'
5407 * parameter must not be 'none' and either that parameter or W's
5408 * buffer's 'mode-line-format' value must be non-nil. Finally, W must
5409 * be higher than its frame's canonical character height and be able
5410 * to accommodate a mode line and a header line too if necessary (the
5411 * mode line and a header line prevail).
5412 */
5413bool
5414window_wants_tab_line (struct window *w)
5415{
5416 Lisp_Object window_tab_line_format =
5417 window_parameter (w, Qtab_line_format);
5418
5419 return ((WINDOW_LEAF_P (w)
5420 && !MINI_WINDOW_P (w)
5421 && !WINDOW_PSEUDO_P (w)
5422 && !EQ (window_tab_line_format, Qnone)
5423 && (!NILP (window_tab_line_format)
5424 || !NILP (BVAR (XBUFFER (WINDOW_BUFFER (w)), tab_line_format)))
5425 && (WINDOW_PIXEL_HEIGHT (w)
5426 > (((window_wants_mode_line (w) ? 1 : 0)
5427 + (window_wants_header_line (w) ? 1 : 0)
5428 + 1) * WINDOW_FRAME_LINE_HEIGHT (w))))
5429 ? 1
5430 : 0);
5431}
5432
5353/* Return number of lines of text in window W, not counting the mode 5433/* Return number of lines of text in window W, not counting the mode
5354 line and header line, if any. Do NOT use this for windows on GUI 5434 line and header line, if any. Do NOT use this for windows on GUI
5355 frames; use window_body_height instead. This function is only for 5435 frames; use window_body_height instead. This function is only for
@@ -5366,6 +5446,9 @@ window_internal_height (struct window *w)
5366 if (window_wants_header_line (w)) 5446 if (window_wants_header_line (w))
5367 --ht; 5447 --ht;
5368 5448
5449 if (window_wants_tab_line (w))
5450 --ht;
5451
5369 return ht; 5452 return ht;
5370} 5453}
5371 5454
@@ -5726,8 +5809,9 @@ window_scroll_pixel_based (Lisp_Object window, int n, bool whole, bool noerror)
5726 move_it_to (&it, PT, -1, -1, -1, MOVE_TO_POS); 5809 move_it_to (&it, PT, -1, -1, -1, MOVE_TO_POS);
5727 if (IT_CHARPOS (it) == PT 5810 if (IT_CHARPOS (it) == PT
5728 && it.current_y >= this_scroll_margin 5811 && it.current_y >= this_scroll_margin
5729 && it.current_y <= last_y - WINDOW_HEADER_LINE_HEIGHT (w) 5812 && it.current_y <= last_y - WINDOW_TAB_LINE_HEIGHT (w)
5730 && (NILP (Vscroll_preserve_screen_position) 5813 - WINDOW_HEADER_LINE_HEIGHT (w)
5814 && (NILP (Vscroll_preserve_screen_position)
5731 || EQ (Vscroll_preserve_screen_position, Qt))) 5815 || EQ (Vscroll_preserve_screen_position, Qt)))
5732 /* We found PT at a legitimate height. Leave it alone. */ 5816 /* We found PT at a legitimate height. Leave it alone. */
5733 ; 5817 ;
@@ -5742,7 +5826,8 @@ window_scroll_pixel_based (Lisp_Object window, int n, bool whole, bool noerror)
5742 is necessary because we set it.current_y to 0, above. */ 5826 is necessary because we set it.current_y to 0, above. */
5743 move_it_to (&it, -1, 5827 move_it_to (&it, -1,
5744 window_scroll_pixel_based_preserve_x, 5828 window_scroll_pixel_based_preserve_x,
5745 goal_y - WINDOW_HEADER_LINE_HEIGHT (w), 5829 goal_y - WINDOW_TAB_LINE_HEIGHT (w)
5830 - WINDOW_HEADER_LINE_HEIGHT (w),
5746 -1, MOVE_TO_Y | MOVE_TO_X); 5831 -1, MOVE_TO_Y | MOVE_TO_X);
5747 } 5832 }
5748 5833
@@ -5778,8 +5863,9 @@ window_scroll_pixel_based (Lisp_Object window, int n, bool whole, bool noerror)
5778 /* We subtract WINDOW_HEADER_LINE_HEIGHT because 5863 /* We subtract WINDOW_HEADER_LINE_HEIGHT because
5779 it.y is relative to the bottom of the header 5864 it.y is relative to the bottom of the header
5780 line, see above. */ 5865 line, see above. */
5781 (it.last_visible_y - WINDOW_HEADER_LINE_HEIGHT (w) 5866 (it.last_visible_y - WINDOW_TAB_LINE_HEIGHT (w)
5782 - partial_line_height (&it) - this_scroll_margin - 1), 5867 - WINDOW_HEADER_LINE_HEIGHT (w)
5868 - partial_line_height (&it) - this_scroll_margin - 1),
5783 -1, 5869 -1,
5784 MOVE_TO_POS | MOVE_TO_Y); 5870 MOVE_TO_POS | MOVE_TO_Y);
5785 5871
@@ -5817,13 +5903,15 @@ window_scroll_pixel_based (Lisp_Object window, int n, bool whole, bool noerror)
5817 if (it.what == IT_EOB) 5903 if (it.what == IT_EOB)
5818 partial_p = 5904 partial_p =
5819 it.current_y + it.ascent + it.descent 5905 it.current_y + it.ascent + it.descent
5820 > it.last_visible_y - this_scroll_margin - WINDOW_HEADER_LINE_HEIGHT (w); 5906 > it.last_visible_y - this_scroll_margin
5907 - WINDOW_TAB_LINE_HEIGHT (w) - WINDOW_HEADER_LINE_HEIGHT (w);
5821 else 5908 else
5822 { 5909 {
5823 move_it_by_lines (&it, 1); 5910 move_it_by_lines (&it, 1);
5824 partial_p = 5911 partial_p =
5825 it.current_y 5912 it.current_y
5826 > it.last_visible_y - this_scroll_margin - WINDOW_HEADER_LINE_HEIGHT (w); 5913 > it.last_visible_y - this_scroll_margin
5914 - WINDOW_TAB_LINE_HEIGHT (w) - WINDOW_HEADER_LINE_HEIGHT (w);
5827 } 5915 }
5828 5916
5829 if (charpos == PT && !partial_p 5917 if (charpos == PT && !partial_p
@@ -6370,6 +6458,9 @@ and redisplay normally--don't erase and redraw the frame. */)
6370 /* Invalidate pixel data calculated for all compositions. */ 6458 /* Invalidate pixel data calculated for all compositions. */
6371 for (i = 0; i < n_compositions; i++) 6459 for (i = 0; i < n_compositions; i++)
6372 composition_table[i]->font = NULL; 6460 composition_table[i]->font = NULL;
6461#if defined (HAVE_WINDOW_SYSTEM)
6462 WINDOW_XFRAME (w)->minimize_tab_bar_window_p = 1;
6463#endif
6373#if defined (HAVE_WINDOW_SYSTEM) && ! defined (HAVE_EXT_TOOL_BAR) 6464#if defined (HAVE_WINDOW_SYSTEM) && ! defined (HAVE_EXT_TOOL_BAR)
6374 WINDOW_XFRAME (w)->minimize_tool_bar_window_p = 1; 6465 WINDOW_XFRAME (w)->minimize_tool_bar_window_p = 1;
6375#endif 6466#endif
@@ -6686,13 +6777,13 @@ struct save_window_data
6686 6777
6687 /* We should be able to do without the following two. */ 6778 /* We should be able to do without the following two. */
6688 int frame_cols, frame_lines; 6779 int frame_cols, frame_lines;
6689 /* These two should get eventually replaced by their pixel 6780 /* These three should get eventually replaced by their pixel
6690 counterparts. */ 6781 counterparts. */
6691 int frame_menu_bar_lines, frame_tool_bar_lines; 6782 int frame_menu_bar_lines, frame_tab_bar_lines, frame_tool_bar_lines;
6692 int frame_text_width, frame_text_height; 6783 int frame_text_width, frame_text_height;
6693 /* These are currently unused. We need them as soon as we convert 6784 /* These are currently unused. We need them as soon as we convert
6694 to pixels. */ 6785 to pixels. */
6695 int frame_menu_bar_height, frame_tool_bar_height; 6786 int frame_menu_bar_height, frame_tab_bar_height, frame_tool_bar_height;
6696 } GCALIGNED_STRUCT; 6787 } GCALIGNED_STRUCT;
6697 6788
6698/* This is saved as a Lisp_Vector. */ 6789/* This is saved as a Lisp_Vector. */
@@ -7370,10 +7461,12 @@ saved by this function. */)
7370 data->frame_cols = FRAME_COLS (f); 7461 data->frame_cols = FRAME_COLS (f);
7371 data->frame_lines = FRAME_LINES (f); 7462 data->frame_lines = FRAME_LINES (f);
7372 data->frame_menu_bar_lines = FRAME_MENU_BAR_LINES (f); 7463 data->frame_menu_bar_lines = FRAME_MENU_BAR_LINES (f);
7464 data->frame_tab_bar_lines = FRAME_TAB_BAR_LINES (f);
7373 data->frame_tool_bar_lines = FRAME_TOOL_BAR_LINES (f); 7465 data->frame_tool_bar_lines = FRAME_TOOL_BAR_LINES (f);
7374 data->frame_text_width = FRAME_TEXT_WIDTH (f); 7466 data->frame_text_width = FRAME_TEXT_WIDTH (f);
7375 data->frame_text_height = FRAME_TEXT_HEIGHT (f); 7467 data->frame_text_height = FRAME_TEXT_HEIGHT (f);
7376 data->frame_menu_bar_height = FRAME_MENU_BAR_HEIGHT (f); 7468 data->frame_menu_bar_height = FRAME_MENU_BAR_HEIGHT (f);
7469 data->frame_tab_bar_height = FRAME_TAB_BAR_HEIGHT (f);
7377 data->frame_tool_bar_height = FRAME_TOOL_BAR_HEIGHT (f); 7470 data->frame_tool_bar_height = FRAME_TOOL_BAR_HEIGHT (f);
7378 data->selected_frame = selected_frame; 7471 data->selected_frame = selected_frame;
7379 data->current_window = FRAME_SELECTED_WINDOW (f); 7472 data->current_window = FRAME_SELECTED_WINDOW (f);
@@ -7663,6 +7756,7 @@ set_window_scroll_bars (struct window *w, Lisp_Object width,
7663 7756
7664 /* Don't change anything if new scroll bar won't fit. */ 7757 /* Don't change anything if new scroll bar won't fit. */
7665 if ((WINDOW_PIXEL_HEIGHT (w) 7758 if ((WINDOW_PIXEL_HEIGHT (w)
7759 - WINDOW_TAB_LINE_HEIGHT (w)
7666 - WINDOW_HEADER_LINE_HEIGHT (w) 7760 - WINDOW_HEADER_LINE_HEIGHT (w)
7667 - WINDOW_MODE_LINE_HEIGHT (w) 7761 - WINDOW_MODE_LINE_HEIGHT (w)
7668 - (new_height == -1 ? FRAME_SCROLL_BAR_AREA_HEIGHT (f) : new_height)) 7762 - (new_height == -1 ? FRAME_SCROLL_BAR_AREA_HEIGHT (f) : new_height))
@@ -8086,6 +8180,7 @@ syms_of_window (void)
8086 DEFSYM (Qmark_for_redisplay, "mark-for-redisplay"); 8180 DEFSYM (Qmark_for_redisplay, "mark-for-redisplay");
8087 DEFSYM (Qmode_line_format, "mode-line-format"); 8181 DEFSYM (Qmode_line_format, "mode-line-format");
8088 DEFSYM (Qheader_line_format, "header-line-format"); 8182 DEFSYM (Qheader_line_format, "header-line-format");
8183 DEFSYM (Qtab_line_format, "tab-line-format");
8089 8184
8090 DEFVAR_LISP ("temp-buffer-show-function", Vtemp_buffer_show_function, 8185 DEFVAR_LISP ("temp-buffer-show-function", Vtemp_buffer_show_function,
8091 doc: /* Non-nil means call as function to display a help buffer. 8186 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. */);
8389 defsubr (&Sset_window_redisplay_end_trigger); 8484 defsubr (&Sset_window_redisplay_end_trigger);
8390 defsubr (&Swindow_mode_line_height); 8485 defsubr (&Swindow_mode_line_height);
8391 defsubr (&Swindow_header_line_height); 8486 defsubr (&Swindow_header_line_height);
8487 defsubr (&Swindow_tab_line_height);
8392 defsubr (&Swindow_right_divider_width); 8488 defsubr (&Swindow_right_divider_width);
8393 defsubr (&Swindow_bottom_divider_width); 8489 defsubr (&Swindow_bottom_divider_width);
8394 defsubr (&Swindow_scroll_bar_width); 8490 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
361 /* Effective height of the header line, or -1 if not known. */ 361 /* Effective height of the header line, or -1 if not known. */
362 int header_line_height; 362 int header_line_height;
363 363
364 /* Effective height of the tab line, or -1 if not known. */
365 int tab_line_height;
366
364 /* Z - the buffer position of the last glyph in the current 367 /* Z - the buffer position of the last glyph in the current
365 matrix of W. Only valid if window_end_valid is true. */ 368 matrix of W. Only valid if window_end_valid is true. */
366 ptrdiff_t window_end_pos; 369 ptrdiff_t window_end_pos;
@@ -697,7 +700,7 @@ wset_next_buffers (struct window *w, Lisp_Object val)
697 (WINDOW_LEFT_EDGE_COL (W) + WINDOW_TOTAL_COLS (W)) 700 (WINDOW_LEFT_EDGE_COL (W) + WINDOW_TOTAL_COLS (W))
698 701
699/* Return the canonical frame line at which window W starts. 702/* Return the canonical frame line at which window W starts.
700 This includes a header line, if any. */ 703 This includes a header/tab line, if any. */
701#define WINDOW_TOP_EDGE_LINE(W) (W)->top_line 704#define WINDOW_TOP_EDGE_LINE(W) (W)->top_line
702 705
703/* Return the canonical frame line before which window W ends. 706/* Return the canonical frame line before which window W ends.
@@ -715,7 +718,7 @@ wset_next_buffers (struct window *w, Lisp_Object val)
715 (WINDOW_LEFT_PIXEL_EDGE (W) + WINDOW_PIXEL_WIDTH (W)) 718 (WINDOW_LEFT_PIXEL_EDGE (W) + WINDOW_PIXEL_WIDTH (W))
716 719
717/* Return the top pixel edge at which window W starts. 720/* Return the top pixel edge at which window W starts.
718 This includes a header line, if any. */ 721 This includes a header/tab line, if any. */
719#define WINDOW_TOP_PIXEL_EDGE(W) (W)->pixel_top 722#define WINDOW_TOP_PIXEL_EDGE(W) (W)->pixel_top
720 723
721/* Return the bottom pixel edge before which window W ends. 724/* Return the bottom pixel edge before which window W ends.
@@ -745,6 +748,15 @@ wset_next_buffers (struct window *w, Lisp_Object val)
745#define WINDOW_MENU_BAR_P(W) false 748#define WINDOW_MENU_BAR_P(W) false
746#endif 749#endif
747 750
751/* True if W is a tab bar window. */
752#if defined (HAVE_WINDOW_SYSTEM)
753#define WINDOW_TAB_BAR_P(W) \
754 (WINDOWP (WINDOW_XFRAME (W)->tab_bar_window) \
755 && (W) == XWINDOW (WINDOW_XFRAME (W)->tab_bar_window))
756#else
757#define WINDOW_TAB_BAR_P(W) false
758#endif
759
748/* True if W is a tool bar window. */ 760/* True if W is a tool bar window. */
749#if defined (HAVE_WINDOW_SYSTEM) && ! defined (HAVE_EXT_TOOL_BAR) 761#if defined (HAVE_WINDOW_SYSTEM) && ! defined (HAVE_EXT_TOOL_BAR)
750#define WINDOW_TOOL_BAR_P(W) \ 762#define WINDOW_TOOL_BAR_P(W) \
@@ -756,13 +768,13 @@ wset_next_buffers (struct window *w, Lisp_Object val)
756 768
757/* Return the frame y-position at which window W starts. */ 769/* Return the frame y-position at which window W starts. */
758#define WINDOW_TOP_EDGE_Y(W) \ 770#define WINDOW_TOP_EDGE_Y(W) \
759 (((WINDOW_MENU_BAR_P (W) || WINDOW_TOOL_BAR_P (W)) \ 771 (((WINDOW_MENU_BAR_P (W) || WINDOW_TAB_BAR_P (W) || WINDOW_TOOL_BAR_P (W)) \
760 ? 0 : FRAME_INTERNAL_BORDER_WIDTH (WINDOW_XFRAME (W))) \ 772 ? 0 : FRAME_INTERNAL_BORDER_WIDTH (WINDOW_XFRAME (W))) \
761 + WINDOW_TOP_PIXEL_EDGE (W)) 773 + WINDOW_TOP_PIXEL_EDGE (W))
762 774
763/* Return the frame y-position before which window W ends. */ 775/* Return the frame y-position before which window W ends. */
764#define WINDOW_BOTTOM_EDGE_Y(W) \ 776#define WINDOW_BOTTOM_EDGE_Y(W) \
765 (((WINDOW_MENU_BAR_P (W) || WINDOW_TOOL_BAR_P (W)) \ 777 (((WINDOW_MENU_BAR_P (W) || WINDOW_TAB_BAR_P (W) || WINDOW_TOOL_BAR_P (W)) \
766 ? 0 : FRAME_INTERNAL_BORDER_WIDTH (WINDOW_XFRAME (W))) \ 778 ? 0 : FRAME_INTERNAL_BORDER_WIDTH (WINDOW_XFRAME (W))) \
767 + WINDOW_BOTTOM_PIXEL_EDGE (W)) 779 + WINDOW_BOTTOM_PIXEL_EDGE (W))
768 780
@@ -996,6 +1008,16 @@ wset_next_buffers (struct window *w, Lisp_Object val)
996#define WINDOW_HEADER_LINE_LINES(W) \ 1008#define WINDOW_HEADER_LINE_LINES(W) \
997 window_wants_header_line (W) 1009 window_wants_header_line (W)
998 1010
1011/* Height in pixels of the tab line.
1012 Zero if W doesn't have a tab line. */
1013#define WINDOW_TAB_LINE_HEIGHT(W) \
1014 (window_wants_tab_line (W) \
1015 ? CURRENT_TAB_LINE_HEIGHT (W) \
1016 : 0)
1017
1018#define WINDOW_TAB_LINE_LINES(W) \
1019 window_wants_tab_line (W)
1020
999/* Pixel height of window W without mode line, bottom scroll bar and 1021/* Pixel height of window W without mode line, bottom scroll bar and
1000 bottom divider. */ 1022 bottom divider. */
1001#define WINDOW_BOX_HEIGHT_NO_MODE_LINE(W) \ 1023#define WINDOW_BOX_HEIGHT_NO_MODE_LINE(W) \
@@ -1004,14 +1026,15 @@ wset_next_buffers (struct window *w, Lisp_Object val)
1004 - WINDOW_SCROLL_BAR_AREA_HEIGHT (W) \ 1026 - WINDOW_SCROLL_BAR_AREA_HEIGHT (W) \
1005 - WINDOW_MODE_LINE_HEIGHT (W)) 1027 - WINDOW_MODE_LINE_HEIGHT (W))
1006 1028
1007/* Pixel height of window W without mode and header line and bottom 1029/* Pixel height of window W without mode and header/tab line and bottom
1008 divider. */ 1030 divider. */
1009#define WINDOW_BOX_TEXT_HEIGHT(W) \ 1031#define WINDOW_BOX_TEXT_HEIGHT(W) \
1010 (WINDOW_PIXEL_HEIGHT ((W)) \ 1032 (WINDOW_PIXEL_HEIGHT ((W)) \
1011 - WINDOW_BOTTOM_DIVIDER_WIDTH (W) \ 1033 - WINDOW_BOTTOM_DIVIDER_WIDTH (W) \
1012 - WINDOW_SCROLL_BAR_AREA_HEIGHT (W) \ 1034 - WINDOW_SCROLL_BAR_AREA_HEIGHT (W) \
1013 - WINDOW_MODE_LINE_HEIGHT (W) \ 1035 - WINDOW_MODE_LINE_HEIGHT (W) \
1014 - WINDOW_HEADER_LINE_HEIGHT (W)) 1036 - WINDOW_HEADER_LINE_HEIGHT (W) \
1037 - WINDOW_TAB_LINE_HEIGHT (W))
1015 1038
1016/* Return the frame position where the horizontal scroll bar of window W 1039/* Return the frame position where the horizontal scroll bar of window W
1017 starts. */ 1040 starts. */
@@ -1068,7 +1091,7 @@ extern Lisp_Object minibuf_selected_window;
1068 1091
1069extern Lisp_Object make_window (void); 1092extern Lisp_Object make_window (void);
1070extern Lisp_Object window_from_coordinates (struct frame *, int, int, 1093extern Lisp_Object window_from_coordinates (struct frame *, int, int,
1071 enum window_part *, bool); 1094 enum window_part *, bool, bool);
1072extern void resize_frame_windows (struct frame *, int, bool); 1095extern void resize_frame_windows (struct frame *, int, bool);
1073extern void restore_window_configuration (Lisp_Object); 1096extern void restore_window_configuration (Lisp_Object);
1074extern void delete_all_child_windows (Lisp_Object); 1097extern void delete_all_child_windows (Lisp_Object);
@@ -1158,6 +1181,7 @@ extern bool compare_window_configurations (Lisp_Object, Lisp_Object, bool);
1158extern void mark_window_cursors_off (struct window *); 1181extern void mark_window_cursors_off (struct window *);
1159extern bool window_wants_mode_line (struct window *); 1182extern bool window_wants_mode_line (struct window *);
1160extern bool window_wants_header_line (struct window *); 1183extern bool window_wants_header_line (struct window *);
1184extern bool window_wants_tab_line (struct window *);
1161extern int window_internal_height (struct window *); 1185extern int window_internal_height (struct window *);
1162extern int window_body_width (struct window *w, bool); 1186extern int window_body_width (struct window *w, bool);
1163enum margin_unit { MARGIN_IN_LINES, MARGIN_IN_PIXELS }; 1187enum 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,
947 Lisp_Object); 947 Lisp_Object);
948static const char *decode_mode_spec (struct window *, int, int, Lisp_Object *); 948static const char *decode_mode_spec (struct window *, int, int, Lisp_Object *);
949static void display_menu_bar (struct window *); 949static void display_menu_bar (struct window *);
950static void display_tab_bar (struct window *);
951static void update_tab_bar (struct frame *, bool);
950static ptrdiff_t display_count_lines (ptrdiff_t, ptrdiff_t, ptrdiff_t, 952static ptrdiff_t display_count_lines (ptrdiff_t, ptrdiff_t, ptrdiff_t,
951 ptrdiff_t *); 953 ptrdiff_t *);
952static void pint2str (register char *, register int, register ptrdiff_t); 954static void pint2str (register char *, register int, register ptrdiff_t);
@@ -1080,7 +1082,7 @@ window_box_height (struct window *w)
1080 height -= WINDOW_BOTTOM_DIVIDER_WIDTH (w); 1082 height -= WINDOW_BOTTOM_DIVIDER_WIDTH (w);
1081 height -= WINDOW_SCROLL_BAR_AREA_HEIGHT (w); 1083 height -= WINDOW_SCROLL_BAR_AREA_HEIGHT (w);
1082 1084
1083 /* Note: the code below that determines the mode-line/header-line 1085 /* Note: the code below that determines the mode-line/header-line/tab-line
1084 height is essentially the same as that contained in the macro 1086 height is essentially the same as that contained in the macro
1085 CURRENT_{MODE,HEADER}_LINE_HEIGHT, except that it checks whether 1087 CURRENT_{MODE,HEADER}_LINE_HEIGHT, except that it checks whether
1086 the appropriate glyph row has its `mode_line_p' flag set, 1088 the appropriate glyph row has its `mode_line_p' flag set,
@@ -1098,6 +1100,18 @@ window_box_height (struct window *w)
1098 height -= estimate_mode_line_height (f, CURRENT_MODE_LINE_FACE_ID (w)); 1100 height -= estimate_mode_line_height (f, CURRENT_MODE_LINE_FACE_ID (w));
1099 } 1101 }
1100 1102
1103 if (window_wants_tab_line (w))
1104 {
1105 struct glyph_row *tl_row
1106 = (w->current_matrix && w->current_matrix->rows
1107 ? MATRIX_TAB_LINE_ROW (w->current_matrix)
1108 : 0);
1109 if (tl_row && tl_row->mode_line_p)
1110 height -= tl_row->height;
1111 else
1112 height -= estimate_mode_line_height (f, TAB_LINE_FACE_ID);
1113 }
1114
1101 if (window_wants_header_line (w)) 1115 if (window_wants_header_line (w))
1102 { 1116 {
1103 struct glyph_row *hl_row 1117 struct glyph_row *hl_row
@@ -1210,6 +1224,8 @@ window_box (struct window *w, enum glyph_row_area area, int *box_x,
1210 if (box_y) 1224 if (box_y)
1211 { 1225 {
1212 *box_y = WINDOW_TOP_EDGE_Y (w); 1226 *box_y = WINDOW_TOP_EDGE_Y (w);
1227 if (window_wants_tab_line (w))
1228 *box_y += CURRENT_TAB_LINE_HEIGHT (w);
1213 if (window_wants_header_line (w)) 1229 if (window_wants_header_line (w))
1214 *box_y += CURRENT_HEADER_LINE_HEIGHT (w); 1230 *box_y += CURRENT_HEADER_LINE_HEIGHT (w);
1215 } 1231 }
@@ -1435,13 +1451,14 @@ pos_visible_p (struct window *w, ptrdiff_t charpos, int *x, int *y,
1435 1451
1436 /* Some Lisp hook could call us in the middle of redisplaying this 1452 /* Some Lisp hook could call us in the middle of redisplaying this
1437 very window. If, by some bad luck, we are retrying redisplay 1453 very window. If, by some bad luck, we are retrying redisplay
1438 because we found that the mode-line height and/or header-line 1454 because we found that the mode-line height and/or tab/header-line
1439 height needs to be updated, the assignment of mode_line_height 1455 height needs to be updated, the assignment of mode_line_height
1440 and header_line_height below could disrupt that, due to the 1456 and header_line_height below could disrupt that, due to the
1441 selected/nonselected window dance during mode-line display, and 1457 selected/nonselected window dance during mode-line display, and
1442 we could infloop. Avoid that. */ 1458 we could infloop. Avoid that. */
1443 int prev_mode_line_height = w->mode_line_height; 1459 int prev_mode_line_height = w->mode_line_height;
1444 int prev_header_line_height = w->header_line_height; 1460 int prev_header_line_height = w->header_line_height;
1461 int prev_tab_line_height = w->tab_line_height;
1445 /* Compute exact mode line heights. */ 1462 /* Compute exact mode line heights. */
1446 if (window_wants_mode_line (w)) 1463 if (window_wants_mode_line (w))
1447 { 1464 {
@@ -1455,6 +1472,18 @@ pos_visible_p (struct window *w, ptrdiff_t charpos, int *x, int *y,
1455 : window_mode_line_format); 1472 : window_mode_line_format);
1456 } 1473 }
1457 1474
1475 if (window_wants_tab_line (w))
1476 {
1477 Lisp_Object window_tab_line_format
1478 = window_parameter (w, Qtab_line_format);
1479
1480 w->tab_line_height
1481 = display_mode_line (w, TAB_LINE_FACE_ID,
1482 NILP (window_tab_line_format)
1483 ? BVAR (current_buffer, tab_line_format)
1484 : window_tab_line_format);
1485 }
1486
1458 if (window_wants_header_line (w)) 1487 if (window_wants_header_line (w))
1459 { 1488 {
1460 Lisp_Object window_header_line_format 1489 Lisp_Object window_header_line_format
@@ -1511,7 +1540,7 @@ pos_visible_p (struct window *w, ptrdiff_t charpos, int *x, int *y,
1511 glyph. */ 1540 glyph. */
1512 int top_x = it.current_x; 1541 int top_x = it.current_x;
1513 int top_y = it.current_y; 1542 int top_y = it.current_y;
1514 int window_top_y = WINDOW_HEADER_LINE_HEIGHT (w); 1543 int window_top_y = WINDOW_TAB_LINE_HEIGHT (w) + WINDOW_HEADER_LINE_HEIGHT (w);
1515 int bottom_y; 1544 int bottom_y;
1516 struct it save_it; 1545 struct it save_it;
1517 void *save_it_data = NULL; 1546 void *save_it_data = NULL;
@@ -1779,7 +1808,8 @@ pos_visible_p (struct window *w, ptrdiff_t charpos, int *x, int *y,
1779 - it.last_visible_y)); 1808 - it.last_visible_y));
1780 *rowh = max (0, (min (it2.current_y + it2.max_ascent + it2.max_descent, 1809 *rowh = max (0, (min (it2.current_y + it2.max_ascent + it2.max_descent,
1781 it.last_visible_y) 1810 it.last_visible_y)
1782 - max (it2.current_y, 1811 - max (max (it2.current_y,
1812 WINDOW_TAB_LINE_HEIGHT (w)),
1783 WINDOW_HEADER_LINE_HEIGHT (w)))); 1813 WINDOW_HEADER_LINE_HEIGHT (w))));
1784 *vpos = it2.vpos; 1814 *vpos = it2.vpos;
1785 if (it2.bidi_it.paragraph_dir == R2L) 1815 if (it2.bidi_it.paragraph_dir == R2L)
@@ -1820,6 +1850,7 @@ pos_visible_p (struct window *w, ptrdiff_t charpos, int *x, int *y,
1820 /* Restore potentially overwritten values. */ 1850 /* Restore potentially overwritten values. */
1821 w->mode_line_height = prev_mode_line_height; 1851 w->mode_line_height = prev_mode_line_height;
1822 w->header_line_height = prev_header_line_height; 1852 w->header_line_height = prev_header_line_height;
1853 w->tab_line_height = prev_tab_line_height;
1823 1854
1824 return visible_p; 1855 return visible_p;
1825} 1856}
@@ -2212,7 +2243,7 @@ get_glyph_string_clip_rects (struct glyph_string *s, NativeRectangle *rects, int
2212 intentionally draws over other lines. */ 2243 intentionally draws over other lines. */
2213 if (s->for_overlaps) 2244 if (s->for_overlaps)
2214 { 2245 {
2215 r.y = WINDOW_HEADER_LINE_HEIGHT (s->w); 2246 r.y = WINDOW_TAB_LINE_HEIGHT (s->w) + WINDOW_HEADER_LINE_HEIGHT (s->w);
2216 r.height = window_text_bottom_y (s->w) - r.y; 2247 r.height = window_text_bottom_y (s->w) - r.y;
2217 2248
2218 /* Alas, the above simple strategy does not work for the 2249 /* 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
2239 partially visible lines at the top of a window. */ 2270 partially visible lines at the top of a window. */
2240 if (!s->row->full_width_p 2271 if (!s->row->full_width_p
2241 && MATRIX_ROW_PARTIALLY_VISIBLE_AT_TOP_P (s->w, s->row)) 2272 && MATRIX_ROW_PARTIALLY_VISIBLE_AT_TOP_P (s->w, s->row))
2242 r.y = WINDOW_HEADER_LINE_HEIGHT (s->w); 2273 r.y = WINDOW_TAB_LINE_HEIGHT (s->w) + WINDOW_HEADER_LINE_HEIGHT (s->w);
2243 else 2274 else
2244 r.y = max (0, s->row->y); 2275 r.y = max (0, s->row->y);
2245 } 2276 }
@@ -2416,7 +2447,7 @@ get_phys_cursor_geometry (struct window *w, struct glyph_row *row,
2416 h = min (h, row->height); 2447 h = min (h, row->height);
2417 h0 = min (h0, ascent + glyph->descent); 2448 h0 = min (h0, ascent + glyph->descent);
2418 2449
2419 y0 = WINDOW_HEADER_LINE_HEIGHT (w); 2450 y0 = WINDOW_TAB_LINE_HEIGHT (w) + WINDOW_HEADER_LINE_HEIGHT (w);
2420 if (y < y0) 2451 if (y < y0)
2421 { 2452 {
2422 h = max (h - (y0 - y) + 1, h0); 2453 h = max (h - (y0 - y) + 1, h0);
@@ -2460,7 +2491,7 @@ remember_mouse_glyph (struct frame *f, int gx, int gy, NativeRectangle *rect)
2460 goto virtual_glyph; 2491 goto virtual_glyph;
2461 } 2492 }
2462 else if (!f->glyphs_initialized_p 2493 else if (!f->glyphs_initialized_p
2463 || (window = window_from_coordinates (f, gx, gy, &part, false), 2494 || (window = window_from_coordinates (f, gx, gy, &part, false, false),
2464 NILP (window))) 2495 NILP (window)))
2465 { 2496 {
2466 width = FRAME_SMALLEST_CHAR_WIDTH (f); 2497 width = FRAME_SMALLEST_CHAR_WIDTH (f);
@@ -2495,11 +2526,14 @@ remember_mouse_glyph (struct frame *f, int gx, int gy, NativeRectangle *rect)
2495 area = RIGHT_MARGIN_AREA; 2526 area = RIGHT_MARGIN_AREA;
2496 goto text_glyph; 2527 goto text_glyph;
2497 2528
2529 case ON_TAB_LINE:
2498 case ON_HEADER_LINE: 2530 case ON_HEADER_LINE:
2499 case ON_MODE_LINE: 2531 case ON_MODE_LINE:
2500 gr = (part == ON_HEADER_LINE 2532 gr = (part == ON_TAB_LINE
2501 ? MATRIX_HEADER_LINE_ROW (w->current_matrix) 2533 ? MATRIX_TAB_LINE_ROW (w->current_matrix)
2502 : MATRIX_MODE_LINE_ROW (w->current_matrix)); 2534 : (part == ON_HEADER_LINE
2535 ? MATRIX_HEADER_LINE_ROW (w->current_matrix)
2536 : MATRIX_MODE_LINE_ROW (w->current_matrix)));
2503 gy = gr->y; 2537 gy = gr->y;
2504 area = TEXT_AREA; 2538 area = TEXT_AREA;
2505 goto text_glyph_row_found; 2539 goto text_glyph_row_found;
@@ -2545,7 +2579,8 @@ remember_mouse_glyph (struct frame *f, int gx, int gy, NativeRectangle *rect)
2545 gx += (x / width) * width; 2579 gx += (x / width) * width;
2546 } 2580 }
2547 2581
2548 if (part != ON_MODE_LINE && part != ON_HEADER_LINE) 2582 if (part != ON_MODE_LINE && part != ON_HEADER_LINE
2583 && part != ON_TAB_LINE)
2549 { 2584 {
2550 gx += window_box_left_offset (w, area); 2585 gx += window_box_left_offset (w, area);
2551 /* Don't expand over the modeline to make sure the vertical 2586 /* 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)
2560 gx = (x / width) * width; 2595 gx = (x / width) * width;
2561 y -= gy; 2596 y -= gy;
2562 gy += (y / height) * height; 2597 gy += (y / height) * height;
2563 if (part != ON_MODE_LINE && part != ON_HEADER_LINE) 2598 if (part != ON_MODE_LINE && part != ON_HEADER_LINE
2599 && part != ON_TAB_LINE)
2564 /* See comment above. */ 2600 /* See comment above. */
2565 height = min (height, 2601 height = min (height,
2566 max (0, WINDOW_BOX_HEIGHT_NO_MODE_LINE (w) - gy)); 2602 max (0, WINDOW_BOX_HEIGHT_NO_MODE_LINE (w) - gy));
@@ -2920,8 +2956,14 @@ init_iterator (struct it *it, struct window *w,
2920 if (base_face_id == MODE_LINE_FACE_ID 2956 if (base_face_id == MODE_LINE_FACE_ID
2921 || base_face_id == MODE_LINE_INACTIVE_FACE_ID) 2957 || base_face_id == MODE_LINE_INACTIVE_FACE_ID)
2922 row = MATRIX_MODE_LINE_ROW (w->desired_matrix); 2958 row = MATRIX_MODE_LINE_ROW (w->desired_matrix);
2959 else if (base_face_id == TAB_LINE_FACE_ID)
2960 row = MATRIX_TAB_LINE_ROW (w->desired_matrix);
2923 else if (base_face_id == HEADER_LINE_FACE_ID) 2961 else if (base_face_id == HEADER_LINE_FACE_ID)
2924 row = MATRIX_HEADER_LINE_ROW (w->desired_matrix); 2962 {
2963 /* Header line row depends on whether tab line is enabled. */
2964 w->desired_matrix->tab_line_p = window_wants_tab_line (w);
2965 row = MATRIX_HEADER_LINE_ROW (w->desired_matrix);
2966 }
2925 } 2967 }
2926 2968
2927 /* Clear IT, and set it->object and other IT's Lisp objects to Qnil. 2969 /* 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,
3097 it->last_visible_x -= it->continuation_pixel_width; 3139 it->last_visible_x -= it->continuation_pixel_width;
3098 } 3140 }
3099 3141
3142 it->tab_line_p = window_wants_tab_line (w);
3100 it->header_line_p = window_wants_header_line (w); 3143 it->header_line_p = window_wants_header_line (w);
3101 body_height = WINDOW_HEADER_LINE_HEIGHT (w); 3144 body_height = WINDOW_TAB_LINE_HEIGHT (w) + WINDOW_HEADER_LINE_HEIGHT (w);
3102 it->current_y = body_height + w->vscroll; 3145 it->current_y = body_height + w->vscroll;
3103 } 3146 }
3104 3147
@@ -3201,7 +3244,7 @@ void
3201start_display (struct it *it, struct window *w, struct text_pos pos) 3244start_display (struct it *it, struct window *w, struct text_pos pos)
3202{ 3245{
3203 struct glyph_row *row; 3246 struct glyph_row *row;
3204 bool first_vpos = window_wants_header_line (w); 3247 int first_vpos = window_wants_tab_line (w) + window_wants_header_line (w);
3205 3248
3206 row = w->desired_matrix->rows + first_vpos; 3249 row = w->desired_matrix->rows + first_vpos;
3207 init_iterator (it, w, CHARPOS (pos), BYTEPOS (pos), row, DEFAULT_FACE_ID); 3250 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. */)
10413 /* Subtract height of header-line which was counted automatically by 10456 /* Subtract height of header-line which was counted automatically by
10414 start_display. */ 10457 start_display. */
10415 y = it.current_y + it.max_ascent + it.max_descent 10458 y = it.current_y + it.max_ascent + it.max_descent
10416 - WINDOW_HEADER_LINE_HEIGHT (w); 10459 - WINDOW_TAB_LINE_HEIGHT (w) - WINDOW_HEADER_LINE_HEIGHT (w);
10417 /* Don't return more than Y-LIMIT. */ 10460 /* Don't return more than Y-LIMIT. */
10418 if (y > max_y) 10461 if (y > max_y)
10419 y = max_y; 10462 y = max_y;
10420 10463
10464 if (EQ (mode_and_header_line, Qtab_line)
10465 || EQ (mode_and_header_line, Qt))
10466 /* Re-add height of tab-line as requested. */
10467 y = y + WINDOW_TAB_LINE_HEIGHT (w);
10468
10421 if (EQ (mode_and_header_line, Qheader_line) 10469 if (EQ (mode_and_header_line, Qheader_line)
10422 || EQ (mode_and_header_line, Qt)) 10470 || EQ (mode_and_header_line, Qt))
10423 /* Re-add height of header-line as requested. */ 10471 /* Re-add height of header-line as requested. */
@@ -12363,6 +12411,7 @@ prepare_menu_bars (void)
12363 continue; 12411 continue;
12364 12412
12365 menu_bar_hooks_run = update_menu_bar (f, false, menu_bar_hooks_run); 12413 menu_bar_hooks_run = update_menu_bar (f, false, menu_bar_hooks_run);
12414 update_tab_bar (f, false);
12366#ifdef HAVE_WINDOW_SYSTEM 12415#ifdef HAVE_WINDOW_SYSTEM
12367 update_tool_bar (f, false); 12416 update_tool_bar (f, false);
12368#endif 12417#endif
@@ -12374,6 +12423,7 @@ prepare_menu_bars (void)
12374 { 12423 {
12375 struct frame *sf = SELECTED_FRAME (); 12424 struct frame *sf = SELECTED_FRAME ();
12376 update_menu_bar (sf, true, false); 12425 update_menu_bar (sf, true, false);
12426 update_tab_bar (sf, true);
12377#ifdef HAVE_WINDOW_SYSTEM 12427#ifdef HAVE_WINDOW_SYSTEM
12378 update_tool_bar (sf, true); 12428 update_tool_bar (sf, true);
12379#endif 12429#endif
@@ -12490,8 +12540,10 @@ update_menu_bar (struct frame *f, bool save_match_data, bool hooks_run)
12490 return hooks_run; 12540 return hooks_run;
12491} 12541}
12492 12542
12543
12544
12493/*********************************************************************** 12545/***********************************************************************
12494 Tool-bars 12546 Tab-bars
12495 ***********************************************************************/ 12547 ***********************************************************************/
12496 12548
12497#ifdef HAVE_WINDOW_SYSTEM 12549#ifdef HAVE_WINDOW_SYSTEM
@@ -12510,6 +12562,871 @@ fast_set_selected_frame (Lisp_Object frame)
12510 } 12562 }
12511} 12563}
12512 12564
12565#endif /* HAVE_WINDOW_SYSTEM */
12566
12567/* Update the tab-bar item list for frame F. This has to be done
12568 before we start to fill in any display lines. Called from
12569 prepare_menu_bars. If SAVE_MATCH_DATA, we must save
12570 and restore it here. */
12571
12572static void
12573update_tab_bar (struct frame *f, bool save_match_data)
12574{
12575 bool do_update = false;
12576
12577#ifdef HAVE_WINDOW_SYSTEM
12578 if (FRAME_WINDOW_P (f) && WINDOWP (f->tab_bar_window)) {
12579 if (WINDOW_TOTAL_LINES (XWINDOW (f->tab_bar_window)) > 0)
12580 do_update = true;
12581 }
12582 else
12583#endif
12584 if (FRAME_TAB_BAR_LINES (f) > 0)
12585 do_update = true;
12586
12587 if (do_update)
12588 {
12589 Lisp_Object window;
12590 struct window *w;
12591
12592 window = FRAME_SELECTED_WINDOW (f);
12593 w = XWINDOW (window);
12594
12595 /* If the user has switched buffers or windows, we need to
12596 recompute to reflect the new bindings. But we'll
12597 recompute when update_mode_lines is set too; that means
12598 that people can use force-mode-line-update to request
12599 that the menu bar be recomputed. The adverse effect on
12600 the rest of the redisplay algorithm is about the same as
12601 windows_or_buffers_changed anyway. */
12602 if (windows_or_buffers_changed
12603 || w->update_mode_line
12604 || update_mode_lines
12605 || window_buffer_changed (w))
12606 {
12607 struct buffer *prev = current_buffer;
12608 ptrdiff_t count = SPECPDL_INDEX ();
12609 Lisp_Object new_tab_bar;
12610 int new_n_tab_bar;
12611
12612 /* Set current_buffer to the buffer of the selected
12613 window of the frame, so that we get the right local
12614 keymaps. */
12615 set_buffer_internal_1 (XBUFFER (w->contents));
12616
12617 /* Save match data, if we must. */
12618 if (save_match_data)
12619 record_unwind_save_match_data ();
12620
12621 /* Make sure that we don't accidentally use bogus keymaps. */
12622 if (NILP (Voverriding_local_map_menu_flag))
12623 {
12624 specbind (Qoverriding_terminal_local_map, Qnil);
12625 specbind (Qoverriding_local_map, Qnil);
12626 }
12627
12628 /* We must temporarily set the selected frame to this frame
12629 before calling tab_bar_items, because the calculation of
12630 the tab-bar keymap uses the selected frame (see
12631 `tab-bar-make-keymap' in tab-bar.el). */
12632 eassert (EQ (selected_window,
12633 /* Since we only explicitly preserve selected_frame,
12634 check that selected_window would be redundant. */
12635 XFRAME (selected_frame)->selected_window));
12636#ifdef HAVE_WINDOW_SYSTEM
12637 Lisp_Object frame;
12638 record_unwind_protect (fast_set_selected_frame, selected_frame);
12639 XSETFRAME (frame, f);
12640 fast_set_selected_frame (frame);
12641#endif
12642
12643 /* Build desired tab-bar items from keymaps. */
12644 new_tab_bar
12645 = tab_bar_items (Fcopy_sequence (f->tab_bar_items),
12646 &new_n_tab_bar);
12647
12648 /* Redisplay the tab-bar if we changed it. */
12649 if (new_n_tab_bar != f->n_tab_bar_items
12650 || NILP (Fequal (new_tab_bar, f->tab_bar_items)))
12651 {
12652 /* Redisplay that happens asynchronously due to an expose event
12653 may access f->tab_bar_items. Make sure we update both
12654 variables within BLOCK_INPUT so no such event interrupts. */
12655 block_input ();
12656 fset_tab_bar_items (f, new_tab_bar);
12657 f->n_tab_bar_items = new_n_tab_bar;
12658 w->update_mode_line = true;
12659 unblock_input ();
12660 }
12661
12662 unbind_to (count, Qnil);
12663 set_buffer_internal_1 (prev);
12664 }
12665 }
12666}
12667
12668/* Redisplay the tab bar in the frame for window W.
12669
12670 The tab bar of X frames that don't have X toolkit support is
12671 displayed in a special window W->frame->tab_bar_window.
12672
12673 The tab bar of terminal frames is treated specially as far as
12674 glyph matrices are concerned. Tab bar lines are not part of
12675 windows, so the update is done directly on the frame matrix rows
12676 for the tab bar. */
12677
12678static void
12679display_tab_bar (struct window *w)
12680{
12681 struct frame *f = XFRAME (WINDOW_FRAME (w));
12682 struct it it;
12683 Lisp_Object items;
12684 int i;
12685
12686 /* Don't do all this for graphical frames. */
12687#ifdef HAVE_NTGUI
12688 if (FRAME_W32_P (f))
12689 return;
12690#endif
12691#if defined (USE_X_TOOLKIT) || defined (USE_GTK)
12692 if (FRAME_X_P (f))
12693 return;
12694#endif
12695
12696#ifdef HAVE_NS
12697 if (FRAME_NS_P (f))
12698 return;
12699#endif /* HAVE_NS */
12700
12701#if defined (USE_X_TOOLKIT) || defined (USE_GTK)
12702 eassert (!FRAME_WINDOW_P (f));
12703 init_iterator (&it, w, -1, -1, f->desired_matrix->rows
12704 + (FRAME_MENU_BAR_LINES (f) > 0 ? 1 : 0),
12705 TAB_BAR_FACE_ID);
12706 it.first_visible_x = 0;
12707 it.last_visible_x = FRAME_PIXEL_WIDTH (f);
12708#elif defined (HAVE_X_WINDOWS) /* X without toolkit. */
12709 if (FRAME_WINDOW_P (f))
12710 {
12711 /* Tab bar lines are displayed in the desired matrix of the
12712 dummy window tab_bar_window. */
12713 struct window *tab_w;
12714 tab_w = XWINDOW (f->tab_bar_window);
12715 init_iterator (&it, tab_w, -1, -1, tab_w->desired_matrix->rows,
12716 TAB_BAR_FACE_ID);
12717 it.first_visible_x = 0;
12718 it.last_visible_x = FRAME_PIXEL_WIDTH (f);
12719 }
12720 else
12721#endif /* not USE_X_TOOLKIT and not USE_GTK */
12722 {
12723 /* This is a TTY frame, i.e. character hpos/vpos are used as
12724 pixel x/y. */
12725 init_iterator (&it, w, -1, -1, f->desired_matrix->rows
12726 + (FRAME_MENU_BAR_LINES (f) > 0 ? 1 : 0),
12727 TAB_BAR_FACE_ID);
12728 it.first_visible_x = 0;
12729 it.last_visible_x = FRAME_COLS (f);
12730 }
12731
12732 /* FIXME: This should be controlled by a user option. See the
12733 comments in redisplay_tool_bar and display_mode_line about
12734 this. */
12735 it.paragraph_embedding = L2R;
12736
12737 /* Clear all rows of the tab bar. */
12738 for (i = 0; i < FRAME_TAB_BAR_LINES (f); ++i)
12739 {
12740 struct glyph_row *row = it.glyph_row + i;
12741 clear_glyph_row (row);
12742 row->enabled_p = true;
12743 row->full_width_p = true;
12744 row->reversed_p = false;
12745 }
12746
12747 /* Display all items of the tab bar. */
12748 items = it.f->tab_bar_items;
12749 for (i = 0; i < it.f->n_tab_bar_items; ++i)
12750 {
12751 Lisp_Object string;
12752
12753 /* Stop at nil string. */
12754 string = AREF (items, i * TAB_BAR_ITEM_NSLOTS + TAB_BAR_ITEM_CAPTION);
12755 if (NILP (string))
12756 break;
12757
12758 if (it.current_x < it.last_visible_x)
12759 display_string (NULL, string, Qnil, 0, 0, &it,
12760 SCHARS (string), 0, 0, STRING_MULTIBYTE (string));
12761 }
12762
12763 /* Fill out the line with spaces. */
12764 if (it.current_x < it.last_visible_x)
12765 display_string ("", Qnil, Qnil, 0, 0, &it, -1, 0, 0, -1);
12766
12767 /* Compute the total height of the lines. */
12768 compute_line_metrics (&it);
12769}
12770
12771#ifdef HAVE_WINDOW_SYSTEM
12772
12773/* Set F->desired_tab_bar_string to a Lisp string representing frame
12774 F's desired tab-bar contents. F->tab_bar_items must have
12775 been set up previously by calling prepare_menu_bars. */
12776
12777static void
12778build_desired_tab_bar_string (struct frame *f)
12779{
12780 int i;
12781 Lisp_Object caption;
12782
12783 caption = Qnil;
12784
12785 /* Prepare F->desired_tab_bar_string. Make a new string. */
12786 fset_desired_tab_bar_string (f, build_string (""));
12787
12788 /* Put a `display' property on the string for the captions to display,
12789 put a `menu_item' property on tab-bar items with a value that
12790 is the index of the item in F's tab-bar item vector. */
12791 for (i = 0; i < f->n_tab_bar_items; ++i)
12792 {
12793#define PROP(IDX) \
12794 AREF (f->tab_bar_items, i * TAB_BAR_ITEM_NSLOTS + (IDX))
12795
12796 caption = Fcopy_sequence (PROP (TAB_BAR_ITEM_CAPTION));
12797
12798 /* Put a `display' text property on the string for the caption to
12799 display. Put a `menu-item' property on the string that gives
12800 the start of this item's properties in the tab-bar items
12801 vector. */
12802 AUTO_LIST2 (props, Qmenu_item, make_fixnum (i * TAB_BAR_ITEM_NSLOTS));
12803
12804 Fadd_text_properties (make_fixnum (0), make_fixnum (SCHARS (caption)),
12805 props, caption);
12806
12807 f->desired_tab_bar_string =
12808 concat2 (f->desired_tab_bar_string, caption);
12809
12810#undef PROP
12811 }
12812}
12813
12814
12815/* Display one line of the tab-bar of frame IT->f.
12816
12817 HEIGHT specifies the desired height of the tab-bar line.
12818 If the actual height of the glyph row is less than HEIGHT, the
12819 row's height is increased to HEIGHT, and the icons are centered
12820 vertically in the new height.
12821
12822 If HEIGHT is -1, we are counting needed tab-bar lines, so don't
12823 count a final empty row in case the tab-bar width exactly matches
12824 the window width.
12825*/
12826
12827static void
12828display_tab_bar_line (struct it *it, int height)
12829{
12830 struct glyph_row *row = it->glyph_row;
12831 int max_x = it->last_visible_x;
12832 struct glyph *last;
12833
12834 /* Don't extend on a previously drawn tab bar items (Bug#16058). */
12835 clear_glyph_row (row);
12836 row->enabled_p = true;
12837 row->y = it->current_y;
12838
12839 /* Note that this isn't made use of if the face hasn't a box,
12840 so there's no need to check the face here. */
12841 it->start_of_box_run_p = true;
12842
12843 while (it->current_x < max_x)
12844 {
12845 int x, n_glyphs_before, i, nglyphs;
12846 struct it it_before;
12847
12848 /* Get the next display element. */
12849 if (!get_next_display_element (it))
12850 {
12851 /* Don't count empty row if we are counting needed tab-bar lines. */
12852 if (height < 0 && !it->hpos)
12853 return;
12854 break;
12855 }
12856
12857 /* Produce glyphs. */
12858 n_glyphs_before = row->used[TEXT_AREA];
12859 it_before = *it;
12860
12861 PRODUCE_GLYPHS (it);
12862
12863 nglyphs = row->used[TEXT_AREA] - n_glyphs_before;
12864 i = 0;
12865 x = it_before.current_x;
12866 while (i < nglyphs)
12867 {
12868 struct glyph *glyph = row->glyphs[TEXT_AREA] + n_glyphs_before + i;
12869
12870 if (x + glyph->pixel_width > max_x)
12871 {
12872 /* Glyph doesn't fit on line. Backtrack. */
12873 row->used[TEXT_AREA] = n_glyphs_before;
12874 *it = it_before;
12875 /* If this is the only glyph on this line, it will never fit on the
12876 tab-bar, so skip it. But ensure there is at least one glyph,
12877 so we don't accidentally disable the tab-bar. */
12878 if (n_glyphs_before == 0
12879 && (it->vpos > 0 || IT_STRING_CHARPOS (*it) < it->end_charpos-1))
12880 break;
12881 goto out;
12882 }
12883
12884 ++it->hpos;
12885 x += glyph->pixel_width;
12886 ++i;
12887 }
12888
12889 /* Stop at line end. */
12890 if (ITERATOR_AT_END_OF_LINE_P (it))
12891 break;
12892
12893 set_iterator_to_next (it, true);
12894 }
12895
12896 out:;
12897
12898 row->displays_text_p = row->used[TEXT_AREA] != 0;
12899
12900 /* Use default face for the border below the tab bar.
12901
12902 FIXME: When auto-resize-tab-bars is grow-only, there is
12903 no additional border below the possibly empty tab-bar lines.
12904 So to make the extra empty lines look "normal", we have to
12905 use the tab-bar face for the border too. */
12906 if (!MATRIX_ROW_DISPLAYS_TEXT_P (row)
12907 && !EQ (Vauto_resize_tab_bars, Qgrow_only))
12908 it->face_id = DEFAULT_FACE_ID;
12909
12910 extend_face_to_end_of_line (it);
12911 last = row->glyphs[TEXT_AREA] + row->used[TEXT_AREA] - 1;
12912 last->right_box_line_p = true;
12913 if (last == row->glyphs[TEXT_AREA])
12914 last->left_box_line_p = true;
12915
12916 /* Make line the desired height and center it vertically. */
12917 if ((height -= it->max_ascent + it->max_descent) > 0)
12918 {
12919 /* Don't add more than one line height. */
12920 height %= FRAME_LINE_HEIGHT (it->f);
12921 it->max_ascent += height / 2;
12922 it->max_descent += (height + 1) / 2;
12923 }
12924
12925 compute_line_metrics (it);
12926
12927 /* If line is empty, make it occupy the rest of the tab-bar. */
12928 if (!MATRIX_ROW_DISPLAYS_TEXT_P (row))
12929 {
12930 row->height = row->phys_height = it->last_visible_y - row->y;
12931 row->visible_height = row->height;
12932 row->ascent = row->phys_ascent = 0;
12933 row->extra_line_spacing = 0;
12934 }
12935
12936 row->full_width_p = true;
12937 row->continued_p = false;
12938 row->truncated_on_left_p = false;
12939 row->truncated_on_right_p = false;
12940
12941 it->current_x = it->hpos = 0;
12942 it->current_y += row->height;
12943 ++it->vpos;
12944 ++it->glyph_row;
12945}
12946
12947
12948/* Value is the number of pixels needed to make all tab-bar items of
12949 frame F visible. The actual number of glyph rows needed is
12950 returned in *N_ROWS if non-NULL. */
12951static int
12952tab_bar_height (struct frame *f, int *n_rows, bool pixelwise)
12953{
12954 struct window *w = XWINDOW (f->tab_bar_window);
12955 struct it it;
12956 /* tab_bar_height is called from redisplay_tab_bar after building
12957 the desired matrix, so use (unused) mode-line row as temporary row to
12958 avoid destroying the first tab-bar row. */
12959 struct glyph_row *temp_row = MATRIX_MODE_LINE_ROW (w->desired_matrix);
12960
12961 /* Initialize an iterator for iteration over
12962 F->desired_tab_bar_string in the tab-bar window of frame F. */
12963 init_iterator (&it, w, -1, -1, temp_row, TAB_BAR_FACE_ID);
12964 temp_row->reversed_p = false;
12965 it.first_visible_x = 0;
12966 it.last_visible_x = WINDOW_PIXEL_WIDTH (w);
12967 reseat_to_string (&it, NULL, f->desired_tab_bar_string,
12968 0, 0, 0, STRING_MULTIBYTE (f->desired_tab_bar_string));
12969 it.paragraph_embedding = L2R;
12970
12971 while (!ITERATOR_AT_END_P (&it))
12972 {
12973 clear_glyph_row (temp_row);
12974 it.glyph_row = temp_row;
12975 display_tab_bar_line (&it, -1);
12976 }
12977 clear_glyph_row (temp_row);
12978
12979 /* f->n_tab_bar_rows == 0 means "unknown"; -1 means no tab-bar. */
12980 if (n_rows)
12981 *n_rows = it.vpos > 0 ? it.vpos : -1;
12982
12983 if (pixelwise)
12984 return it.current_y;
12985 else
12986 return (it.current_y + FRAME_LINE_HEIGHT (f) - 1) / FRAME_LINE_HEIGHT (f);
12987}
12988
12989DEFUN ("tab-bar-height", Ftab_bar_height, Stab_bar_height,
12990 0, 2, 0,
12991 doc: /* Return the number of lines occupied by the tab bar of FRAME.
12992If FRAME is nil or omitted, use the selected frame. Optional argument
12993PIXELWISE non-nil means return the height of the tab bar in pixels. */)
12994 (Lisp_Object frame, Lisp_Object pixelwise)
12995{
12996 int height = 0;
12997
12998 struct frame *f = decode_any_frame (frame);
12999
13000 if (WINDOWP (f->tab_bar_window)
13001 && WINDOW_PIXEL_HEIGHT (XWINDOW (f->tab_bar_window)) > 0)
13002 {
13003 update_tab_bar (f, true);
13004 if (f->n_tab_bar_items)
13005 {
13006 build_desired_tab_bar_string (f);
13007 height = tab_bar_height (f, NULL, !NILP (pixelwise));
13008 }
13009 }
13010
13011 return make_fixnum (height);
13012}
13013
13014
13015/* Display the tab-bar of frame F. Value is true if tab-bar's
13016 height should be changed. */
13017static bool
13018redisplay_tab_bar (struct frame *f)
13019{
13020 f->tab_bar_redisplayed = true;
13021
13022 struct window *w;
13023 struct it it;
13024 struct glyph_row *row;
13025
13026 /* If frame hasn't a tab-bar window or if it is zero-height, don't
13027 do anything. This means you must start with tab-bar-lines
13028 non-zero to get the auto-sizing effect. Or in other words, you
13029 can turn off tab-bars by specifying tab-bar-lines zero. */
13030 if (!WINDOWP (f->tab_bar_window)
13031 || (w = XWINDOW (f->tab_bar_window),
13032 WINDOW_TOTAL_LINES (w) == 0))
13033 return false;
13034
13035 /* Set up an iterator for the tab-bar window. */
13036 init_iterator (&it, w, -1, -1, w->desired_matrix->rows, TAB_BAR_FACE_ID);
13037 it.first_visible_x = 0;
13038 it.last_visible_x = WINDOW_PIXEL_WIDTH (w);
13039 row = it.glyph_row;
13040 row->reversed_p = false;
13041
13042 /* Build a string that represents the contents of the tab-bar. */
13043 build_desired_tab_bar_string (f);
13044 reseat_to_string (&it, NULL, f->desired_tab_bar_string, 0, 0, 0,
13045 STRING_MULTIBYTE (f->desired_tab_bar_string));
13046 /* FIXME: This should be controlled by a user option. But it
13047 doesn't make sense to have an R2L tab bar if the menu bar cannot
13048 be drawn also R2L, and making the menu bar R2L is tricky due
13049 tabkit-specific code that implements it. If an R2L tab bar is
13050 ever supported, display_tab_bar_line should also be augmented to
13051 call unproduce_glyphs like display_line and display_string
13052 do. */
13053 it.paragraph_embedding = L2R;
13054
13055 if (f->n_tab_bar_rows == 0)
13056 {
13057 int new_height = tab_bar_height (f, &f->n_tab_bar_rows, true);
13058
13059 if (new_height != WINDOW_PIXEL_HEIGHT (w))
13060 {
13061 if (FRAME_TERMINAL (f)->change_tab_bar_height_hook)
13062 FRAME_TERMINAL (f)->change_tab_bar_height_hook (f, new_height);
13063 frame_default_tab_bar_height = new_height;
13064 /* Always do that now. */
13065 clear_glyph_matrix (w->desired_matrix);
13066 f->fonts_changed = true;
13067 return true;
13068 }
13069 }
13070
13071 /* Display as many lines as needed to display all tab-bar items. */
13072
13073 if (f->n_tab_bar_rows > 0)
13074 {
13075 int border, rows, height, extra;
13076
13077 if (TYPE_RANGED_FIXNUMP (int, Vtab_bar_border))
13078 border = XFIXNUM (Vtab_bar_border);
13079 else if (EQ (Vtab_bar_border, Qinternal_border_width))
13080 border = FRAME_INTERNAL_BORDER_WIDTH (f);
13081 else if (EQ (Vtab_bar_border, Qborder_width))
13082 border = f->border_width;
13083 else
13084 border = 0;
13085 if (border < 0)
13086 border = 0;
13087
13088 rows = f->n_tab_bar_rows;
13089 height = max (1, (it.last_visible_y - border) / rows);
13090 extra = it.last_visible_y - border - height * rows;
13091
13092 while (it.current_y < it.last_visible_y)
13093 {
13094 int h = 0;
13095 if (extra > 0 && rows-- > 0)
13096 {
13097 h = (extra + rows - 1) / rows;
13098 extra -= h;
13099 }
13100 display_tab_bar_line (&it, height + h);
13101 }
13102 }
13103 else
13104 {
13105 while (it.current_y < it.last_visible_y)
13106 display_tab_bar_line (&it, 0);
13107 }
13108
13109 /* It doesn't make much sense to try scrolling in the tab-bar
13110 window, so don't do it. */
13111 w->desired_matrix->no_scrolling_p = true;
13112 w->must_be_updated_p = true;
13113
13114 if (!NILP (Vauto_resize_tab_bars))
13115 {
13116 bool change_height_p = true;
13117
13118 /* If we couldn't display everything, change the tab-bar's
13119 height if there is room for more. */
13120 if (IT_STRING_CHARPOS (it) < it.end_charpos)
13121 change_height_p = true;
13122
13123 /* We subtract 1 because display_tab_bar_line advances the
13124 glyph_row pointer before returning to its caller. We want to
13125 examine the last glyph row produced by
13126 display_tab_bar_line. */
13127 row = it.glyph_row - 1;
13128
13129 /* If there are blank lines at the end, except for a partially
13130 visible blank line at the end that is smaller than
13131 FRAME_LINE_HEIGHT, change the tab-bar's height. */
13132 if (!MATRIX_ROW_DISPLAYS_TEXT_P (row)
13133 && row->height >= FRAME_LINE_HEIGHT (f))
13134 change_height_p = true;
13135
13136 /* If row displays tab-bar items, but is partially visible,
13137 change the tab-bar's height. */
13138 if (MATRIX_ROW_DISPLAYS_TEXT_P (row)
13139 && MATRIX_ROW_BOTTOM_Y (row) > it.last_visible_y)
13140 change_height_p = true;
13141
13142 /* Resize windows as needed by changing the `tab-bar-lines'
13143 frame parameter. */
13144 if (change_height_p)
13145 {
13146 int nrows;
13147 int new_height = tab_bar_height (f, &nrows, true);
13148
13149 change_height_p = ((EQ (Vauto_resize_tab_bars, Qgrow_only)
13150 && !f->minimize_tab_bar_window_p)
13151 ? (new_height > WINDOW_PIXEL_HEIGHT (w))
13152 : (new_height != WINDOW_PIXEL_HEIGHT (w)));
13153 f->minimize_tab_bar_window_p = false;
13154
13155 if (change_height_p)
13156 {
13157 if (FRAME_TERMINAL (f)->change_tab_bar_height_hook)
13158 FRAME_TERMINAL (f)->change_tab_bar_height_hook (f, new_height);
13159 frame_default_tab_bar_height = new_height;
13160 clear_glyph_matrix (w->desired_matrix);
13161 f->n_tab_bar_rows = nrows;
13162 f->fonts_changed = true;
13163
13164 return true;
13165 }
13166 }
13167 }
13168
13169 f->minimize_tab_bar_window_p = false;
13170 return false;
13171}
13172
13173/* Get information about the tab-bar item which is displayed in GLYPH
13174 on frame F. Return in *PROP_IDX the index where tab-bar item
13175 properties start in F->tab_bar_items. Value is false if
13176 GLYPH doesn't display a tab-bar item. */
13177
13178static bool
13179tab_bar_item_info (struct frame *f, struct glyph *glyph, int *prop_idx, bool *close_p)
13180{
13181 Lisp_Object prop;
13182 int charpos;
13183
13184 /* This function can be called asynchronously, which means we must
13185 exclude any possibility that Fget_text_property signals an
13186 error. */
13187 charpos = min (SCHARS (f->current_tab_bar_string), glyph->charpos);
13188 charpos = max (0, charpos);
13189
13190 /* Get the text property `menu-item' at pos. The value of that
13191 property is the start index of this item's properties in
13192 F->tab_bar_items. */
13193 prop = Fget_text_property (make_fixnum (charpos),
13194 Qmenu_item, f->current_tab_bar_string);
13195 if (! FIXNUMP (prop))
13196 return false;
13197 *prop_idx = XFIXNUM (prop);
13198
13199 *close_p = !NILP (Fget_text_property (make_fixnum (charpos),
13200 Qclose_tab,
13201 f->current_tab_bar_string));
13202
13203 return true;
13204}
13205
13206
13207/* Get information about the tab-bar item at position X/Y on frame F.
13208 Return in *GLYPH a pointer to the glyph of the tab-bar item in
13209 the current matrix of the tab-bar window of F, or NULL if not
13210 on a tab-bar item. Return in *PROP_IDX the index of the tab-bar
13211 item in F->tab_bar_items. Value is
13212
13213 -1 if X/Y is not on a tab-bar item
13214 0 if X/Y is on the same item that was highlighted before.
13215 1 otherwise. */
13216
13217static int
13218get_tab_bar_item (struct frame *f, int x, int y, struct glyph **glyph,
13219 int *hpos, int *vpos, int *prop_idx, bool *close_p)
13220{
13221 Mouse_HLInfo *hlinfo = MOUSE_HL_INFO (f);
13222 struct window *w = XWINDOW (f->tab_bar_window);
13223 int area;
13224
13225 /* Find the glyph under X/Y. */
13226 *glyph = x_y_to_hpos_vpos (w, x, y, hpos, vpos, 0, 0, &area);
13227 if (*glyph == NULL)
13228 return -1;
13229
13230 /* Get the start of this tab-bar item's properties in
13231 f->tab_bar_items. */
13232 if (!tab_bar_item_info (f, *glyph, prop_idx, close_p))
13233 return -1;
13234
13235 /* Is mouse on the highlighted item? */
13236 if (EQ (f->tab_bar_window, hlinfo->mouse_face_window)
13237 && *vpos >= hlinfo->mouse_face_beg_row
13238 && *vpos <= hlinfo->mouse_face_end_row
13239 && (*vpos > hlinfo->mouse_face_beg_row
13240 || *hpos >= hlinfo->mouse_face_beg_col)
13241 && (*vpos < hlinfo->mouse_face_end_row
13242 || *hpos < hlinfo->mouse_face_end_col
13243 || hlinfo->mouse_face_past_end))
13244 return 0;
13245
13246 return 1;
13247}
13248
13249
13250/* EXPORT:
13251 Handle mouse button event on the tab-bar of frame F, at
13252 frame-relative coordinates X/Y. DOWN_P is true for a button press,
13253 false for button release. MODIFIERS is event modifiers for button
13254 release. */
13255
13256void
13257handle_tab_bar_click (struct frame *f, int x, int y, bool down_p,
13258 int modifiers)
13259{
13260 Mouse_HLInfo *hlinfo = MOUSE_HL_INFO (f);
13261 struct window *w = XWINDOW (f->tab_bar_window);
13262 int hpos, vpos, prop_idx;
13263 bool close_p;
13264 struct glyph *glyph;
13265 Lisp_Object enabled_p;
13266 int ts;
13267
13268 /* If not on the highlighted tab-bar item, and mouse-highlight is
13269 non-nil, return. This is so we generate the tab-bar button
13270 click only when the mouse button is released on the same item as
13271 where it was pressed. However, when mouse-highlight is disabled,
13272 generate the click when the button is released regardless of the
13273 highlight, since tab-bar items are not highlighted in that
13274 case. */
13275 frame_to_window_pixel_xy (w, &x, &y);
13276 ts = get_tab_bar_item (f, x, y, &glyph, &hpos, &vpos, &prop_idx, &close_p);
13277 if (ts == -1
13278 || (ts != 0 && !NILP (Vmouse_highlight)))
13279 return;
13280
13281 /* When mouse-highlight is off, generate the click for the item
13282 where the button was pressed, disregarding where it was
13283 released. */
13284 if (NILP (Vmouse_highlight) && !down_p)
13285 prop_idx = f->last_tab_bar_item;
13286
13287 /* If item is disabled, do nothing. */
13288 enabled_p = AREF (f->tab_bar_items, prop_idx + TAB_BAR_ITEM_ENABLED_P);
13289 if (NILP (enabled_p))
13290 return;
13291
13292 if (down_p)
13293 {
13294 /* Show item in pressed state. */
13295 if (!NILP (Vmouse_highlight))
13296 show_mouse_face (hlinfo, DRAW_IMAGE_SUNKEN);
13297 f->last_tab_bar_item = prop_idx;
13298 }
13299 else
13300 {
13301 Lisp_Object key, frame;
13302 struct input_event event;
13303 EVENT_INIT (event);
13304
13305 /* Show item in released state. */
13306 if (!NILP (Vmouse_highlight))
13307 show_mouse_face (hlinfo, DRAW_IMAGE_RAISED);
13308
13309 key = AREF (f->tab_bar_items, prop_idx + TAB_BAR_ITEM_KEY);
13310
13311 XSETFRAME (frame, f);
13312 event.kind = TAB_BAR_EVENT;
13313 event.frame_or_window = frame;
13314 event.arg = frame;
13315 kbd_buffer_store_event (&event);
13316
13317 event.kind = TAB_BAR_EVENT;
13318 event.frame_or_window = frame;
13319 event.arg = key;
13320 event.modifiers = close_p ? ctrl_modifier | modifiers : modifiers;
13321 kbd_buffer_store_event (&event);
13322 f->last_tab_bar_item = -1;
13323 }
13324}
13325
13326
13327/* Possibly highlight a tab-bar item on frame F when mouse moves to
13328 tab-bar window-relative coordinates X/Y. Called from
13329 note_mouse_highlight. */
13330
13331static void
13332note_tab_bar_highlight (struct frame *f, int x, int y)
13333{
13334 Lisp_Object window = f->tab_bar_window;
13335 struct window *w = XWINDOW (window);
13336 Display_Info *dpyinfo = FRAME_DISPLAY_INFO (f);
13337 Mouse_HLInfo *hlinfo = MOUSE_HL_INFO (f);
13338 int hpos, vpos;
13339 struct glyph *glyph;
13340 struct glyph_row *row;
13341 int i;
13342 Lisp_Object enabled_p;
13343 int prop_idx;
13344 bool close_p;
13345 enum draw_glyphs_face draw = DRAW_IMAGE_RAISED;
13346 bool mouse_down_p;
13347 int rc;
13348
13349 /* Function note_mouse_highlight is called with negative X/Y
13350 values when mouse moves outside of the frame. */
13351 if (x <= 0 || y <= 0)
13352 {
13353 clear_mouse_face (hlinfo);
13354 return;
13355 }
13356
13357 rc = get_tab_bar_item (f, x, y, &glyph, &hpos, &vpos, &prop_idx, &close_p);
13358 if (rc < 0)
13359 {
13360 /* Not on tab-bar item. */
13361 clear_mouse_face (hlinfo);
13362 return;
13363 }
13364 else if (rc == 0)
13365 /* On same tab-bar item as before. */
13366 goto set_help_echo;
13367
13368 clear_mouse_face (hlinfo);
13369
13370#ifndef HAVE_NS
13371 /* Mouse is down, but on different tab-bar item? */
13372 mouse_down_p = (gui_mouse_grabbed (dpyinfo)
13373 && f == dpyinfo->last_mouse_frame);
13374
13375 if (mouse_down_p && f->last_tab_bar_item != prop_idx)
13376 return;
13377
13378 draw = mouse_down_p ? DRAW_IMAGE_SUNKEN : DRAW_IMAGE_RAISED;
13379#else
13380 draw = DRAW_IMAGE_RAISED;
13381#endif /* HAVE_NS */
13382
13383 /* If tab-bar item is not enabled, don't highlight it. */
13384 enabled_p = AREF (f->tab_bar_items, prop_idx + TAB_BAR_ITEM_ENABLED_P);
13385 if (!NILP (enabled_p) && !NILP (Vmouse_highlight))
13386 {
13387 /* Compute the x-position of the glyph. In front and past the
13388 image is a space. We include this in the highlighted area. */
13389 row = MATRIX_ROW (w->current_matrix, vpos);
13390 for (i = x = 0; i < hpos; ++i)
13391 x += row->glyphs[TEXT_AREA][i].pixel_width;
13392
13393 /* Record this as the current active region. */
13394 hlinfo->mouse_face_beg_col = hpos;
13395 hlinfo->mouse_face_beg_row = vpos;
13396 hlinfo->mouse_face_beg_x = x;
13397 hlinfo->mouse_face_past_end = false;
13398
13399 hlinfo->mouse_face_end_col = hpos + 1;
13400 hlinfo->mouse_face_end_row = vpos;
13401 hlinfo->mouse_face_end_x = x + glyph->pixel_width;
13402 hlinfo->mouse_face_window = window;
13403 hlinfo->mouse_face_face_id = TAB_BAR_FACE_ID;
13404
13405 /* Display it as active. */
13406 show_mouse_face (hlinfo, draw);
13407 }
13408
13409 set_help_echo:
13410
13411 /* Set help_echo_string to a help string to display for this tab-bar item.
13412 XTread_socket does the rest. */
13413 help_echo_object = help_echo_window = Qnil;
13414 help_echo_pos = -1;
13415 help_echo_string = AREF (f->tab_bar_items, prop_idx + TAB_BAR_ITEM_HELP);
13416 if (NILP (help_echo_string))
13417 help_echo_string = AREF (f->tab_bar_items, prop_idx + TAB_BAR_ITEM_CAPTION);
13418}
13419
13420#endif /* HAVE_WINDOW_SYSTEM */
13421
13422
13423
13424/***********************************************************************
13425 Tool-bars
13426 ***********************************************************************/
13427
13428#ifdef HAVE_WINDOW_SYSTEM
13429
12513/* Update the tool-bar item list for frame F. This has to be done 13430/* Update the tool-bar item list for frame F. This has to be done
12514 before we start to fill in any display lines. Called from 13431 before we start to fill in any display lines. Called from
12515 prepare_menu_bars. If SAVE_MATCH_DATA, we must save 13432 prepare_menu_bars. If SAVE_MATCH_DATA, we must save
@@ -16265,7 +17182,8 @@ compute_window_start_on_continuation_line (struct window *w)
16265 17182
16266 /* Find the start of the continued line. This should be fast 17183 /* Find the start of the continued line. This should be fast
16267 because find_newline is fast (newline cache). */ 17184 because find_newline is fast (newline cache). */
16268 row = w->desired_matrix->rows + window_wants_header_line (w); 17185 row = w->desired_matrix->rows + window_wants_tab_line (w)
17186 + window_wants_header_line (w);
16269 init_iterator (&it, w, CHARPOS (start_pos), BYTEPOS (start_pos), 17187 init_iterator (&it, w, CHARPOS (start_pos), BYTEPOS (start_pos),
16270 row, DEFAULT_FACE_ID); 17188 row, DEFAULT_FACE_ID);
16271 reseat_at_previous_visible_line_start (&it); 17189 reseat_at_previous_visible_line_start (&it);
@@ -16428,6 +17346,8 @@ try_cursor_movement (Lisp_Object window, struct text_pos startp,
16428 this_scroll_margin = window_scroll_margin (w, MARGIN_IN_PIXELS); 17346 this_scroll_margin = window_scroll_margin (w, MARGIN_IN_PIXELS);
16429 17347
16430 top_scroll_margin = this_scroll_margin; 17348 top_scroll_margin = this_scroll_margin;
17349 if (window_wants_tab_line (w))
17350 top_scroll_margin += CURRENT_TAB_LINE_HEIGHT (w);
16431 if (window_wants_header_line (w)) 17351 if (window_wants_header_line (w))
16432 top_scroll_margin += CURRENT_HEADER_LINE_HEIGHT (w); 17352 top_scroll_margin += CURRENT_HEADER_LINE_HEIGHT (w);
16433 17353
@@ -17197,13 +18117,14 @@ redisplay_window (Lisp_Object window, bool just_this_one_p)
17197 margin, even though this part handles windows that didn't 18117 margin, even though this part handles windows that didn't
17198 scroll at all. */ 18118 scroll at all. */
17199 int pixel_margin = margin * frame_line_height; 18119 int pixel_margin = margin * frame_line_height;
18120 bool tab_line = window_wants_tab_line (w);
17200 bool header_line = window_wants_header_line (w); 18121 bool header_line = window_wants_header_line (w);
17201 18122
17202 /* Note: We add an extra FRAME_LINE_HEIGHT, because the loop 18123 /* Note: We add an extra FRAME_LINE_HEIGHT, because the loop
17203 below, which finds the row to move point to, advances by 18124 below, which finds the row to move point to, advances by
17204 the Y coordinate of the _next_ row, see the definition of 18125 the Y coordinate of the _next_ row, see the definition of
17205 MATRIX_ROW_BOTTOM_Y. */ 18126 MATRIX_ROW_BOTTOM_Y. */
17206 if (w->cursor.vpos < margin + header_line) 18127 if (w->cursor.vpos < margin + tab_line + header_line)
17207 { 18128 {
17208 w->cursor.vpos = -1; 18129 w->cursor.vpos = -1;
17209 clear_glyph_matrix (w->desired_matrix); 18130 clear_glyph_matrix (w->desired_matrix);
@@ -17213,6 +18134,8 @@ redisplay_window (Lisp_Object window, bool just_this_one_p)
17213 { 18134 {
17214 int window_height = window_box_height (w); 18135 int window_height = window_box_height (w);
17215 18136
18137 if (tab_line)
18138 window_height += CURRENT_TAB_LINE_HEIGHT (w);
17216 if (header_line) 18139 if (header_line)
17217 window_height += CURRENT_HEADER_LINE_HEIGHT (w); 18140 window_height += CURRENT_HEADER_LINE_HEIGHT (w);
17218 if (w->cursor.y >= window_height - pixel_margin) 18141 if (w->cursor.y >= window_height - pixel_margin)
@@ -17544,7 +18467,7 @@ redisplay_window (Lisp_Object window, bool just_this_one_p)
17544 centering_position -= pt_offset; 18467 centering_position -= pt_offset;
17545 centering_position -= 18468 centering_position -=
17546 (frame_line_height * (1 + margin + last_line_misfit) 18469 (frame_line_height * (1 + margin + last_line_misfit)
17547 + WINDOW_HEADER_LINE_HEIGHT (w)); 18470 + WINDOW_TAB_LINE_HEIGHT (w) + WINDOW_HEADER_LINE_HEIGHT (w));
17548 /* Don't let point enter the scroll margin near top of 18471 /* Don't let point enter the scroll margin near top of
17549 the window. */ 18472 the window. */
17550 if (centering_position < margin * frame_line_height) 18473 if (centering_position < margin * frame_line_height)
@@ -17772,7 +18695,8 @@ redisplay_window (Lisp_Object window, bool just_this_one_p)
17772 && (w->column_number_displayed != current_column ()))) 18695 && (w->column_number_displayed != current_column ())))
17773 /* This means that the window has a mode line. */ 18696 /* This means that the window has a mode line. */
17774 && (window_wants_mode_line (w) 18697 && (window_wants_mode_line (w)
17775 || window_wants_header_line (w))) 18698 || window_wants_header_line (w)
18699 || window_wants_tab_line (w)))
17776 { 18700 {
17777 18701
17778 display_mode_lines (w); 18702 display_mode_lines (w);
@@ -17788,6 +18712,17 @@ redisplay_window (Lisp_Object window, bool just_this_one_p)
17788 = DESIRED_MODE_LINE_HEIGHT (w); 18712 = DESIRED_MODE_LINE_HEIGHT (w);
17789 } 18713 }
17790 18714
18715 /* If tab line height has changed, arrange for a thorough
18716 immediate redisplay using the correct tab line height. */
18717 if (window_wants_tab_line (w)
18718 && CURRENT_TAB_LINE_HEIGHT (w) != DESIRED_TAB_LINE_HEIGHT (w))
18719 {
18720 f->fonts_changed = true;
18721 w->tab_line_height = -1;
18722 MATRIX_TAB_LINE_ROW (w->current_matrix)->height
18723 = DESIRED_TAB_LINE_HEIGHT (w);
18724 }
18725
17791 /* If header line height has changed, arrange for a thorough 18726 /* If header line height has changed, arrange for a thorough
17792 immediate redisplay using the correct header line height. */ 18727 immediate redisplay using the correct header line height. */
17793 if (window_wants_header_line (w) 18728 if (window_wants_header_line (w)
@@ -17835,6 +18770,12 @@ redisplay_window (Lisp_Object window, bool just_this_one_p)
17835#ifdef HAVE_WINDOW_SYSTEM 18770#ifdef HAVE_WINDOW_SYSTEM
17836 if (FRAME_WINDOW_P (f)) 18771 if (FRAME_WINDOW_P (f))
17837 { 18772 {
18773 if (WINDOWP (f->tab_bar_window)
18774 && (FRAME_TAB_BAR_LINES (f) > 0
18775 || !NILP (Vauto_resize_tab_bars))
18776 && redisplay_tab_bar (f))
18777 ignore_mouse_drag_p = true;
18778
17838#ifdef HAVE_EXT_TOOL_BAR 18779#ifdef HAVE_EXT_TOOL_BAR
17839 if (FRAME_EXTERNAL_TOOL_BAR (f)) 18780 if (FRAME_EXTERNAL_TOOL_BAR (f))
17840 redisplay_tool_bar (f); 18781 redisplay_tool_bar (f);
@@ -17846,7 +18787,16 @@ redisplay_window (Lisp_Object window, bool just_this_one_p)
17846 ignore_mouse_drag_p = true; 18787 ignore_mouse_drag_p = true;
17847#endif 18788#endif
17848 } 18789 }
18790 else
18791 {
18792 if ((FRAME_TAB_BAR_LINES (f) > 0))
18793 display_tab_bar (w);
18794 }
18795
17849 gui_consider_frame_title (w->frame); 18796 gui_consider_frame_title (w->frame);
18797#else
18798 if ((FRAME_TAB_BAR_LINES (f) > 0))
18799 display_tab_bar (w);
17850#endif 18800#endif
17851 } 18801 }
17852 18802
@@ -18058,6 +19008,11 @@ try_window_reusing_current_matrix (struct window *w)
18058 return false; 19008 return false;
18059 19009
18060 /* If top-line visibility has changed, give up. */ 19010 /* If top-line visibility has changed, give up. */
19011 if (window_wants_tab_line (w)
19012 != MATRIX_TAB_LINE_ROW (w->current_matrix)->mode_line_p)
19013 return false;
19014
19015 /* If top-line visibility has changed, give up. */
18061 if (window_wants_header_line (w) 19016 if (window_wants_header_line (w)
18062 != MATRIX_HEADER_LINE_ROW (w->current_matrix)->mode_line_p) 19017 != MATRIX_HEADER_LINE_ROW (w->current_matrix)->mode_line_p)
18063 return false; 19018 return false;
@@ -18202,7 +19157,7 @@ try_window_reusing_current_matrix (struct window *w)
18202 (start_row + i)->enabled_p = false; 19157 (start_row + i)->enabled_p = false;
18203 19158
18204 /* Re-compute Y positions. */ 19159 /* Re-compute Y positions. */
18205 min_y = WINDOW_HEADER_LINE_HEIGHT (w); 19160 min_y = WINDOW_TAB_LINE_HEIGHT (w) + WINDOW_HEADER_LINE_HEIGHT (w);
18206 max_y = it.last_visible_y; 19161 max_y = it.last_visible_y;
18207 for (row = start_row + nrows_scrolled; 19162 for (row = start_row + nrows_scrolled;
18208 row < bottom_row; 19163 row < bottom_row;
@@ -18229,7 +19184,7 @@ try_window_reusing_current_matrix (struct window *w)
18229 /* Disable lines in the current matrix which are now 19184 /* Disable lines in the current matrix which are now
18230 below the window. */ 19185 below the window. */
18231 for (++row; row < bottom_row; ++row) 19186 for (++row; row < bottom_row; ++row)
18232 row->enabled_p = row->mode_line_p = false; 19187 row->enabled_p = row->mode_line_p = row->tab_line_p = false;
18233 } 19188 }
18234 19189
18235 /* Update window_end_pos etc.; last_reused_text_row is the last 19190 /* Update window_end_pos etc.; last_reused_text_row is the last
@@ -18307,7 +19262,7 @@ try_window_reusing_current_matrix (struct window *w)
18307 it.vpos = (MATRIX_ROW_VPOS (first_row_to_display, w->current_matrix) 19262 it.vpos = (MATRIX_ROW_VPOS (first_row_to_display, w->current_matrix)
18308 - nrows_scrolled); 19263 - nrows_scrolled);
18309 it.current_y = (first_row_to_display->y - first_reusable_row->y 19264 it.current_y = (first_row_to_display->y - first_reusable_row->y
18310 + WINDOW_HEADER_LINE_HEIGHT (w)); 19265 + WINDOW_TAB_LINE_HEIGHT (w) + WINDOW_HEADER_LINE_HEIGHT (w));
18311 19266
18312 /* Display lines beginning with first_row_to_display in the 19267 /* Display lines beginning with first_row_to_display in the
18313 desired matrix. Set last_text_row to the last row displayed 19268 desired matrix. Set last_text_row to the last row displayed
@@ -18340,7 +19295,7 @@ try_window_reusing_current_matrix (struct window *w)
18340 19295
18341 /* Scroll the display. */ 19296 /* Scroll the display. */
18342 run.current_y = first_reusable_row->y; 19297 run.current_y = first_reusable_row->y;
18343 run.desired_y = WINDOW_HEADER_LINE_HEIGHT (w); 19298 run.desired_y = WINDOW_TAB_LINE_HEIGHT (w) + WINDOW_HEADER_LINE_HEIGHT (w);
18344 run.height = it.last_visible_y - run.current_y; 19299 run.height = it.last_visible_y - run.current_y;
18345 dy = run.current_y - run.desired_y; 19300 dy = run.current_y - run.desired_y;
18346 19301
@@ -18358,7 +19313,7 @@ try_window_reusing_current_matrix (struct window *w)
18358 19313
18359 /* Adjust Y positions of reused rows. */ 19314 /* Adjust Y positions of reused rows. */
18360 bottom_row = MATRIX_BOTTOM_TEXT_ROW (w->current_matrix, w); 19315 bottom_row = MATRIX_BOTTOM_TEXT_ROW (w->current_matrix, w);
18361 min_y = WINDOW_HEADER_LINE_HEIGHT (w); 19316 min_y = WINDOW_TAB_LINE_HEIGHT (w) + WINDOW_HEADER_LINE_HEIGHT (w);
18362 max_y = it.last_visible_y; 19317 max_y = it.last_visible_y;
18363 for (row = first_reusable_row; row < first_row_to_display; ++row) 19318 for (row = first_reusable_row; row < first_row_to_display; ++row)
18364 { 19319 {
@@ -19316,6 +20271,7 @@ try_window_id (struct window *w)
19316 = MATRIX_ROW_VPOS (first_unchanged_at_end_row, w->current_matrix); 20271 = MATRIX_ROW_VPOS (first_unchanged_at_end_row, w->current_matrix);
19317 int from = WINDOW_TOP_EDGE_LINE (w) + from_vpos; 20272 int from = WINDOW_TOP_EDGE_LINE (w) + from_vpos;
19318 int end = (WINDOW_TOP_EDGE_LINE (w) 20273 int end = (WINDOW_TOP_EDGE_LINE (w)
20274 + window_wants_tab_line (w)
19319 + window_wants_header_line (w) 20275 + window_wants_header_line (w)
19320 + window_internal_height (w)); 20276 + window_internal_height (w));
19321 20277
@@ -19494,7 +20450,7 @@ try_window_id (struct window *w)
19494 { 20450 {
19495 /* Displayed to end of window, but no line containing text was 20451 /* Displayed to end of window, but no line containing text was
19496 displayed. Lines were deleted at the end of the window. */ 20452 displayed. Lines were deleted at the end of the window. */
19497 bool first_vpos = window_wants_header_line (w); 20453 int first_vpos = window_wants_tab_line (w) + window_wants_header_line (w);
19498 int vpos = w->window_end_vpos; 20454 int vpos = w->window_end_vpos;
19499 struct glyph_row *current_row = current_matrix->rows + vpos; 20455 struct glyph_row *current_row = current_matrix->rows + vpos;
19500 struct glyph_row *desired_row = desired_matrix->rows + vpos; 20456 struct glyph_row *desired_row = desired_matrix->rows + vpos;
@@ -19866,6 +20822,39 @@ GLYPHS > 1 or omitted means dump glyphs in long form. */)
19866} 20822}
19867 20823
19868 20824
20825DEFUN ("dump-tab-bar-row", Fdump_tab_bar_row, Sdump_tab_bar_row, 1, 2, "P",
20826 doc: /* Dump glyph row ROW of the tab-bar of the current frame to stderr.
20827Interactively, ROW is the prefix numeric argument and defaults to zero.
20828GLYPHS 0 means don't dump glyphs.
20829GLYPHS 1 means dump glyphs in short form.
20830GLYPHS > 1 or omitted means dump glyphs in long form.
20831
20832If there's no tab-bar, or if the tab-bar is not drawn by Emacs,
20833do nothing. */)
20834 (Lisp_Object row, Lisp_Object glyphs)
20835{
20836#if defined (HAVE_WINDOW_SYSTEM)
20837 struct frame *sf = SELECTED_FRAME ();
20838 struct glyph_matrix *m = WINDOWP (sf->tab_bar_window)
20839 ? XWINDOW (sf->tab_bar_window)->current_matrix
20840 : sf->current_matrix;
20841 EMACS_INT vpos;
20842
20843 if (NILP (row))
20844 vpos = WINDOWP (sf->tab_bar_window) ? 0 :
20845 FRAME_MENU_BAR_LINES (sf) > 0 ? 1 : 0;
20846 else
20847 {
20848 CHECK_FIXNUM (row);
20849 vpos = XFIXNUM (row);
20850 }
20851 if (vpos >= 0 && vpos < m->nrows)
20852 dump_glyph_row (MATRIX_ROW (m, vpos), vpos,
20853 TYPE_RANGED_FIXNUMP (int, glyphs) ? XFIXNUM (glyphs) : 2);
20854#endif
20855 return Qnil;
20856}
20857
19869DEFUN ("dump-tool-bar-row", Fdump_tool_bar_row, Sdump_tool_bar_row, 1, 2, "P", 20858DEFUN ("dump-tool-bar-row", Fdump_tool_bar_row, Sdump_tool_bar_row, 1, 2, "P",
19870 doc: /* Dump glyph row ROW of the tool-bar of the current frame to stderr. 20859 doc: /* Dump glyph row ROW of the tool-bar of the current frame to stderr.
19871Interactively, ROW is the prefix numeric argument and defaults to zero. 20860Interactively, ROW is the prefix numeric argument and defaults to zero.
@@ -20233,7 +21222,7 @@ compute_line_metrics (struct it *it)
20233 /* Compute how much of the line is visible. */ 21222 /* Compute how much of the line is visible. */
20234 row->visible_height = row->height; 21223 row->visible_height = row->height;
20235 21224
20236 min_y = WINDOW_HEADER_LINE_HEIGHT (it->w); 21225 min_y = WINDOW_TAB_LINE_HEIGHT (it->w) + WINDOW_HEADER_LINE_HEIGHT (it->w);
20237 max_y = WINDOW_BOX_HEIGHT_NO_MODE_LINE (it->w); 21226 max_y = WINDOW_BOX_HEIGHT_NO_MODE_LINE (it->w);
20238 21227
20239 if (row->y < min_y) 21228 if (row->y < min_y)
@@ -20547,6 +21536,8 @@ extend_face_to_end_of_line (struct it *it)
20547 /* Mode line and the header line don't have margins, and 21536 /* Mode line and the header line don't have margins, and
20548 likewise the frame's tool-bar window, if there is any. */ 21537 likewise the frame's tool-bar window, if there is any. */
20549 if (!(it->glyph_row->mode_line_p 21538 if (!(it->glyph_row->mode_line_p
21539 || (WINDOWP (f->tab_bar_window)
21540 && it->w == XWINDOW (f->tab_bar_window))
20550#ifndef HAVE_EXT_TOOL_BAR 21541#ifndef HAVE_EXT_TOOL_BAR
20551 || (WINDOWP (f->tool_bar_window) 21542 || (WINDOWP (f->tool_bar_window)
20552 && it->w == XWINDOW (f->tool_bar_window)) 21543 && it->w == XWINDOW (f->tool_bar_window))
@@ -21743,9 +22734,10 @@ display_line (struct it *it, int cursor_vpos)
21743 ptrdiff_t min_pos = ZV + 1, max_pos = 0; 22734 ptrdiff_t min_pos = ZV + 1, max_pos = 0;
21744 ptrdiff_t min_bpos UNINIT, max_bpos UNINIT; 22735 ptrdiff_t min_bpos UNINIT, max_bpos UNINIT;
21745 bool pending_handle_line_prefix = false; 22736 bool pending_handle_line_prefix = false;
22737 int tab_line = window_wants_tab_line (it->w);
21746 int header_line = window_wants_header_line (it->w); 22738 int header_line = window_wants_header_line (it->w);
21747 bool hscroll_this_line = (cursor_vpos >= 0 22739 bool hscroll_this_line = (cursor_vpos >= 0
21748 && it->vpos == cursor_vpos - header_line 22740 && it->vpos == cursor_vpos - tab_line - header_line
21749 && hscrolling_current_line_p (it->w)); 22741 && hscrolling_current_line_p (it->w));
21750 int first_visible_x = it->first_visible_x; 22742 int first_visible_x = it->first_visible_x;
21751 int last_visible_x = it->last_visible_x; 22743 int last_visible_x = it->last_visible_x;
@@ -23786,6 +24778,18 @@ display_mode_lines (struct window *w)
23786 ++n; 24778 ++n;
23787 } 24779 }
23788 24780
24781 if (window_wants_tab_line (w))
24782 {
24783 Lisp_Object window_tab_line_format
24784 = window_parameter (w, Qtab_line_format);
24785
24786 display_mode_line (w, TAB_LINE_FACE_ID,
24787 NILP (window_tab_line_format)
24788 ? BVAR (current_buffer, tab_line_format)
24789 : window_tab_line_format);
24790 ++n;
24791 }
24792
23789 if (window_wants_header_line (w)) 24793 if (window_wants_header_line (w))
23790 { 24794 {
23791 Lisp_Object window_header_line_format 24795 Lisp_Object window_header_line_format
@@ -23807,10 +24811,10 @@ display_mode_lines (struct window *w)
23807} 24811}
23808 24812
23809 24813
23810/* Display mode or header line of window W. FACE_ID specifies which 24814/* Display mode or header/tab line of window W. FACE_ID specifies which
23811 line to display; it is either MODE_LINE_FACE_ID or 24815 line to display; it is either MODE_LINE_FACE_ID, HEADER_LINE_FACE_ID or
23812 HEADER_LINE_FACE_ID. FORMAT is the mode/header line format to 24816 TAB_LINE_FACE_ID. FORMAT is the mode/header/tab line format to
23813 display. Value is the pixel height of the mode/header line 24817 display. Value is the pixel height of the mode/header/tab line
23814 displayed. */ 24818 displayed. */
23815 24819
23816static int 24820static int
@@ -23827,6 +24831,8 @@ display_mode_line (struct window *w, enum face_id face_id, Lisp_Object format)
23827 prepare_desired_row (w, it.glyph_row, true); 24831 prepare_desired_row (w, it.glyph_row, true);
23828 24832
23829 it.glyph_row->mode_line_p = true; 24833 it.glyph_row->mode_line_p = true;
24834 if (face_id == TAB_LINE_FACE_ID)
24835 it.glyph_row->tab_line_p = true;
23830 24836
23831 /* FIXME: This should be controlled by a user option. But 24837 /* FIXME: This should be controlled by a user option. But
23832 supporting such an option is not trivial, since the mode line is 24838 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). */)
24544 : EQ (face, Qmode_line) ? MODE_LINE_FACE_ID 25550 : EQ (face, Qmode_line) ? MODE_LINE_FACE_ID
24545 : EQ (face, Qmode_line_inactive) ? MODE_LINE_INACTIVE_FACE_ID 25551 : EQ (face, Qmode_line_inactive) ? MODE_LINE_INACTIVE_FACE_ID
24546 : EQ (face, Qheader_line) ? HEADER_LINE_FACE_ID 25552 : EQ (face, Qheader_line) ? HEADER_LINE_FACE_ID
25553 : EQ (face, Qtab_line) ? TAB_LINE_FACE_ID
25554 : EQ (face, Qtab_bar) ? TAB_BAR_FACE_ID
24547 : EQ (face, Qtool_bar) ? TOOL_BAR_FACE_ID 25555 : EQ (face, Qtool_bar) ? TOOL_BAR_FACE_ID
24548 : DEFAULT_FACE_ID; 25556 : DEFAULT_FACE_ID;
24549 25557
@@ -29475,7 +30483,7 @@ gui_clear_end_of_line (struct window *w, struct glyph_row *updated_row,
29475 to_x += area_left; 30483 to_x += area_left;
29476 } 30484 }
29477 30485
29478 min_y = WINDOW_HEADER_LINE_HEIGHT (w); 30486 min_y = WINDOW_TAB_LINE_HEIGHT (w) + WINDOW_HEADER_LINE_HEIGHT (w);
29479 from_y = WINDOW_TO_FRAME_PIXEL_Y (w, max (min_y, w->output_cursor.y)); 30487 from_y = WINDOW_TO_FRAME_PIXEL_Y (w, max (min_y, w->output_cursor.y));
29480 to_y = WINDOW_TO_FRAME_PIXEL_Y (w, to_y); 30488 to_y = WINDOW_TO_FRAME_PIXEL_Y (w, to_y);
29481 30489
@@ -29985,6 +30993,7 @@ erase_phys_cursor (struct window *w)
29985 if (w->phys_cursor_type == HOLLOW_BOX_CURSOR) 30993 if (w->phys_cursor_type == HOLLOW_BOX_CURSOR)
29986 { 30994 {
29987 int x, y; 30995 int x, y;
30996 int tab_line_height = WINDOW_TAB_LINE_HEIGHT (w);
29988 int header_line_height = WINDOW_HEADER_LINE_HEIGHT (w); 30997 int header_line_height = WINDOW_HEADER_LINE_HEIGHT (w);
29989 int width; 30998 int width;
29990 30999
@@ -30000,7 +31009,7 @@ erase_phys_cursor (struct window *w)
30000 x = 0; 31009 x = 0;
30001 } 31010 }
30002 width = min (width, window_box_width (w, TEXT_AREA) - x); 31011 width = min (width, window_box_width (w, TEXT_AREA) - x);
30003 y = WINDOW_TO_FRAME_PIXEL_Y (w, max (header_line_height, cursor_row->y)); 31012 y = WINDOW_TO_FRAME_PIXEL_Y (w, max (tab_line_height, max (header_line_height, cursor_row->y)));
30004 x = WINDOW_TEXT_TO_FRAME_PIXEL_X (w, x); 31013 x = WINDOW_TEXT_TO_FRAME_PIXEL_X (w, x);
30005 31014
30006 if (width > 0) 31015 if (width > 0)
@@ -30346,12 +31355,13 @@ show_mouse_face (Mouse_HLInfo *hlinfo, enum draw_glyphs_face draw)
30346 /* Change the mouse cursor. */ 31355 /* Change the mouse cursor. */
30347 if (FRAME_WINDOW_P (f) && NILP (track_mouse)) 31356 if (FRAME_WINDOW_P (f) && NILP (track_mouse))
30348 { 31357 {
30349#ifndef HAVE_EXT_TOOL_BAR
30350 if (draw == DRAW_NORMAL_TEXT 31358 if (draw == DRAW_NORMAL_TEXT
30351 && !EQ (hlinfo->mouse_face_window, f->tool_bar_window)) 31359#ifndef HAVE_EXT_TOOL_BAR
31360 && !EQ (hlinfo->mouse_face_window, f->tool_bar_window)
31361#endif
31362 && !EQ (hlinfo->mouse_face_window, f->tab_bar_window))
30352 FRAME_RIF (f)->define_frame_cursor (f, FRAME_OUTPUT_DATA (f)->text_cursor); 31363 FRAME_RIF (f)->define_frame_cursor (f, FRAME_OUTPUT_DATA (f)->text_cursor);
30353 else 31364 else
30354#endif
30355 if (draw == DRAW_MOUSE_FACE) 31365 if (draw == DRAW_MOUSE_FACE)
30356 FRAME_RIF (f)->define_frame_cursor (f, FRAME_OUTPUT_DATA (f)->hand_cursor); 31366 FRAME_RIF (f)->define_frame_cursor (f, FRAME_OUTPUT_DATA (f)->hand_cursor);
30357 else 31367 else
@@ -31347,7 +32357,7 @@ note_mode_line_or_margin_highlight (Lisp_Object window, int x, int y,
31347 struct glyph * glyph = NULL, * row_start_glyph = NULL; 32357 struct glyph * glyph = NULL, * row_start_glyph = NULL;
31348 struct glyph_row *row UNINIT; 32358 struct glyph_row *row UNINIT;
31349 32359
31350 if (area == ON_MODE_LINE || area == ON_HEADER_LINE) 32360 if (area == ON_MODE_LINE || area == ON_HEADER_LINE || area == ON_TAB_LINE)
31351 { 32361 {
31352 int x0; 32362 int x0;
31353 struct glyph *end; 32363 struct glyph *end;
@@ -31359,7 +32369,9 @@ note_mode_line_or_margin_highlight (Lisp_Object window, int x, int y,
31359 32369
31360 row = (area == ON_MODE_LINE 32370 row = (area == ON_MODE_LINE
31361 ? MATRIX_MODE_LINE_ROW (w->current_matrix) 32371 ? MATRIX_MODE_LINE_ROW (w->current_matrix)
31362 : MATRIX_HEADER_LINE_ROW (w->current_matrix)); 32372 : (area == ON_TAB_LINE
32373 ? MATRIX_TAB_LINE_ROW (w->current_matrix)
32374 : MATRIX_HEADER_LINE_ROW (w->current_matrix)));
31363 32375
31364 /* Find the glyph under the mouse pointer. */ 32376 /* Find the glyph under the mouse pointer. */
31365 if (row->mode_line_p && row->enabled_p) 32377 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,
31474 32486
31475 /* Change the mouse pointer according to what is under X/Y. */ 32487 /* Change the mouse pointer according to what is under X/Y. */
31476 if (NILP (pointer) 32488 if (NILP (pointer)
31477 && (area == ON_MODE_LINE || area == ON_HEADER_LINE)) 32489 && (area == ON_MODE_LINE || area == ON_HEADER_LINE
32490 || area == ON_TAB_LINE))
31478 { 32491 {
31479 Lisp_Object map; 32492 Lisp_Object map;
31480 32493
@@ -31500,7 +32513,8 @@ note_mode_line_or_margin_highlight (Lisp_Object window, int x, int y,
31500 { 32513 {
31501 mouse_face = Fget_text_property (pos, Qmouse_face, string); 32514 mouse_face = Fget_text_property (pos, Qmouse_face, string);
31502 if (!NILP (Vmouse_highlight) && !NILP (mouse_face) 32515 if (!NILP (Vmouse_highlight) && !NILP (mouse_face)
31503 && ((area == ON_MODE_LINE) || (area == ON_HEADER_LINE)) 32516 && ((area == ON_MODE_LINE) || (area == ON_HEADER_LINE)
32517 || (area == ON_TAB_LINE))
31504 && glyph) 32518 && glyph)
31505 { 32519 {
31506 Lisp_Object b, e; 32520 Lisp_Object b, e;
@@ -31571,7 +32585,11 @@ note_mode_line_or_margin_highlight (Lisp_Object window, int x, int y,
31571 hpos = x - gpos; 32585 hpos = x - gpos;
31572 vpos = (area == ON_MODE_LINE 32586 vpos = (area == ON_MODE_LINE
31573 ? (w->current_matrix)->nrows - 1 32587 ? (w->current_matrix)->nrows - 1
31574 : 0); 32588 : (area == ON_TAB_LINE
32589 ? 0
32590 : (w->current_matrix->tab_line_p
32591 ? 1
32592 : 0)));
31575 32593
31576 /* If GLYPH's position is included in the region that is 32594 /* If GLYPH's position is included in the region that is
31577 already drawn in mouse face, we have nothing to do. */ 32595 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,
31627 32645
31628 /* If mouse-face doesn't need to be shown, clear any existing 32646 /* If mouse-face doesn't need to be shown, clear any existing
31629 mouse-face. */ 32647 mouse-face. */
31630 if ((area == ON_MODE_LINE || area == ON_HEADER_LINE) && !mouse_face_shown) 32648 if ((area == ON_MODE_LINE || area == ON_HEADER_LINE
32649 || area == ON_TAB_LINE) && !mouse_face_shown)
31631 clear_mouse_face (hlinfo); 32650 clear_mouse_face (hlinfo);
31632 32651
31633 define_frame_cursor1 (f, cursor, pointer); 32652 define_frame_cursor1 (f, cursor, pointer);
@@ -31671,7 +32690,7 @@ note_mouse_highlight (struct frame *f, int x, int y)
31671 return; 32690 return;
31672 32691
31673 /* Which window is that in? */ 32692 /* Which window is that in? */
31674 window = window_from_coordinates (f, x, y, &part, true); 32693 window = window_from_coordinates (f, x, y, &part, true, true);
31675 32694
31676 /* If displaying active text in another window, clear that. */ 32695 /* If displaying active text in another window, clear that. */
31677 if (! EQ (window, hlinfo->mouse_face_window) 32696 if (! EQ (window, hlinfo->mouse_face_window)
@@ -31680,7 +32699,8 @@ note_mouse_highlight (struct frame *f, int x, int y)
31680 && !NILP (window) 32699 && !NILP (window)
31681 && part != ON_TEXT 32700 && part != ON_TEXT
31682 && part != ON_MODE_LINE 32701 && part != ON_MODE_LINE
31683 && part != ON_HEADER_LINE)) 32702 && part != ON_HEADER_LINE
32703 && part != ON_TAB_LINE))
31684 clear_mouse_face (hlinfo); 32704 clear_mouse_face (hlinfo);
31685 32705
31686 /* Reset help_echo_string. It will get recomputed below. */ 32706 /* Reset help_echo_string. It will get recomputed below. */
@@ -31748,6 +32768,16 @@ note_mouse_highlight (struct frame *f, int x, int y)
31748 w = XWINDOW (window); 32768 w = XWINDOW (window);
31749 frame_to_window_pixel_xy (w, &x, &y); 32769 frame_to_window_pixel_xy (w, &x, &y);
31750 32770
32771#if defined (HAVE_WINDOW_SYSTEM)
32772 /* Handle tab-bar window differently since it doesn't display a
32773 buffer. */
32774 if (EQ (window, f->tab_bar_window))
32775 {
32776 note_tab_bar_highlight (f, x, y);
32777 return;
32778 }
32779#endif
32780
31751#if defined (HAVE_WINDOW_SYSTEM) && ! defined (HAVE_EXT_TOOL_BAR) 32781#if defined (HAVE_WINDOW_SYSTEM) && ! defined (HAVE_EXT_TOOL_BAR)
31752 /* Handle tool-bar window differently since it doesn't display a 32782 /* Handle tool-bar window differently since it doesn't display a
31753 buffer. */ 32783 buffer. */
@@ -31759,7 +32789,7 @@ note_mouse_highlight (struct frame *f, int x, int y)
31759#endif 32789#endif
31760 32790
31761 /* Mouse is on the mode, header line or margin? */ 32791 /* Mouse is on the mode, header line or margin? */
31762 if (part == ON_MODE_LINE || part == ON_HEADER_LINE 32792 if (part == ON_MODE_LINE || part == ON_HEADER_LINE || part == ON_TAB_LINE
31763 || part == ON_LEFT_MARGIN || part == ON_RIGHT_MARGIN) 32793 || part == ON_LEFT_MARGIN || part == ON_RIGHT_MARGIN)
31764 { 32794 {
31765 note_mode_line_or_margin_highlight (window, x, y, part); 32795 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)
32774 r.x, r.y, r.width, r.height); 33804 r.x, r.y, r.width, r.height);
32775 mouse_face_overwritten_p = expose_window_tree (XWINDOW (f->root_window), &r); 33805 mouse_face_overwritten_p = expose_window_tree (XWINDOW (f->root_window), &r);
32776 33806
33807 if (WINDOWP (f->tab_bar_window))
33808 mouse_face_overwritten_p
33809 |= expose_window (XWINDOW (f->tab_bar_window), &r);
33810
32777#ifndef HAVE_EXT_TOOL_BAR 33811#ifndef HAVE_EXT_TOOL_BAR
32778 if (WINDOWP (f->tool_bar_window)) 33812 if (WINDOWP (f->tool_bar_window))
32779 mouse_face_overwritten_p 33813 mouse_face_overwritten_p
@@ -32914,11 +33948,13 @@ be let-bound around code that needs to disable messages temporarily. */);
32914 defsubr (&Sdump_frame_glyph_matrix); 33948 defsubr (&Sdump_frame_glyph_matrix);
32915 defsubr (&Sdump_glyph_matrix); 33949 defsubr (&Sdump_glyph_matrix);
32916 defsubr (&Sdump_glyph_row); 33950 defsubr (&Sdump_glyph_row);
33951 defsubr (&Sdump_tab_bar_row);
32917 defsubr (&Sdump_tool_bar_row); 33952 defsubr (&Sdump_tool_bar_row);
32918 defsubr (&Strace_redisplay); 33953 defsubr (&Strace_redisplay);
32919 defsubr (&Strace_to_stderr); 33954 defsubr (&Strace_to_stderr);
32920#endif 33955#endif
32921#ifdef HAVE_WINDOW_SYSTEM 33956#ifdef HAVE_WINDOW_SYSTEM
33957 defsubr (&Stab_bar_height);
32922 defsubr (&Stool_bar_height); 33958 defsubr (&Stool_bar_height);
32923 defsubr (&Slookup_image_map); 33959 defsubr (&Slookup_image_map);
32924#endif 33960#endif
@@ -33304,6 +34340,18 @@ make sure that (1) your window manager has focus follow the mouse and
33304of your window manager. */); 34340of your window manager. */);
33305 Vmouse_autoselect_window = Qnil; 34341 Vmouse_autoselect_window = Qnil;
33306 34342
34343 DEFVAR_LISP ("auto-resize-tab-bars", Vauto_resize_tab_bars,
34344 doc: /* Non-nil means automatically resize tab-bars.
34345This dynamically changes the tab-bar's height to the minimum height
34346that is needed to make all tab-bar items visible.
34347If value is `grow-only', the tab-bar's height is only increased
34348automatically; to decrease the tab-bar height, use \\[recenter]. */);
34349 Vauto_resize_tab_bars = Qt;
34350
34351 DEFVAR_BOOL ("auto-raise-tab-bar-buttons", auto_raise_tab_bar_buttons_p,
34352 doc: /* Non-nil means raise tab-bar buttons when the mouse moves over them. */);
34353 auto_raise_tab_bar_buttons_p = true;
34354
33307 DEFVAR_LISP ("auto-resize-tool-bars", Vauto_resize_tool_bars, 34355 DEFVAR_LISP ("auto-resize-tool-bars", Vauto_resize_tool_bars,
33308 doc: /* Non-nil means automatically resize tool-bars. 34356 doc: /* Non-nil means automatically resize tool-bars.
33309This dynamically changes the tool-bar's height to the minimum height 34357This 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. */);
33326 Vmake_cursor_line_fully_visible = Qt; 34374 Vmake_cursor_line_fully_visible = Qt;
33327 DEFSYM (Qmake_cursor_line_fully_visible, "make-cursor-line-fully-visible"); 34375 DEFSYM (Qmake_cursor_line_fully_visible, "make-cursor-line-fully-visible");
33328 34376
34377 DEFSYM (Qclose_tab, "close-tab");
34378 DEFVAR_LISP ("tab-bar-border", Vtab_bar_border,
34379 doc: /* Border below tab-bar in pixels.
34380If an integer, use it as the height of the border.
34381If it is one of `internal-border-width' or `border-width', use the
34382value of the corresponding frame parameter.
34383Otherwise, no border is added below the tab-bar. */);
34384 Vtab_bar_border = Qinternal_border_width;
34385
34386 DEFVAR_LISP ("tab-bar-button-margin", Vtab_bar_button_margin,
34387 doc: /* Margin around tab-bar buttons in pixels.
34388If an integer, use that for both horizontal and vertical margins.
34389Otherwise, value should be a pair of integers `(HORZ . VERT)' with
34390HORZ specifying the horizontal margin, and VERT specifying the
34391vertical margin. */);
34392 Vtab_bar_button_margin = make_fixnum (DEFAULT_TAB_BAR_BUTTON_MARGIN);
34393
34394 DEFVAR_INT ("tab-bar-button-relief", tab_bar_button_relief,
34395 doc: /* Relief thickness of tab-bar buttons. */);
34396 tab_bar_button_relief = DEFAULT_TAB_BAR_BUTTON_RELIEF;
34397
33329 DEFVAR_LISP ("tool-bar-border", Vtool_bar_border, 34398 DEFVAR_LISP ("tool-bar-border", Vtool_bar_border,
33330 doc: /* Border below tool-bar in pixels. 34399 doc: /* Border below tool-bar in pixels.
33331If an integer, use it as the height of the border. 34400If 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)
4586 case MODE_LINE_FACE_ID: name = Qmode_line; break; 4586 case MODE_LINE_FACE_ID: name = Qmode_line; break;
4587 case MODE_LINE_INACTIVE_FACE_ID: name = Qmode_line_inactive; break; 4587 case MODE_LINE_INACTIVE_FACE_ID: name = Qmode_line_inactive; break;
4588 case HEADER_LINE_FACE_ID: name = Qheader_line; break; 4588 case HEADER_LINE_FACE_ID: name = Qheader_line; break;
4589 case TAB_LINE_FACE_ID: name = Qtab_line; break;
4590 case TAB_BAR_FACE_ID: name = Qtab_bar; break;
4589 case TOOL_BAR_FACE_ID: name = Qtool_bar; break; 4591 case TOOL_BAR_FACE_ID: name = Qtool_bar; break;
4590 case FRINGE_FACE_ID: name = Qfringe; break; 4592 case FRINGE_FACE_ID: name = Qfringe; break;
4591 case SCROLL_BAR_FACE_ID: name = Qscroll_bar; break; 4593 case SCROLL_BAR_FACE_ID: name = Qscroll_bar; break;
@@ -5293,6 +5295,8 @@ realize_basic_faces (struct frame *f)
5293 realize_named_face (f, Qwindow_divider_last_pixel, 5295 realize_named_face (f, Qwindow_divider_last_pixel,
5294 WINDOW_DIVIDER_LAST_PIXEL_FACE_ID); 5296 WINDOW_DIVIDER_LAST_PIXEL_FACE_ID);
5295 realize_named_face (f, Qinternal_border, INTERNAL_BORDER_FACE_ID); 5297 realize_named_face (f, Qinternal_border, INTERNAL_BORDER_FACE_ID);
5298 realize_named_face (f, Qtab_bar, TAB_BAR_FACE_ID);
5299 realize_named_face (f, Qtab_line, TAB_LINE_FACE_ID);
5296 5300
5297 /* Reflect changes in the `menu' face in menu bars. */ 5301 /* Reflect changes in the `menu' face in menu bars. */
5298 if (FRAME_FACE_CACHE (f)->menu_face_changed_p) 5302 if (FRAME_FACE_CACHE (f)->menu_face_changed_p)
@@ -6579,7 +6583,9 @@ syms_of_xfaces (void)
6579 /* Names of basic faces. */ 6583 /* Names of basic faces. */
6580 DEFSYM (Qdefault, "default"); 6584 DEFSYM (Qdefault, "default");
6581 DEFSYM (Qtool_bar, "tool-bar"); 6585 DEFSYM (Qtool_bar, "tool-bar");
6586 DEFSYM (Qtab_bar, "tab-bar");
6582 DEFSYM (Qfringe, "fringe"); 6587 DEFSYM (Qfringe, "fringe");
6588 DEFSYM (Qtab_line, "tab-line");
6583 DEFSYM (Qheader_line, "header-line"); 6589 DEFSYM (Qheader_line, "header-line");
6584 DEFSYM (Qscroll_bar, "scroll-bar"); 6590 DEFSYM (Qscroll_bar, "scroll-bar");
6585 DEFSYM (Qmenu, "menu"); 6591 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)
1602} 1602}
1603 1603
1604 1604
1605/* Set the number of lines used for the tab bar of frame F to VALUE.
1606 VALUE not an integer, or < 0 means set the lines to zero. OLDVAL
1607 is the old number of tab bar lines. This function changes the
1608 height of all windows on frame F to match the new tab bar height.
1609 The frame's height doesn't change. */
1610
1611static void
1612x_set_tab_bar_lines (struct frame *f, Lisp_Object value, Lisp_Object oldval)
1613{
1614 int nlines;
1615
1616 /* Treat tab bars like menu bars. */
1617 if (FRAME_MINIBUF_ONLY_P (f))
1618 return;
1619
1620 /* Use VALUE only if an int >= 0. */
1621 if (RANGED_FIXNUMP (0, value, INT_MAX))
1622 nlines = XFIXNAT (value);
1623 else
1624 nlines = 0;
1625
1626 x_change_tab_bar_height (f, nlines * FRAME_LINE_HEIGHT (f));
1627}
1628
1629
1630/* Set the pixel height of the tab bar of frame F to HEIGHT. */
1631void
1632x_change_tab_bar_height (struct frame *f, int height)
1633{
1634 int unit = FRAME_LINE_HEIGHT (f);
1635 int old_height = FRAME_TAB_BAR_HEIGHT (f);
1636 int lines = (height + unit - 1) / unit;
1637 Lisp_Object fullscreen;
1638
1639 /* Make sure we redisplay all windows in this frame. */
1640 fset_redisplay (f);
1641
1642 /* Recalculate tab bar and frame text sizes. */
1643 FRAME_TAB_BAR_HEIGHT (f) = height;
1644 FRAME_TAB_BAR_LINES (f) = lines;
1645 /* Store the `tab-bar-lines' and `height' frame parameters. */
1646 store_frame_param (f, Qtab_bar_lines, make_fixnum (lines));
1647 store_frame_param (f, Qheight, make_fixnum (FRAME_LINES (f)));
1648
1649 /* We also have to make sure that the internal border at the top of
1650 the frame, below the menu bar or tab bar, is redrawn when the
1651 tab bar disappears. This is so because the internal border is
1652 below the tab bar if one is displayed, but is below the menu bar
1653 if there isn't a tab bar. The tab bar draws into the area
1654 below the menu bar. */
1655 if (FRAME_X_WINDOW (f) && FRAME_TAB_BAR_HEIGHT (f) == 0)
1656 {
1657 clear_frame (f);
1658 clear_current_matrices (f);
1659 }
1660
1661 if ((height < old_height) && WINDOWP (f->tab_bar_window))
1662 clear_glyph_matrix (XWINDOW (f->tab_bar_window)->current_matrix);
1663
1664 /* Recalculate tabbar height. */
1665 f->n_tab_bar_rows = 0;
1666 if (old_height == 0
1667 && (!f->after_make_frame
1668 || NILP (frame_inhibit_implied_resize)
1669 || (CONSP (frame_inhibit_implied_resize)
1670 && NILP (Fmemq (Qtab_bar_lines, frame_inhibit_implied_resize)))))
1671 f->tab_bar_redisplayed = f->tab_bar_resized = false;
1672
1673 adjust_frame_size (f, -1, -1,
1674 ((!f->tab_bar_resized
1675 && (NILP (fullscreen =
1676 get_frame_param (f, Qfullscreen))
1677 || EQ (fullscreen, Qfullwidth))) ? 1
1678 : (old_height == 0 || height == 0) ? 2
1679 : 4),
1680 false, Qtab_bar_lines);
1681
1682 f->tab_bar_resized = f->tab_bar_redisplayed;
1683
1684 /* adjust_frame_size might not have done anything, garbage frame
1685 here. */
1686 adjust_frame_glyphs (f);
1687 SET_FRAME_GARBAGED (f);
1688 if (FRAME_X_WINDOW (f))
1689 x_clear_under_internal_border (f);
1690}
1691
1692
1605/* Set the number of lines used for the tool bar of frame F to VALUE. 1693/* Set the number of lines used for the tool bar of frame F to VALUE.
1606 VALUE not an integer, or < 0 means set the lines to zero. OLDVAL 1694 VALUE not an integer, or < 0 means set the lines to zero. OLDVAL
1607 is the old number of tool bar lines. This function changes the 1695 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. */)
3922 NILP (Vmenu_bar_mode) 4010 NILP (Vmenu_bar_mode)
3923 ? make_fixnum (0) : make_fixnum (1), 4011 ? make_fixnum (0) : make_fixnum (1),
3924 NULL, NULL, RES_TYPE_NUMBER); 4012 NULL, NULL, RES_TYPE_NUMBER);
4013 gui_default_parameter (f, parms, Qtab_bar_lines,
4014 NILP (Vtab_bar_mode)
4015 ? make_fixnum (0) : make_fixnum (1),
4016 NULL, NULL, RES_TYPE_NUMBER);
3925 gui_default_parameter (f, parms, Qtool_bar_lines, 4017 gui_default_parameter (f, parms, Qtool_bar_lines,
3926 NILP (Vtool_bar_mode) 4018 NILP (Vtool_bar_mode)
3927 ? make_fixnum (0) : make_fixnum (1), 4019 ? make_fixnum (0) : make_fixnum (1),
@@ -3941,7 +4033,7 @@ This function is an internal primitive--use `make-frame' instead. */)
3941 RES_TYPE_BOOLEAN); 4033 RES_TYPE_BOOLEAN);
3942 4034
3943 /* Compute the size of the X window. */ 4035 /* Compute the size of the X window. */
3944 window_prompting = gui_figure_window_size (f, parms, true, 4036 window_prompting = gui_figure_window_size (f, parms, true, true,
3945 &x_width, &x_height); 4037 &x_width, &x_height);
3946 4038
3947 tem = gui_display_get_arg (dpyinfo, parms, Qunsplittable, 0, 0, 4039 tem = gui_display_get_arg (dpyinfo, parms, Qunsplittable, 0, 0,
@@ -5060,6 +5152,7 @@ frame_geometry (Lisp_Object frame, Lisp_Object attribute)
5060 int internal_border_width; 5152 int internal_border_width;
5061 bool menu_bar_external = false, tool_bar_external = false; 5153 bool menu_bar_external = false, tool_bar_external = false;
5062 int menu_bar_height = 0, menu_bar_width = 0; 5154 int menu_bar_height = 0, menu_bar_width = 0;
5155 int tab_bar_height = 0, tab_bar_width = 0;
5063 int tool_bar_height = 0, tool_bar_width = 0; 5156 int tool_bar_height = 0, tool_bar_width = 0;
5064 5157
5065 if (FRAME_INITIAL_P (f) || !FRAME_X_P (f) || !FRAME_OUTER_WINDOW (f)) 5158 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)
5130#endif 5223#endif
5131 menu_bar_width = menu_bar_height ? native_width : 0; 5224 menu_bar_width = menu_bar_height ? native_width : 0;
5132 5225
5226 tab_bar_height = FRAME_TAB_BAR_HEIGHT (f);
5227 tab_bar_width = (tab_bar_height
5228 ? native_width - 2 * internal_border_width
5229 : 0);
5230 inner_top += tab_bar_height;
5231
5133#ifdef HAVE_EXT_TOOL_BAR 5232#ifdef HAVE_EXT_TOOL_BAR
5134 tool_bar_external = true; 5233 tool_bar_external = true;
5135 if (EQ (FRAME_TOOL_BAR_POSITION (f), Qleft)) 5234 if (EQ (FRAME_TOOL_BAR_POSITION (f), Qleft))
@@ -5198,6 +5297,9 @@ frame_geometry (Lisp_Object frame, Lisp_Object attribute)
5198 Fcons (Qmenu_bar_size, 5297 Fcons (Qmenu_bar_size,
5199 Fcons (make_fixnum (menu_bar_width), 5298 Fcons (make_fixnum (menu_bar_width),
5200 make_fixnum (menu_bar_height))), 5299 make_fixnum (menu_bar_height))),
5300 Fcons (Qtab_bar_size,
5301 Fcons (make_fixnum (tab_bar_width),
5302 make_fixnum (tab_bar_height))),
5201 Fcons (Qtool_bar_external, tool_bar_external ? Qt : Qnil), 5303 Fcons (Qtool_bar_external, tool_bar_external ? Qt : Qnil),
5202 Fcons (Qtool_bar_position, FRAME_TOOL_BAR_POSITION (f)), 5304 Fcons (Qtool_bar_position, FRAME_TOOL_BAR_POSITION (f)),
5203 Fcons (Qtool_bar_size, 5305 Fcons (Qtool_bar_size,
@@ -6331,7 +6433,7 @@ x_create_tip_frame (struct x_display_info *dpyinfo, Lisp_Object parms)
6331 "inhibitDoubleBuffering", "InhibitDoubleBuffering", 6433 "inhibitDoubleBuffering", "InhibitDoubleBuffering",
6332 RES_TYPE_BOOLEAN); 6434 RES_TYPE_BOOLEAN);
6333 6435
6334 gui_figure_window_size (f, parms, false, &x_width, &x_height); 6436 gui_figure_window_size (f, parms, false, false, &x_width, &x_height);
6335 6437
6336 { 6438 {
6337 XSetWindowAttributes attrs; 6439 XSetWindowAttributes attrs;
@@ -7672,6 +7774,7 @@ frame_parm_handler x_frame_parm_handlers[] =
7672 gui_set_vertical_scroll_bars, 7774 gui_set_vertical_scroll_bars,
7673 gui_set_horizontal_scroll_bars, 7775 gui_set_horizontal_scroll_bars,
7674 gui_set_visibility, 7776 gui_set_visibility,
7777 x_set_tab_bar_lines,
7675 x_set_tool_bar_lines, 7778 x_set_tool_bar_lines,
7676 x_set_scroll_bar_foreground, 7779 x_set_scroll_bar_foreground,
7677 x_set_scroll_bar_background, 7780 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)
3220 if (s->hl == DRAW_IMAGE_SUNKEN 3220 if (s->hl == DRAW_IMAGE_SUNKEN
3221 || s->hl == DRAW_IMAGE_RAISED) 3221 || s->hl == DRAW_IMAGE_RAISED)
3222 { 3222 {
3223 thick = (tool_bar_button_relief < 0 3223 thick = (tab_bar_button_relief < 0
3224 ? DEFAULT_TOOL_BAR_BUTTON_RELIEF 3224 ? DEFAULT_TAB_BAR_BUTTON_RELIEF
3225 : min (tool_bar_button_relief, 1000000)); 3225 : (tool_bar_button_relief < 0
3226 ? DEFAULT_TOOL_BAR_BUTTON_RELIEF
3227 : min (tool_bar_button_relief, 1000000)));
3226 raised_p = s->hl == DRAW_IMAGE_RAISED; 3228 raised_p = s->hl == DRAW_IMAGE_RAISED;
3227 } 3229 }
3228 else 3230 else
@@ -3235,6 +3237,19 @@ x_draw_image_relief (struct glyph_string *s)
3235 y1 = y + s->slice.height - 1; 3237 y1 = y + s->slice.height - 1;
3236 3238
3237 extra_x = extra_y = 0; 3239 extra_x = extra_y = 0;
3240 if (s->face->id == TAB_BAR_FACE_ID)
3241 {
3242 if (CONSP (Vtab_bar_button_margin)
3243 && FIXNUMP (XCAR (Vtab_bar_button_margin))
3244 && FIXNUMP (XCDR (Vtab_bar_button_margin)))
3245 {
3246 extra_x = XFIXNUM (XCAR (Vtab_bar_button_margin));
3247 extra_y = XFIXNUM (XCDR (Vtab_bar_button_margin));
3248 }
3249 else if (FIXNUMP (Vtab_bar_button_margin))
3250 extra_x = extra_y = XFIXNUM (Vtab_bar_button_margin);
3251 }
3252
3238 if (s->face->id == TOOL_BAR_FACE_ID) 3253 if (s->face->id == TOOL_BAR_FACE_ID)
3239 { 3254 {
3240 if (CONSP (Vtool_bar_button_margin) 3255 if (CONSP (Vtool_bar_button_margin)
@@ -8396,10 +8411,11 @@ handle_one_xevent (struct x_display_info *dpyinfo,
8396 /* If mouse-highlight is an integer, input clears out 8411 /* If mouse-highlight is an integer, input clears out
8397 mouse highlighting. */ 8412 mouse highlighting. */
8398 if (!hlinfo->mouse_face_hidden && FIXNUMP (Vmouse_highlight) 8413 if (!hlinfo->mouse_face_hidden && FIXNUMP (Vmouse_highlight)
8399#if ! defined (USE_GTK)
8400 && (f == 0 8414 && (f == 0
8401 || !EQ (f->tool_bar_window, hlinfo->mouse_face_window)) 8415#if ! defined (USE_GTK)
8416 || !EQ (f->tool_bar_window, hlinfo->mouse_face_window)
8402#endif 8417#endif
8418 || !EQ (f->tab_bar_window, hlinfo->mouse_face_window))
8403 ) 8419 )
8404 { 8420 {
8405 clear_mouse_face (hlinfo); 8421 clear_mouse_face (hlinfo);
@@ -8823,7 +8839,7 @@ handle_one_xevent (struct x_display_info *dpyinfo,
8823 { 8839 {
8824 static Lisp_Object last_mouse_window; 8840 static Lisp_Object last_mouse_window;
8825 Lisp_Object window = window_from_coordinates 8841 Lisp_Object window = window_from_coordinates
8826 (f, event->xmotion.x, event->xmotion.y, 0, false); 8842 (f, event->xmotion.x, event->xmotion.y, 0, false, false);
8827 8843
8828 /* A window will be autoselected only when it is not 8844 /* A window will be autoselected only when it is not
8829 selected now and the last mouse movement event was 8845 selected now and the last mouse movement event was
@@ -9034,6 +9050,7 @@ handle_one_xevent (struct x_display_info *dpyinfo,
9034 { 9050 {
9035 /* If we decide we want to generate an event to be seen 9051 /* If we decide we want to generate an event to be seen
9036 by the rest of Emacs, we put it here. */ 9052 by the rest of Emacs, we put it here. */
9053 bool tab_bar_p = false;
9037 bool tool_bar_p = false; 9054 bool tool_bar_p = false;
9038 9055
9039 memset (&compose_status, 0, sizeof (compose_status)); 9056 memset (&compose_status, 0, sizeof (compose_status));
@@ -9070,6 +9087,23 @@ handle_one_xevent (struct x_display_info *dpyinfo,
9070#endif 9087#endif
9071 if (f) 9088 if (f)
9072 { 9089 {
9090 /* Is this in the tab-bar? */
9091 if (WINDOWP (f->tab_bar_window)
9092 && WINDOW_TOTAL_LINES (XWINDOW (f->tab_bar_window)))
9093 {
9094 Lisp_Object window;
9095 int x = event->xbutton.x;
9096 int y = event->xbutton.y;
9097
9098 window = window_from_coordinates (f, x, y, 0, true, true);
9099 tab_bar_p = EQ (window, f->tab_bar_window);
9100
9101 if (tab_bar_p && event->xbutton.button < 4)
9102 handle_tab_bar_click
9103 (f, x, y, event->xbutton.type == ButtonPress,
9104 x_x_to_emacs_modifiers (dpyinfo, event->xbutton.state));
9105 }
9106
9073#if ! defined (USE_GTK) 9107#if ! defined (USE_GTK)
9074 /* Is this in the tool-bar? */ 9108 /* Is this in the tool-bar? */
9075 if (WINDOWP (f->tool_bar_window) 9109 if (WINDOWP (f->tool_bar_window)
@@ -9079,7 +9113,7 @@ handle_one_xevent (struct x_display_info *dpyinfo,
9079 int x = event->xbutton.x; 9113 int x = event->xbutton.x;
9080 int y = event->xbutton.y; 9114 int y = event->xbutton.y;
9081 9115
9082 window = window_from_coordinates (f, x, y, 0, true); 9116 window = window_from_coordinates (f, x, y, 0, true, true);
9083 tool_bar_p = EQ (window, f->tool_bar_window); 9117 tool_bar_p = EQ (window, f->tool_bar_window);
9084 9118
9085 if (tool_bar_p && event->xbutton.button < 4) 9119 if (tool_bar_p && event->xbutton.button < 4)
@@ -9089,7 +9123,7 @@ handle_one_xevent (struct x_display_info *dpyinfo,
9089 } 9123 }
9090#endif /* !USE_GTK */ 9124#endif /* !USE_GTK */
9091 9125
9092 if (!tool_bar_p) 9126 if (!tab_bar_p && !tool_bar_p)
9093#if defined (USE_X_TOOLKIT) || defined (USE_GTK) 9127#if defined (USE_X_TOOLKIT) || defined (USE_GTK)
9094 if (! popup_activated ()) 9128 if (! popup_activated ())
9095#endif 9129#endif
@@ -9136,6 +9170,8 @@ handle_one_xevent (struct x_display_info *dpyinfo,
9136 { 9170 {
9137 dpyinfo->grabbed |= (1 << event->xbutton.button); 9171 dpyinfo->grabbed |= (1 << event->xbutton.button);
9138 dpyinfo->last_mouse_frame = f; 9172 dpyinfo->last_mouse_frame = f;
9173 if (f && !tab_bar_p)
9174 f->last_tab_bar_item = -1;
9139#if ! defined (USE_GTK) 9175#if ! defined (USE_GTK)
9140 if (f && !tool_bar_p) 9176 if (f && !tool_bar_p)
9141 f->last_tool_bar_item = -1; 9177 f->last_tool_bar_item = -1;
@@ -10142,6 +10178,7 @@ x_new_font (struct frame *f, Lisp_Object font_object, int fontset)
10142 int unit, font_ascent, font_descent; 10178 int unit, font_ascent, font_descent;
10143#ifndef USE_X_TOOLKIT 10179#ifndef USE_X_TOOLKIT
10144 int old_menu_bar_height = FRAME_MENU_BAR_HEIGHT (f); 10180 int old_menu_bar_height = FRAME_MENU_BAR_HEIGHT (f);
10181 int old_tab_bar_height = FRAME_TAB_BAR_HEIGHT (f);
10145 Lisp_Object fullscreen; 10182 Lisp_Object fullscreen;
10146#endif 10183#endif
10147 10184
@@ -10161,6 +10198,7 @@ x_new_font (struct frame *f, Lisp_Object font_object, int fontset)
10161 10198
10162#ifndef USE_X_TOOLKIT 10199#ifndef USE_X_TOOLKIT
10163 FRAME_MENU_BAR_HEIGHT (f) = FRAME_MENU_BAR_LINES (f) * FRAME_LINE_HEIGHT (f); 10200 FRAME_MENU_BAR_HEIGHT (f) = FRAME_MENU_BAR_LINES (f) * FRAME_LINE_HEIGHT (f);
10201 FRAME_TAB_BAR_HEIGHT (f) = FRAME_TAB_BAR_LINES (f) * FRAME_LINE_HEIGHT (f);
10164#endif 10202#endif
10165 10203
10166 /* Compute character columns occupied by scrollbar. 10204 /* Compute character columns occupied by scrollbar.
@@ -10185,18 +10223,20 @@ x_new_font (struct frame *f, Lisp_Object font_object, int fontset)
10185 FRAME_LINES (f) * FRAME_LINE_HEIGHT (f), 3, 10223 FRAME_LINES (f) * FRAME_LINE_HEIGHT (f), 3,
10186 false, Qfont); 10224 false, Qfont);
10187#ifndef USE_X_TOOLKIT 10225#ifndef USE_X_TOOLKIT
10188 if (FRAME_MENU_BAR_HEIGHT (f) != old_menu_bar_height 10226 if ((FRAME_MENU_BAR_HEIGHT (f) != old_menu_bar_height
10227 || FRAME_TAB_BAR_HEIGHT (f) != old_tab_bar_height)
10189 && !f->after_make_frame 10228 && !f->after_make_frame
10190 && (EQ (frame_inhibit_implied_resize, Qt) 10229 && (EQ (frame_inhibit_implied_resize, Qt)
10191 || (CONSP (frame_inhibit_implied_resize) 10230 || (CONSP (frame_inhibit_implied_resize)
10192 && NILP (Fmemq (Qfont, frame_inhibit_implied_resize)))) 10231 && NILP (Fmemq (Qfont, frame_inhibit_implied_resize))))
10193 && (NILP (fullscreen = get_frame_param (f, Qfullscreen)) 10232 && (NILP (fullscreen = get_frame_param (f, Qfullscreen))
10194 || EQ (fullscreen, Qfullwidth))) 10233 || EQ (fullscreen, Qfullwidth)))
10195 /* If the menu bar height changes, try to keep text height 10234 /* If the menu/tab bar height changes, try to keep text height
10196 constant. */ 10235 constant. */
10197 adjust_frame_size 10236 adjust_frame_size
10198 (f, -1, FRAME_TEXT_HEIGHT (f) + FRAME_MENU_BAR_HEIGHT (f) 10237 (f, -1, FRAME_TEXT_HEIGHT (f) + FRAME_MENU_BAR_HEIGHT (f)
10199 - old_menu_bar_height, 1, false, Qfont); 10238 + FRAME_TAB_BAR_HEIGHT (f)
10239 - old_menu_bar_height - old_tab_bar_height, 1, false, Qfont);
10200#endif /* USE_X_TOOLKIT */ 10240#endif /* USE_X_TOOLKIT */
10201 } 10241 }
10202 } 10242 }
@@ -13466,6 +13506,7 @@ x_create_terminal (struct x_display_info *dpyinfo)
13466#if defined (USE_X_TOOLKIT) || defined (USE_GTK) 13506#if defined (USE_X_TOOLKIT) || defined (USE_GTK)
13467 terminal->popup_dialog_hook = xw_popup_dialog; 13507 terminal->popup_dialog_hook = xw_popup_dialog;
13468#endif 13508#endif
13509 terminal->change_tab_bar_height_hook = x_change_tab_bar_height;
13469#ifndef HAVE_EXT_TOOL_BAR 13510#ifndef HAVE_EXT_TOOL_BAR
13470 terminal->change_tool_bar_height_hook = x_change_tool_bar_height; 13511 terminal->change_tool_bar_height_hook = x_change_tool_bar_height;
13471#endif 13512#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);
1178 1178
1179/* Defined in xfns.c. */ 1179/* Defined in xfns.c. */
1180extern void x_real_positions (struct frame *, int *, int *); 1180extern void x_real_positions (struct frame *, int *, int *);
1181extern void x_change_tab_bar_height (struct frame *, int);
1181extern void x_change_tool_bar_height (struct frame *, int); 1182extern void x_change_tool_bar_height (struct frame *, int);
1182extern void x_implicitly_set_name (struct frame *, Lisp_Object, Lisp_Object); 1183extern void x_implicitly_set_name (struct frame *, Lisp_Object, Lisp_Object);
1183extern void x_set_scroll_bar_default_width (struct frame *); 1184extern void x_set_scroll_bar_default_width (struct frame *);