summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJuri Linkov <juri@jurta.org>2010-04-10 02:19:38 +0300
committerJuri Linkov <juri@jurta.org>2010-04-10 02:19:38 +0300
commit9f8524056f853376bd2a9f45b2ef7a0ce5ee665c (patch)
tree87384eb2c587ca7e4ff724aa6fbe6241cbb42367
parent004f9b3f1bf1fdf09c457cd30ea5e377d653a470 (diff)
downloademacs-9f8524056f853376bd2a9f45b2ef7a0ce5ee665c.tar.gz
Initial implementation of non-toolkit X tabs and tab-bar.
-rw-r--r--etc/images/tab-delete.pbmbin0 -> 102 bytes
-rw-r--r--etc/images/tab-delete.xpm29
-rw-r--r--etc/images/tab-left.pbmbin0 -> 102 bytes
-rw-r--r--etc/images/tab-left.xpm64
-rw-r--r--etc/images/tab-right.pbmbin0 -> 102 bytes
-rw-r--r--etc/images/tab-right.xpm62
-rw-r--r--lisp/bindings.el1
-rw-r--r--lisp/dired.el7
-rw-r--r--lisp/emacs-lisp/find-func.el4
-rw-r--r--lisp/files.el6
-rw-r--r--lisp/loadup.el4
-rw-r--r--lisp/menu-bar.el6
-rw-r--r--lisp/progmodes/etags.el25
-rw-r--r--lisp/startup.el33
-rw-r--r--lisp/subr.el5
-rw-r--r--lisp/tab-bar.el266
-rw-r--r--lisp/tab.el290
-rw-r--r--src/Makefile.in6
-rw-r--r--src/dispextern.h79
-rw-r--r--src/dispnew.c60
-rw-r--r--src/frame.c52
-rw-r--r--src/frame.h44
-rw-r--r--src/keyboard.c420
-rw-r--r--src/keymap.c3
-rw-r--r--src/lisp.h1
-rw-r--r--src/menu.c3
-rw-r--r--src/msdos.c4
-rw-r--r--src/term.c2
-rw-r--r--src/termhooks.h5
-rw-r--r--src/w32fns.c4
-rw-r--r--src/w32term.c4
-rw-r--r--src/window.c36
-rw-r--r--src/window.h2
-rw-r--r--src/xdisp.c1061
-rw-r--r--src/xfaces.c6
-rw-r--r--src/xfns.c110
-rw-r--r--src/xmenu.c3
-rw-r--r--src/xterm.c44
-rw-r--r--src/xterm.h8
39 files changed, 2701 insertions, 58 deletions
diff --git a/etc/images/tab-delete.pbm b/etc/images/tab-delete.pbm
new file mode 100644
index 00000000000..1b125d7a6cf
--- /dev/null
+++ b/etc/images/tab-delete.pbm
Binary files differ
diff --git a/etc/images/tab-delete.xpm b/etc/images/tab-delete.xpm
new file mode 100644
index 00000000000..651c986579b
--- /dev/null
+++ b/etc/images/tab-delete.xpm
@@ -0,0 +1,29 @@
+/* XPM */
+static char *magick[] = {
+/* columns rows colors chars-per-pixel */
+"18 18 5 1",
+" c #01c601c601c6",
+". c Gray40",
+"X c #a527a527a527",
+"o c #da22da22da22",
+"O c None",
+/* pixels */
+"OOOOOOOOOOOOOOOOOO",
+"OOOOO OOOOOO",
+"OOO XXooXX. OOOO",
+"OO XooooooooX. OOO",
+"OO oooooooooo. OOO",
+"O Xooo ooo XoX. OO",
+"O Xoo o XXX OO",
+"O oooo XoXX OO",
+"O Xoooo ooXXX OO",
+"O Xooo OXX. OO",
+"O Xoo o XX. OO",
+"O .Xoo ooX XX.. OO",
+"OO XXOoXoXXX.. OOO",
+"OO XXXXXXXXX.. OOO",
+"OOO XXXX... OOOO",
+"OOOOO OOOOOO",
+"OOOOOOOOOOOOOOOOOO",
+"OOOOOOOOOOOOOOOOOO",
+};
diff --git a/etc/images/tab-left.pbm b/etc/images/tab-left.pbm
new file mode 100644
index 00000000000..65376e266ba
--- /dev/null
+++ b/etc/images/tab-left.pbm
Binary files differ
diff --git a/etc/images/tab-left.xpm b/etc/images/tab-left.xpm
new file mode 100644
index 00000000000..7fc0abad2f5
--- /dev/null
+++ b/etc/images/tab-left.xpm
@@ -0,0 +1,64 @@
+/* XPM */
+static char * left_arrow_xpm[] = {
+"18 18 43 1",
+" c None",
+". c #000000",
+"+ c #B9D0B9",
+"@ c #CDDECB",
+"# c #B6C7B6",
+"$ c #B1C9B0",
+"% c #B3C4B3",
+"& c #B4CBB2",
+"* c #B5CEB5",
+"= c #B7CCB5",
+"- c #B9CEB7",
+"; c #BAD1BA",
+"> c #BBCFBA",
+", c #BBD0B9",
+"' c #B2C9B0",
+") c #7EAB78",
+"! c #AAC7A8",
+"~ c #B3CAB1",
+"{ c #B0C9B0",
+"] c #B0C9AE",
+"^ c #AEC7AC",
+"/ c #AAC5A8",
+"( c #A9C4A7",
+"_ c #698267",
+": c #2D2D2D",
+"< c #CFDFCC",
+"[ c #ADC8AB",
+"} c #B0C7AE",
+"| c #ADC6AB",
+"1 c #678C63",
+"2 c #9BAD9A",
+"3 c #85AE81",
+"4 c #87AF84",
+"5 c #87B083",
+"6 c #88AF84",
+"7 c #88B085",
+"8 c #86AF82",
+"9 c #547150",
+"0 c #3C5235",
+"a c #5B7950",
+"b c #4A6342",
+"c c #3B5035",
+"d c #415639",
+" . ",
+" .. ",
+" .+. ",
+" .@#. ",
+" .@$%........ ",
+" .@&*=-;->,'). ",
+" .@!~{]^///^(_. ",
+" :<[}||[!^^}^[1. ",
+" .23444445645789. ",
+" .0aaaaaaaaaaab. ",
+" .0aaaaaaaaaab. ",
+" .0aabccccccd. ",
+" .0ab........ ",
+" .0b. ",
+" .b. ",
+" .. ",
+" . ",
+" "};
diff --git a/etc/images/tab-right.pbm b/etc/images/tab-right.pbm
new file mode 100644
index 00000000000..3b858207412
--- /dev/null
+++ b/etc/images/tab-right.pbm
Binary files differ
diff --git a/etc/images/tab-right.xpm b/etc/images/tab-right.xpm
new file mode 100644
index 00000000000..f4231af3b2d
--- /dev/null
+++ b/etc/images/tab-right.xpm
@@ -0,0 +1,62 @@
+/* XPM */
+static char * right_arrow_xpm[] = {
+"18 18 41 1",
+" c None",
+". c #000000",
+"+ c #8CA782",
+"@ c #B1CDAE",
+"# c #77A16E",
+"$ c #B4CEB1",
+"% c #ACC8A9",
+"& c #709867",
+"* c #C1D6BD",
+"= c #BDD3B8",
+"- c #BFD4BB",
+"; c #C2D7BE",
+"> c #B0CAAD",
+", c #B2CBB0",
+"' c #AAC7A8",
+") c #0F1308",
+"! c #AEC5A8",
+"~ c #AEC8AD",
+"{ c #ABC7A8",
+"] c #AAC6A7",
+"^ c #A8C6A5",
+"/ c #ADC8AD",
+"( c #A8C7A8",
+"_ c #A5C4A3",
+": c #7F9F76",
+"< c #A6BFA0",
+"[ c #ABC7AA",
+"} c #A7C5A4",
+"| c #A9C7A6",
+"1 c #AFC8AD",
+"2 c #A4C3A2",
+"3 c #6B9060",
+"4 c #778E6F",
+"5 c #698D60",
+"6 c #6B9063",
+"7 c #445B2C",
+"8 c #6B8661",
+"9 c #5B7950",
+"0 c #6C8562",
+"a c #65815C",
+"b c #506B46",
+" . ",
+" .. ",
+" .+. ",
+" .@#. ",
+" ........$%&. ",
+" .*=-;;;;>,'&) ",
+" .!~{{{]^'/(_:. ",
+" .<[^}^|{%'{123. ",
+" .45666666666657. ",
+" .8999999999997. ",
+" .099999999997. ",
+" .abbbbbb9997. ",
+" ........b97. ",
+" .b7. ",
+" .7. ",
+" .. ",
+" . ",
+" "};
diff --git a/lisp/bindings.el b/lisp/bindings.el
index a7f6643b2db..5e709a6b968 100644
--- a/lisp/bindings.el
+++ b/lisp/bindings.el
@@ -1119,6 +1119,7 @@ or \\[semantic-mode]")))))
(define-key ctl-x-map "m" 'compose-mail)
(define-key ctl-x-4-map "m" 'compose-mail-other-window)
(define-key ctl-x-5-map "m" 'compose-mail-other-frame)
+(define-key ctl-x-7-map "m" 'compose-mail-other-tab)
(defvar ctl-x-r-map (make-sparse-keymap)
diff --git a/lisp/dired.el b/lisp/dired.el
index 0dc53bf32c4..26ed3957003 100644
--- a/lisp/dired.el
+++ b/lisp/dired.el
@@ -699,6 +699,13 @@ If DIRNAME is already in a dired buffer, that buffer is used without refresh."
(interactive (dired-read-dir-and-switches "in other frame "))
(switch-to-buffer-other-frame (dired-noselect dirname switches)))
+;;;###autoload (define-key ctl-x-7-map "d" 'dired-other-tab)
+;;;###autoload
+(defun dired-other-tab (dirname &optional switches)
+ "\"Edit\" directory DIRNAME. Like `dired' but makes a new tab."
+ (interactive (dired-read-dir-and-switches "in other tab "))
+ (switch-to-buffer-other-tab (dired-noselect dirname switches)))
+
;;;###autoload
(defun dired-noselect (dir-or-list &optional switches)
"Like `dired' but returns the dired buffer as value, does not select it."
diff --git a/lisp/emacs-lisp/find-func.el b/lisp/emacs-lisp/find-func.el
index 216d91baa7b..b678d59ada3 100644
--- a/lisp/emacs-lisp/find-func.el
+++ b/lisp/emacs-lisp/find-func.el
@@ -558,10 +558,12 @@ Set mark before moving, if the buffer already existed."
(define-key ctl-x-map "F" 'find-function)
(define-key ctl-x-4-map "F" 'find-function-other-window)
(define-key ctl-x-5-map "F" 'find-function-other-frame)
+ (define-key ctl-x-7-map "F" 'find-function-other-tab)
(define-key ctl-x-map "K" 'find-function-on-key)
(define-key ctl-x-map "V" 'find-variable)
(define-key ctl-x-4-map "V" 'find-variable-other-window)
- (define-key ctl-x-5-map "V" 'find-variable-other-frame))
+ (define-key ctl-x-5-map "V" 'find-variable-other-frame)
+ (define-key ctl-x-7-map "V" 'find-variable-other-tab))
(provide 'find-func)
diff --git a/lisp/files.el b/lisp/files.el
index b341fe71076..c65e1e87b5f 100644
--- a/lisp/files.el
+++ b/lisp/files.el
@@ -6354,5 +6354,11 @@ Otherwise, trash FILENAME using the freedesktop.org conventions,
(define-key ctl-x-5-map "r" 'find-file-read-only-other-frame)
(define-key ctl-x-5-map "\C-o" 'display-buffer-other-frame)
+(define-key ctl-x-7-map "b" 'switch-to-buffer-other-tab)
+(define-key ctl-x-7-map "f" 'find-file-other-tab)
+(define-key ctl-x-7-map "\C-f" 'find-file-other-tab)
+(define-key ctl-x-7-map "r" 'find-file-read-only-other-tab)
+(define-key ctl-x-7-map "\C-o" 'display-buffer-other-tab)
+
;; arch-tag: bc68d3ea-19ca-468b-aac6-3a4a7766101f
;;; files.el ends here
diff --git a/lisp/loadup.el b/lisp/loadup.el
index 85222ce7d9e..fa5e7ae1e8e 100644
--- a/lisp/loadup.el
+++ b/lisp/loadup.el
@@ -197,7 +197,9 @@
(load "image")
(load "international/fontset")
(load "dnd")
- (load "tool-bar")))
+ (load "tool-bar")
+ (load "tab-bar")
+ (load "tab")))
(if (or (featurep 'system-font-setting) (featurep 'font-render-setting))
(load "font-setting"))
diff --git a/lisp/menu-bar.el b/lisp/menu-bar.el
index d831744f311..3f8d6829fdc 100644
--- a/lisp/menu-bar.el
+++ b/lisp/menu-bar.el
@@ -981,6 +981,12 @@ mail status in mode line"))
:visible (display-graphic-p)
:button (:toggle . (> (frame-parameter nil 'tool-bar-lines) 0))))
+(define-key menu-bar-showhide-menu [showhide-tab-bar]
+ `(menu-item ,(purecopy "Tab-bar") toggle-tab-bar-mode-from-frame
+ :help ,(purecopy "Turn tab-bar on/off")
+ :visible (display-graphic-p)
+ :button (:toggle . (> (frame-parameter nil 'tab-bar-lines) 0))))
+
(define-key menu-bar-options-menu [showhide]
`(menu-item ,(purecopy "Show/Hide") ,menu-bar-showhide-menu))
diff --git a/lisp/progmodes/etags.el b/lisp/progmodes/etags.el
index 23e175cbe7d..fd354b05e18 100644
--- a/lisp/progmodes/etags.el
+++ b/lisp/progmodes/etags.el
@@ -995,6 +995,31 @@ See documentation of variable `tags-file-name'."
;;;###autoload (define-key ctl-x-5-map "." 'find-tag-other-frame)
;;;###autoload
+(defun find-tag-other-tab (tagname &optional next-p)
+ "Find tag (in current tags table) whose name contains TAGNAME.
+Select the buffer containing the tag's definition in another tab, and
+move point there. The default for TAGNAME is the expression in the buffer
+around or before point.
+
+If second arg NEXT-P is t (interactively, with prefix arg), search for
+another tag that matches the last tagname or regexp used. When there are
+multiple matches for a tag, more exact matches are found first. If NEXT-P
+is negative (interactively, with prefix arg that is a negative number or
+just \\[negative-argument]), pop back to the previous tag gone to.
+
+If third arg REGEXP-P is non-nil, treat TAGNAME as a regexp.
+
+A marker representing the point when this command is invoked is pushed
+onto a ring and may be popped back to with \\[pop-tag-mark].
+Contrast this with the ring of marks gone to by the command.
+
+See documentation of variable `tags-file-name'."
+ (interactive (find-tag-interactive "Find tag other tab: "))
+ (let ((pop-up-tabs t))
+ (find-tag-other-window tagname next-p)))
+;;;###autoload (define-key ctl-x-7-map "." 'find-tag-other-tab)
+
+;;;###autoload
(defun find-tag-regexp (regexp &optional next-p other-window)
"Find tag (in current tags table) whose name matches REGEXP.
Select the buffer containing the tag's definition and move point there.
diff --git a/lisp/startup.el b/lisp/startup.el
index 87f1a00bd54..e8a6dc3dbb5 100644
--- a/lisp/startup.el
+++ b/lisp/startup.el
@@ -603,6 +603,12 @@ or `CVS', and any subdirectory that contains a file named `.nosearch'."
(defvar tool-bar-originally-present nil
"Non-nil if tool-bars are present before user and site init files are read.")
+(defconst tab-bar-images-pixel-height 18
+ "Height in pixels of images in the tab-bar.")
+
+(defvar tab-bar-originally-present nil
+ "Non-nil if tab-bars are present before user and site init files are read.")
+
(defvar handle-args-function-alist '((nil . tty-handle-args))
"Functions for processing window-system dependent command-line arguments.
Window system startup files should add their own function to this
@@ -687,6 +693,8 @@ opening the first frame (e.g. open a connection to an X server).")
(attribute class &optional component subclass))
(declare-function tool-bar-mode "tool-bar" (&optional arg))
(declare-function tool-bar-setup "tool-bar")
+(declare-function tab-bar-mode "tab-bar" (&optional arg))
+(declare-function tab-bar-setup "tab-bar")
(defvar server-name)
(defvar server-process)
@@ -910,6 +918,19 @@ opening the first frame (e.g. open a connection to an X server).")
;; Otherwise, enable tool-bar-mode.
(tool-bar-mode 1)))
+ ;; (unless (or noninteractive (not (fboundp 'tab-bar-mode)))
+ ;; ;; Set up the tab-bar. Do this even in tty frames, so that there
+ ;; ;; is a tab-bar if Emacs later opens a graphical frame.
+ ;; (if (or emacs-basic-display
+ ;; (and (numberp (frame-parameter nil 'tab-bar-lines))
+ ;; (<= (frame-parameter nil 'tab-bar-lines) 0)))
+ ;; ;; On a graphical display with the tabbar disabled via X
+ ;; ;; resources, set up the tabbar without enabling it.
+ ;; (tab-bar-setup)
+ ;; ;; Otherwise, enable tab-bar-mode.
+ ;; (tab-bar-mode 1)))
+ (tab-bar-mode 0)
+
;; Re-evaluate predefined variables whose initial value depends on
;; the runtime context.
(mapc 'custom-reevaluate-setting
@@ -944,6 +965,18 @@ opening the first frame (e.g. open a connection to an X server).")
(cdr tool-bar-lines)
(not (eq 0 (cdr tool-bar-lines)))))))
+ ;; Record whether the tab-bar is present before the user and site
+ ;; init files are processed. frame-notice-user-settings uses this
+ ;; to determine if the tab-bar has been disabled by the init files,
+ ;; and the frame needs to be resized.
+ (when (fboundp 'frame-notice-user-settings)
+ (let ((tab-bar-lines (or (assq 'tab-bar-lines initial-frame-alist)
+ (assq 'tab-bar-lines default-frame-alist))))
+ (setq tab-bar-originally-present
+ (and tab-bar-lines
+ (cdr tab-bar-lines)
+ (not (eq 0 (cdr tab-bar-lines)))))))
+
(let ((old-scalable-fonts-allowed scalable-fonts-allowed)
(old-font-list-limit font-list-limit)
(old-face-ignored-fonts face-ignored-fonts))
diff --git a/lisp/subr.el b/lisp/subr.el
index 6118c49530c..eab82cfe86a 100644
--- a/lisp/subr.el
+++ b/lisp/subr.el
@@ -760,6 +760,11 @@ The normal global definition of the character C-x indirects to this keymap.")
(defalias 'ctl-x-5-prefix ctl-x-5-map)
(define-key ctl-x-map "5" 'ctl-x-5-prefix)
+(defvar ctl-x-7-map (make-sparse-keymap)
+ "Keymap for tab commands.")
+(defalias 'ctl-x-7-prefix ctl-x-7-map)
+(define-key ctl-x-map "7" 'ctl-x-7-prefix)
+
;;;; Event manipulation functions.
diff --git a/lisp/tab-bar.el b/lisp/tab-bar.el
new file mode 100644
index 00000000000..35698d8d245
--- /dev/null
+++ b/lisp/tab-bar.el
@@ -0,0 +1,266 @@
+;;; tab-bar.el --- setting up the tab bar
+
+;; Copyright (C) 2010 Free Software Foundation, Inc.
+
+;; Author: Juri Linkov <juri@jurta.org>
+;; Maintainer: FSF
+;; Keywords: frames internal mouse
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; Provides `tab-bar-mode' to control display of the tab-bar and
+;; bindings for the global tab bar with convenience function
+;; `tab-bar-add-item'.
+
+;; The normal global binding for [tab-bar] (below) uses the value of
+;; `tab-bar-map' as the actual keymap to define the tab bar. Modes
+;; may either bind items under the [tab-bar] prefix key of the local
+;; map to add to the global bar or may set `tab-bar-map'
+;; buffer-locally to override it.
+
+;;; Code:
+
+(defgroup tab-bar nil
+ "Tab bar."
+ :group 'frames)
+
+(defface tab-bar
+ '((default
+ :box (:line-width 1 :style pressed-button)
+ :foreground "black"
+ :background "gray78")
+ (((type x w32 ns) (class color))
+ :background "gray78")
+ (((type x) (class mono))
+ :background "grey"))
+ "Basic tab-bar face."
+ :group 'tab-bar
+ :version "24.1")
+
+(defface tab-selected
+ '((default
+ :box (:line-width 1 :style released-button)
+ :foreground "black"
+ :background "gray92")
+ (((type x w32 ns) (class color))
+ :background "gray92")
+ (((type x) (class mono))
+ :background "grey"))
+ "Selected tab face."
+ :group 'tab-bar
+ :version "24.1")
+
+(defface tab
+ '((default
+ :box (:line-width 1 :style pressed-button)
+ :foreground "black"
+ :background "gray78")
+ (((type x w32 ns) (class color))
+ :background "gray78")
+ (((type x) (class mono))
+ :background "grey"))
+ "Inactive tab face."
+ :group 'tab-bar
+ :version "24.1")
+
+
+(define-minor-mode tab-bar-mode
+ "Toggle use of the tab bar.
+With numeric ARG, display the tab bar if and only if ARG is positive.
+
+See `tab-bar-add-item' for conveniently adding tab bar items."
+ :init-value nil
+ :global t
+ :group 'mouse
+ :group 'frames
+ (if tab-bar-mode
+ (progn
+ (modify-all-frames-parameters (list (cons 'tab-bar-lines 1)))
+ (when (<= 1 (length (default-value 'tab-bar-map))) ; not yet setup
+ ;;(make-tab-command)
+ (tab-bar-setup))
+ (setq tab-frames (frame-list))
+ (add-hook 'window-configuration-change-hook 'tab-window-configuration-change))
+ (modify-all-frames-parameters (list (cons 'tab-bar-lines 0)))
+ (remove-hook 'window-configuration-change-hook 'tab-window-configuration-change)))
+
+;;;###autoload
+;; Used in the Show/Hide menu, to have the toggle reflect the current frame.
+(defun toggle-tab-bar-mode-from-frame (&optional arg)
+ "Toggle tab bar on or off, based on the status of the current frame.
+See `tab-bar-mode' for more information."
+ (interactive (list (or current-prefix-arg 'toggle)))
+ (if (eq arg 'toggle)
+ (tab-bar-mode (if (> (frame-parameter nil 'tab-bar-lines) 0) 0 1))
+ (tab-bar-mode arg)))
+
+;;;###autoload
+(put 'tab-bar-mode 'standard-value '(t))
+
+(defvar tab-bar-map (make-sparse-keymap)
+ "Keymap for the tab bar.
+Define this locally to override the global tab bar.")
+
+(global-set-key [tab-bar]
+ `(menu-item ,(purecopy "tab bar") ignore
+ :filter tab-bar-make-keymap))
+
+(declare-function image-mask-p "image.c" (spec &optional frame))
+
+(defconst tab-bar-keymap-cache (make-hash-table :weakness t :test 'equal))
+
+(defun tab-bar-make-keymap (&optional ignore)
+ "Generate an actual keymap from `tab-bar-map'.
+Its main job is to figure out which images to use based on the display's
+color capability and based on the available image libraries."
+ (let ((key (cons (frame-terminal) tab-bar-map)))
+ (or ;; FIXME: commented out: (gethash key tab-bar-keymap-cache)
+ (puthash key (tab-bar-make-keymap-1) tab-bar-keymap-cache))))
+
+(defun tab-bar-make-keymap-1 ()
+ "Generate an actual keymap from `tab-bar-map', without caching."
+ (mapcar (lambda (bind)
+ (let (image-exp plist)
+ (when (and (eq (car-safe (cdr-safe bind)) 'menu-item)
+ ;; For the format of menu-items, see node
+ ;; `Extended Menu Items' in the Elisp manual.
+ (setq plist (nthcdr (if (consp (nth 4 bind)) 5 4)
+ bind))
+ (setq image-exp (plist-get plist :image))
+ (consp image-exp)
+ (not (eq (car image-exp) 'image))
+ (fboundp (car image-exp)))
+ (if (not (display-images-p))
+ (setq bind nil)
+ (let ((image (eval image-exp)))
+ (unless (and image (image-mask-p image))
+ (setq image (append image '(:mask heuristic))))
+ (setq bind (copy-sequence bind)
+ plist (nthcdr (if (consp (nth 4 bind)) 5 4)
+ bind))
+ (plist-put plist :image image))))
+ bind))
+ tab-bar-map))
+
+;;;###autoload
+(defun tab-bar-add-item (icon name def key selected &rest props)
+ "Add an item to the tab bar.
+ICON names the image, DEF is the key definition and KEY is a symbol
+for the fake function key in the menu keymap. Remaining arguments
+PROPS are additional items to add to the menu item specification. See
+Info node `(elisp)Tab Bar'. Items are added from left to right.
+
+ICON is the base name of a file containing the image to use. The
+function will first try to use low-color/ICON.xpm if `display-color-cells'
+is less or equal to 256, then ICON.xpm, then ICON.pbm, and finally
+ICON.xbm, using `find-image'.
+
+Use this function only to make bindings in the global value of `tab-bar-map'.
+To define items in any other map, use `tab-bar-local-item'."
+ (apply 'tab-bar-local-item icon name def key
+ (default-value 'tab-bar-map) selected props))
+
+;;;###autoload
+(defun tab-bar-local-item (icon name def key map selected &rest props)
+ "Add an item to the tab bar in map MAP.
+ICON names the image, DEF is the key definition and KEY is a symbol
+for the fake function key in the menu keymap. Remaining arguments
+PROPS are additional items to add to the menu item specification. See
+Info node `(elisp)Tab Bar'. Items are added from left to right.
+
+ICON is the base name of a file containing the image to use. The
+function will first try to use low-color/ICON.xpm if `display-color-cells'
+is less or equal to 256, then ICON.xpm, then ICON.pbm, and finally
+ICON.xbm, using `find-image'."
+ (if (null icon)
+ (define-key-after map (vector key)
+ `(menu-item ,(propertize name
+ 'face (if selected 'tab-selected 'tab)
+ 'mouse-face 'highlight)
+ ,def ,@props))
+ (let* ((fg (face-attribute 'tab-bar :foreground))
+ (bg (face-attribute 'tab-bar :background))
+ (colors (nconc (if (eq fg 'unspecified) nil (list :foreground fg))
+ (if (eq bg 'unspecified) nil (list :background bg))))
+ (xpm-spec (list :type 'xpm :file (concat icon ".xpm")))
+ (xpm-lo-spec (list :type 'xpm :file
+ (concat "low-color/" icon ".xpm")))
+ (pbm-spec (append (list :type 'pbm :file
+ (concat icon ".pbm")) colors))
+ (xbm-spec (append (list :type 'xbm :file
+ (concat icon ".xbm")) colors))
+ (image-exp `(find-image
+ (cond ((not (display-color-p))
+ ',(list pbm-spec xbm-spec xpm-lo-spec xpm-spec))
+ ((< (display-color-cells) 256)
+ ',(list xpm-lo-spec xpm-spec pbm-spec xbm-spec))
+ (t
+ ',(list xpm-spec pbm-spec xbm-spec))))))
+ (define-key-after map (vector key)
+ `(menu-item ,(if name
+ (propertize name
+ 'face (if selected 'tab-selected 'tab)
+ 'mouse-face 'highlight))
+ ,def :image ,image-exp ,@props)))))
+
+;;; Set up some global items. Additions/deletions up for grabs.
+
+(defun tab-bar-setup ()
+ (setq tab-bar-map (make-sparse-keymap))
+ (tab-bar-add-item "tab-left"
+ ""
+ 'tab-history-back
+ 'tab-history-back
+ nil
+ :enable 'tab-history-back
+ :help "Go back in history")
+ (tab-bar-add-item "tab-right"
+ ""
+ 'tab-history-forward
+ 'tab-history-forward
+ nil
+ :enable 'tab-history-forward
+ :help "Go forward in history")
+ (let ((selected-tab (selected-tab)))
+ (dolist (tab (tab-list))
+ (let ((tab-id (car tab))
+ (tab-name (or (cdr (assoc 'name (nth 1 tab))) (tab-name))))
+ (when tab-id
+ (tab-bar-add-item nil
+ tab-name
+ `(lambda ()
+ (interactive)
+ (select-tab ',tab-id))
+ tab-id
+ (eq selected-tab tab-id)
+ :enable `(not (eq (selected-tab) ',tab-id))
+ :help "Select this tab")
+ (tab-bar-add-item "tab-delete"
+ ""
+ `(lambda ()
+ (interactive)
+ (delete-tab ',tab-id))
+ (intern (concat "delete-" (symbol-name tab-id)))
+ nil
+ :help "Delete this tab")))))
+ ;; (redraw-frame (selected-frame))
+ )
+
+(provide 'tab-bar)
+
+;;; tab-bar.el ends here
diff --git a/lisp/tab.el b/lisp/tab.el
new file mode 100644
index 00000000000..09e9c454b5e
--- /dev/null
+++ b/lisp/tab.el
@@ -0,0 +1,290 @@
+;;; tab.el --- commands for tabs management
+
+;; Copyright (C) 2010 Free Software Foundation, Inc.
+
+;; Author: Juri Linkov <juri@jurta.org>
+;; Maintainer: FSF
+;; Keywords: frames internal mouse
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; Tab functions.
+
+;;; Code:
+
+(defgroup tab nil
+ "Tabs."
+ :group 'frames)
+
+(defcustom tab-initial-buffer nil
+ "Buffer to show in a new tab.
+If the value is nil, show the current buffer from the old tab.
+If the value is a string, visit the buffer with the specified buffer name.
+If the value is a function, call it and switch to the buffer it returns."
+ :type '(choice
+ (string :tag "Buffer name" :value "*scratch*")
+ (function :tag "Function name")
+ (other :tag "Current buffer" nil))
+ :group 'tab
+ :version "24.1")
+
+(defcustom tab-name nil
+ "The name of the current tab to display in the tab bar."
+ :type '(choice
+ (const :tag "Buffer names of all windows" window-list)
+ (function :tag "Function name")
+ (other :tag "Current buffer" nil))
+ :version "24.1")
+
+(defun tab-name ()
+ (cond
+ ((eq tab-name 'window-list)
+ (mapconcat
+ (lambda (w) (buffer-name (window-buffer w)))
+ (window-list)
+ ", "))
+ ((functionp tab-name)
+ (funcall tab-name))
+ (t
+ (buffer-name))))
+
+(defun tab-initial-buffer ()
+ (cond
+ ((stringp tab-initial-buffer)
+ (switch-to-buffer (get-buffer-create tab-initial-buffer)))
+ ((functionp tab-initial-buffer)
+ (switch-to-buffer (funcall tab-initial-buffer)))
+ (t
+ )))
+
+(defun make-tab (&optional frame parameters after)
+ "Create tab with PARAMETERS, right after AFTER's existing tab.
+FRAME nil or omitted means use the selected frame.
+Optional argument PARAMETERS is an alist of parameters for the new frame.
+Each element of PARAMETERS should have the form (NAME . VALUE), for example:
+
+ (name . STRING) The frame should be named STRING.
+
+If AFTER is t or omitted, the new tab goes at the end of the tab list.
+
+Return a newly created frame displaying the current buffer."
+ (unless tab-bar-mode
+ (tab-bar-mode 1))
+ (let* ((tab-list (tab-list frame))
+ (tab-new (list (tab-gensym)
+ parameters
+ (current-window-configuration frame)
+ nil ;; tab-history-back
+ nil ;; tab-history-forward
+ )))
+ ;; FIXME: use `AFTER'.
+ (modify-frame-parameters
+ frame
+ (list (cons 'tab-list (append tab-list (list tab-new)))))
+ (tab-initial-buffer)
+ (tab-bar-setup)
+ (car tab-new)))
+
+;;;###autoload
+(defun make-tab-command ()
+ "Make a new tab, on the same frame as the selected tab.
+If the terminal is a text-only terminal, this also selects the
+new tab."
+ (interactive)
+ (select-tab (make-tab nil `((name . ,(tab-name))))))
+
+(defun tab-list (&optional frame)
+ "Return a list of all tabs on FRAME.
+FRAME nil or omitted means use the selected frame.
+Return the tab list from FRAME's `tab-list' frame parameter."
+ (cdr (assoc 'tab-list (frame-parameters frame))))
+
+(defun selected-tab (&optional frame)
+ "Return the tab that is now selected on FRAME.
+FRAME nil or omitted means use the selected frame."
+ (cdr (assoc 'selected-tab (frame-parameters frame))))
+
+;;;###autoload
+(defun select-tab (tab &optional frame norecord)
+ "Select TAB on FRAME.
+FRAME nil or omitted means use the selected frame.
+Subsequent editing commands apply to its selected window.
+Optional argument NORECORD means to neither change the order of
+recently selected windows nor the buffer list.
+
+The selection of TAB lasts until the next time the user does
+something to select a different tab, or until the next time
+this function is called.
+
+This function returns TAB, or nil if TAB has been deleted."
+ ;; Save current win conf
+ (let* ((selected-tab (selected-tab))
+ (tab-list (tab-list frame))
+ (tab-param (assq selected-tab tab-list))
+ (tab-name (assq 'name (nth 1 tab-param))))
+ (when tab-param
+ (setcar (cddr tab-param) (current-window-configuration frame))
+ (setcar (cdr (cddr tab-param)) tab-history-back)
+ (setcar (cddr (cddr tab-param)) tab-history-forward)
+ (if tab-name (setcdr tab-name (tab-name))))
+ (modify-frame-parameters frame (list (cons 'selected-tab tab)))
+ (set-window-configuration (nth 2 (assq tab tab-list)))
+ (setq tab-history-back (nth 3 (assq tab tab-list)))
+ (setq tab-history-forward (nth 4 (assq tab tab-list)))
+ (tab-bar-setup)))
+
+(defun delete-tab (&optional tab frame)
+ "Remove TAB from its FRAME.
+TAB defaults to the selected tab. Return nil.
+FRAME nil or omitted means use the selected frame.
+Signal an error when TAB is the only tab on its frame."
+ (interactive)
+ (let* ((selected-tab (selected-tab))
+ (tab (or tab selected-tab))
+ (tab-list (tab-list frame))
+ (tab-param (assq tab tab-list))
+ (tab-next (and (eq tab selected-tab)
+ (caar (or (cdr (member tab-param tab-list))
+ (cdr (member tab-param (reverse tab-list))))))))
+ (modify-frame-parameters
+ frame
+ (list (cons 'tab-list (assq-delete-all tab (tab-list)))))
+ (if (null (tab-list))
+ (tab-bar-mode 0)
+ (if tab-next (select-tab tab-next))
+ (tab-bar-setup))))
+
+
+;;; Tab identity (until it's first-class object).
+
+;; Adapted from `gensym' in lisp/emacs-lisp/cl-macs.el.
+(defvar tab-gensym-index 1)
+
+(defun tab-gensym (&optional prefix)
+ "Generate a new interned symbol.
+The name is made by appending a number to PREFIX, default \"tab-\"."
+ (let ((pfix (if (stringp prefix) prefix "tab-"))
+ (num (if (integerp prefix) prefix
+ (prog1 tab-gensym-index
+ (setq tab-gensym-index (1+ tab-gensym-index))))))
+ (intern (format "%s%d" pfix num))))
+
+
+;;; Tab history.
+
+(defvar tab-history-back nil
+ "Stack of window configurations user has visited.
+Each element of the stack is a window configuration.")
+
+(defvar tab-history-forward nil
+ "Stack of window configurations user has visited with `tab-history-back' command.
+Each element of the stack is a window configuration.")
+
+(defun tab-history-back ()
+ (interactive)
+ (let ((win-conf (cadr tab-history-back)))
+ (when win-conf
+ (push (pop tab-history-back) tab-history-forward)
+ (set-window-configuration win-conf))))
+
+(defun tab-history-forward ()
+ (interactive)
+ (let ((win-conf (car tab-history-forward)))
+ (when win-conf
+ (push (pop tab-history-forward) tab-history-back)
+ (set-window-configuration win-conf))))
+
+(defun tab-history-update ()
+ (push (current-window-configuration) tab-history-back))
+
+(defun tab-name-update ()
+ (let* ((selected-tab (selected-tab))
+ (tab-list (tab-list))
+ (tab-param (assq selected-tab tab-list))
+ (tab-name (assq 'name (nth 1 tab-param))))
+ (if tab-name (setcdr tab-name (tab-name)))
+ (tab-bar-setup)))
+
+(defvar tab-frames nil)
+
+(defun tab-window-configuration-change ()
+ (when (or (memq (selected-frame) tab-frames)
+ (/= 0 (minibuffer-depth)))
+ (tab-name-update)
+ (tab-history-update)))
+
+
+;;; List tabs.
+
+(defun list-tabs (&optional frame)
+ "Display a list of names of existing tabs.
+The list is displayed in a tab named `*Tab List*'.
+FRAME nil or omitted means use the selected frame.
+For more information, see the function `tab-menu'."
+ (interactive "P")
+ (switch-to-buffer (list-tabs-noselect frame)))
+
+(defvar list-tabs-column 3)
+
+(defun list-tabs-noselect (&optional frame)
+ "Create and return a buffer with a list of names of existing tabs.
+The buffer is named `*Tab List*'.
+FRAME nil or omitted means use the selected frame.
+For more information, see the function `tab-menu'."
+ (let ((tab-list (tab-list)))
+ (with-current-buffer (get-buffer-create "*Tab List*")
+ (setq buffer-read-only nil)
+ (erase-buffer)
+ ;; Vertical alignment to the center of the frame
+ (insert-char ?\n (/ (- (frame-height) (length tab-list) 1) 2))
+ ;; Horizontal alignment to the center of the frame
+ (setq list-tabs-column (- (/ (frame-width) 2) 15))
+ (dolist (tab tab-list)
+ (insert (propertize
+ (format "%s %s\n"
+ (make-string list-tabs-column ?\040)
+ (propertize
+ (cdr (assq 'name (nth 1 tab)))
+ 'mouse-face 'highlight
+ 'help-echo "mouse-2: select this tab"))
+ 'tab tab)))
+ ;; (tab-menu-mode)
+ (goto-char (point-min))
+ (goto-char (or (next-single-property-change (point) 'tab) (point-min)))
+ ;; (when (> (length tab-list) 1)
+ ;; (tab-menu-next-line))
+ (move-to-column list-tabs-column)
+ (set-buffer-modified-p nil)
+ (delete-other-windows)
+ (current-buffer))))
+
+(define-key ctl-x-7-map "\C-b" 'list-tabs)
+
+
+;;;; Key bindings
+
+(define-key ctl-x-7-map "2" 'make-tab-command)
+(define-key ctl-x-7-map "1" 'delete-other-tabs)
+(define-key ctl-x-7-map "0" 'delete-tab)
+(define-key ctl-x-7-map "o" 'other-tab)
+(define-key ctl-x-7-map "n" 'next-tab)
+(define-key ctl-x-7-map "p" 'previous-tab)
+
+(provide 'tab)
+
+;;; tab.el ends here
diff --git a/src/Makefile.in b/src/Makefile.in
index cd5027dc4d8..254f8344bbe 100644
--- a/src/Makefile.in
+++ b/src/Makefile.in
@@ -594,13 +594,13 @@ otherobj= $(termcapobj) lastfile.o $(mallocobj) $(widgetobj) $(LIBOBJS)
#ifdef HAVE_X_WINDOWS
#define WINDOW_SUPPORT ${lispsource}fringe.elc ${lispsource}image.elc \
${lispsource}international/fontset.elc ${lispsource}dnd.elc \
- ${lispsource}tool-bar.elc ${lispsource}mwheel.elc ${lispsource}x-dnd.elc \
+ ${lispsource}tab.elc ${lispsource}tab-bar.elc ${lispsource}tool-bar.elc ${lispsource}mwheel.elc ${lispsource}x-dnd.elc \
${lispsource}term/common-win.elc ${lispsource}term/x-win.elc \
${lispsource}font-setting.elc
#else
#define WINDOW_SUPPORT ${lispsource}fringe.elc ${lispsource}image.elc \
${lispsource}international/fontset.elc ${lispsource}dnd.elc \
- ${lispsource}tool-bar.elc ${lispsource}mwheel.elc
+ ${lispsource}tab.elc ${lispsource}tab-bar.elc ${lispsource}tool-bar.elc ${lispsource}mwheel.elc
#endif
#else
#define WINDOW_SUPPORT
@@ -830,7 +830,7 @@ SOME_MACHINE_LISP = ../lisp/mouse.elc \
../lisp/disp-table.elc ../lisp/dos-vars.elc \
../lisp/tooltip.elc ../lisp/image.elc \
../lisp/fringe.elc ../lisp/dnd.elc \
- ../lisp/mwheel.elc ../lisp/tool-bar.elc \
+ ../lisp/mwheel.elc ../lisp/tool-bar.elc ../lisp/tab-bar.elc ../lisp/tab.elc \
../lisp/x-dnd.elc ../lisp/font-setting.elc \
../lisp/international/ccl.elc \
../lisp/international/fontset.elc \
diff --git a/src/dispextern.h b/src/dispextern.h
index b8f68ec0e70..e9846c81bb9 100644
--- a/src/dispextern.h
+++ b/src/dispextern.h
@@ -1635,6 +1635,7 @@ enum face_id
MODE_LINE_FACE_ID,
MODE_LINE_INACTIVE_FACE_ID,
TOOL_BAR_FACE_ID,
+ TAB_BAR_FACE_ID,
FRINGE_FACE_ID,
HEADER_LINE_FACE_ID,
SCROLL_BAR_FACE_ID,
@@ -2819,6 +2820,80 @@ extern EMACS_INT tool_bar_button_relief;
/***********************************************************************
+ Tab-bars
+ ***********************************************************************/
+
+/* Enumeration defining where to find tab-bar item information in
+ tab-bar items vectors stored with frames. Each tab-bar item
+ occupies TAB_BAR_ITEM_NSLOTS elements in such a vector. */
+
+enum tab_bar_item_idx
+{
+ /* The key of the tab-bar item. Used to remove items when a binding
+ for `undefined' is found. */
+ TAB_BAR_ITEM_KEY,
+
+ /* Non-nil if item is enabled. */
+ TAB_BAR_ITEM_ENABLED_P,
+
+ /* Non-nil if item is selected (pressed). */
+ TAB_BAR_ITEM_SELECTED_P,
+
+ /* Caption. */
+ TAB_BAR_ITEM_CAPTION,
+
+ /* Image(s) to display. This is either a single image specification
+ or a vector of specifications. */
+ TAB_BAR_ITEM_IMAGES,
+
+ /* The binding. */
+ TAB_BAR_ITEM_BINDING,
+
+ /* Button type. One of nil, `:radio' or `:toggle'. */
+ TAB_BAR_ITEM_TYPE,
+
+ /* Help string. */
+ TAB_BAR_ITEM_HELP,
+
+ /* Icon file name of right to left image when an RTL locale is used. */
+ TAB_BAR_ITEM_RTL_IMAGE,
+
+ /* Sentinel = number of slots in tab_bar_items occupied by one
+ tab-bar item. */
+ TAB_BAR_ITEM_NSLOTS
+};
+
+
+/* An enumeration for the different images that can be specified
+ for a tab-bar item. */
+
+enum tab_bar_item_image
+{
+ TAB_BAR_IMAGE_ENABLED_SELECTED,
+ TAB_BAR_IMAGE_ENABLED_DESELECTED,
+ TAB_BAR_IMAGE_DISABLED_SELECTED,
+ TAB_BAR_IMAGE_DISABLED_DESELECTED
+};
+
+/* Margin around tab-bar buttons in pixels. */
+
+extern Lisp_Object Vtab_bar_button_margin;
+
+/* Thickness of relief to draw around tab-bar buttons. */
+
+extern EMACS_INT tab_bar_button_relief;
+
+/* Default values of the above variables. */
+
+#define DEFAULT_TAB_BAR_BUTTON_MARGIN 0
+#define DEFAULT_TAB_BAR_BUTTON_RELIEF 1
+
+/* The height in pixels of the default tab-bar images. */
+
+#define DEFAULT_TAB_BAR_IMAGE_HEIGHT 18
+
+
+/***********************************************************************
Terminal Capabilities
***********************************************************************/
@@ -2896,6 +2971,7 @@ int in_display_vector_p P_ ((struct it *));
int frame_mode_line_height P_ ((struct frame *));
void highlight_trailing_whitespace P_ ((struct frame *, struct glyph_row *));
extern Lisp_Object Qtool_bar;
+extern Lisp_Object Qtab_bar;
extern Lisp_Object Vshow_trailing_whitespace;
extern int mode_line_in_non_selected_windows;
extern int redisplaying_p;
@@ -2907,6 +2983,7 @@ extern Lisp_Object help_echo_object, previous_help_echo_string;
extern int help_echo_pos;
extern struct frame *last_mouse_frame;
extern int last_tool_bar_item;
+extern int last_tab_bar_item;
extern Lisp_Object Vmouse_autoselect_window;
extern int unibyte_display_via_language_environment;
extern EMACS_INT underline_minimum_offset;
@@ -2963,6 +3040,8 @@ extern void cancel_mouse_face P_ ((struct frame *));
extern void handle_tool_bar_click P_ ((struct frame *,
int, int, int, unsigned int));
+extern void handle_tab_bar_click P_ ((struct frame *,
+ int, int, int, unsigned int));
/* msdos.c defines its own versions of these functions. */
extern int clear_mouse_face P_ ((Display_Info *));
diff --git a/src/dispnew.c b/src/dispnew.c
index 2be00c9c3b0..30ef5b533f4 100644
--- a/src/dispnew.c
+++ b/src/dispnew.c
@@ -1032,6 +1032,10 @@ clear_current_matrices (f)
if (WINDOWP (f->tool_bar_window))
clear_glyph_matrix (XWINDOW (f->tool_bar_window)->current_matrix);
+ /* Clear the matrix of the tab-bar window, if any. */
+ if (WINDOWP (f->tab_bar_window))
+ clear_glyph_matrix (XWINDOW (f->tab_bar_window)->current_matrix);
+
/* Clear current window matrices. */
xassert (WINDOWP (FRAME_ROOT_WINDOW (f)));
clear_window_matrices (XWINDOW (FRAME_ROOT_WINDOW (f)), 0);
@@ -1053,6 +1057,9 @@ clear_desired_matrices (f)
if (WINDOWP (f->tool_bar_window))
clear_glyph_matrix (XWINDOW (f->tool_bar_window)->desired_matrix);
+ if (WINDOWP (f->tab_bar_window))
+ clear_glyph_matrix (XWINDOW (f->tab_bar_window)->desired_matrix);
+
/* Do it for window matrices. */
xassert (WINDOWP (FRAME_ROOT_WINDOW (f)));
clear_window_matrices (XWINDOW (FRAME_ROOT_WINDOW (f)), 1);
@@ -2451,6 +2458,24 @@ adjust_frame_glyphs_for_window_redisplay (f)
XSETFASTINT (w->total_lines, FRAME_TOOL_BAR_LINES (f));
XSETFASTINT (w->total_cols, FRAME_TOTAL_COLS (f));
allocate_matrices_for_window_redisplay (w);
+
+ /* Allocate/ reallocate matrices of the tab bar window. If we
+ don't have a tab bar window yet, make one. */
+ if (NILP (f->tab_bar_window))
+ {
+ f->tab_bar_window = make_window ();
+ w = XWINDOW (f->tab_bar_window);
+ XSETFRAME (w->frame, f);
+ w->pseudo_window_p = 1;
+ }
+ else
+ w = XWINDOW (f->tab_bar_window);
+
+ XSETFASTINT (w->top_line, FRAME_MENU_BAR_LINES (f) + FRAME_TOOL_BAR_LINES (f));
+ XSETFASTINT (w->left_col, 0);
+ XSETFASTINT (w->total_lines, FRAME_TAB_BAR_LINES (f));
+ XSETFASTINT (w->total_cols, FRAME_TOTAL_COLS (f));
+ allocate_matrices_for_window_redisplay (w);
#endif
}
@@ -2539,6 +2564,16 @@ free_glyphs (f)
f->tool_bar_window = Qnil;
}
+ /* Free the tab bar window and its glyph matrices. */
+ if (!NILP (f->tab_bar_window))
+ {
+ struct window *w = XWINDOW (f->tab_bar_window);
+ free_glyph_matrix (w->desired_matrix);
+ free_glyph_matrix (w->current_matrix);
+ w->desired_matrix = w->current_matrix = NULL;
+ f->tab_bar_window = Qnil;
+ }
+
/* Release frame glyph matrices. Reset fields to zero in
case we are called a second time. */
if (f->desired_matrix)
@@ -3536,6 +3571,28 @@ update_frame (f, force_p, inhibit_hairy_id_p)
}
+ /* Update the tab-bar window, if present. */
+ if (WINDOWP (f->tab_bar_window))
+ {
+ struct window *w = XWINDOW (f->tab_bar_window);
+
+ /* Update tab-bar window. */
+ if (w->must_be_updated_p)
+ {
+ Lisp_Object tem;
+
+ update_window (w, 1);
+ w->must_be_updated_p = 0;
+
+ /* Swap tab-bar strings. We swap because we want to
+ reuse strings. */
+ tem = f->current_tab_bar_string;
+ f->current_tab_bar_string = f->desired_tab_bar_string;
+ f->desired_tab_bar_string = tem;
+ }
+ }
+
+
/* Update windows. */
paused_p = update_window_tree (root_window, force_p);
update_end (f);
@@ -6002,6 +6059,9 @@ change_frame_size_1 (f, newheight, newwidth, pretend, delay, safe)
if (WINDOWP (f->tool_bar_window))
XSETFASTINT (XWINDOW (f->tool_bar_window)->total_cols, newwidth);
+
+ if (WINDOWP (f->tab_bar_window))
+ XSETFASTINT (XWINDOW (f->tab_bar_window)->total_cols, newwidth);
}
FRAME_LINES (f) = newheight;
diff --git a/src/frame.c b/src/frame.c
index 757ed8f01a3..9c427720103 100644
--- a/src/frame.c
+++ b/src/frame.c
@@ -118,7 +118,7 @@ Lisp_Object Qparent_id;
Lisp_Object Qtitle, Qname;
Lisp_Object Qexplicit_name;
Lisp_Object Qunsplittable;
-Lisp_Object Qmenu_bar_lines, Qtool_bar_lines;
+Lisp_Object Qmenu_bar_lines, Qtool_bar_lines, Qtab_bar_lines;
Lisp_Object Qleft_fringe, Qright_fringe;
Lisp_Object Qbuffer_predicate, Qbuffer_list, Qburied_buffer_list;
Lisp_Object Qtty_color_mode;
@@ -331,6 +331,10 @@ make_frame (mini_p)
f->tool_bar_items = Qnil;
f->desired_tool_bar_string = f->current_tool_bar_string = Qnil;
f->n_tool_bar_items = 0;
+ f->tab_bar_window = Qnil;
+ f->tab_bar_items = Qnil;
+ f->desired_tab_bar_string = f->current_tab_bar_string = Qnil;
+ f->n_tab_bar_items = 0;
f->left_fringe_width = f->right_fringe_width = 0;
f->fringe_cols = 0;
f->scroll_bar_actual_width = 0;
@@ -2659,13 +2663,13 @@ of the result depends on the window-system and toolkit in use:
In the Gtk+ version of Emacs, it includes only any window (including
the minibuffer or eacho area), mode line, and header line. It does not
-include the tool bar or menu bar.
+include the tool bar, the tab bar or menu bar.
-With the Motif or Lucid toolkits, it also includes the tool bar (but
-not the menu bar).
+With the Motif or Lucid toolkits, it also includes the tool bar and
+the tab bar (but not the menu bar).
-In a graphical version with no toolkit, it includes both the tool bar
-and menu bar.
+In a graphical version with no toolkit, it includes the tool bar,
+the tab bar and menu bar.
For a text-only terminal, it includes the menu bar. In this case, the
result is really in characters rather than pixels (i.e., is identical
@@ -2862,6 +2866,7 @@ static struct frame_parm_table frame_parms[] =
{"vertical-scroll-bars", &Qvertical_scroll_bars},
{"visibility", &Qvisibility},
{"tool-bar-lines", &Qtool_bar_lines},
+ {"tab-bar-lines", &Qtab_bar_lines},
{"scroll-bar-foreground", &Qscroll_bar_foreground},
{"scroll-bar-background", &Qscroll_bar_background},
{"screen-gamma", &Qscreen_gamma},
@@ -3439,6 +3444,8 @@ x_set_font (f, arg, oldval)
#endif
/* Recalculate toolbar height. */
f->n_tool_bar_rows = 0;
+ /* Recalculate tabbar height. */
+ f->n_tab_bar_rows = 0;
/* Ensure we redraw it. */
clear_current_matrices (f);
@@ -4196,16 +4203,19 @@ On Nextstep, this just calls `ns-parse-geometry'. */)
Adjust height for toolbar if TOOLBAR_P is 1.
+ Adjust height for tabbar if TABBAR_P is 1.
+
This function does not make the coordinates positive. */
#define DEFAULT_ROWS 35
#define DEFAULT_COLS 80
int
-x_figure_window_size (f, parms, toolbar_p)
+x_figure_window_size (f, parms, toolbar_p, tabbar_p)
struct frame *f;
Lisp_Object parms;
int toolbar_p;
+ int tabbar_p;
{
register Lisp_Object tem0, tem1, tem2;
long window_prompting = 0;
@@ -4283,6 +4293,34 @@ x_figure_window_size (f, parms, toolbar_p)
FRAME_LINES (f) += (bar_height + FRAME_LINE_HEIGHT (f) - 1) / FRAME_LINE_HEIGHT (f);
}
+ /* Add the tab-bar height to the initial frame height so that the
+ user gets a text display area of the size he specified with -g or
+ via .Xdefaults. Later changes of the tab-bar height don't
+ change the frame size. This is done so that users can create
+ tall Emacs frames without having to guess how tall the tab-bar
+ will get. */
+ if (tabbar_p && FRAME_TAB_BAR_LINES (f))
+ {
+ int margin, relief, bar_height;
+
+ relief = (tab_bar_button_relief >= 0
+ ? tab_bar_button_relief
+ : DEFAULT_TAB_BAR_BUTTON_RELIEF);
+
+ if (INTEGERP (Vtab_bar_button_margin)
+ && XINT (Vtab_bar_button_margin) > 0)
+ margin = XFASTINT (Vtab_bar_button_margin);
+ else if (CONSP (Vtab_bar_button_margin)
+ && INTEGERP (XCDR (Vtab_bar_button_margin))
+ && XINT (XCDR (Vtab_bar_button_margin)) > 0)
+ margin = XFASTINT (XCDR (Vtab_bar_button_margin));
+ else
+ margin = 0;
+
+ bar_height = DEFAULT_TAB_BAR_IMAGE_HEIGHT + 2 * margin + 2 * relief;
+ FRAME_LINES (f) += (bar_height + FRAME_LINE_HEIGHT (f) - 1) / FRAME_LINE_HEIGHT (f);
+ }
+
compute_fringe_widths (f, 0);
FRAME_PIXEL_WIDTH (f) = FRAME_TEXT_COLS_TO_PIXEL_WIDTH (f, FRAME_COLS (f));
diff --git a/src/frame.h b/src/frame.h
index 36b0b6b069c..3e94366cbef 100644
--- a/src/frame.h
+++ b/src/frame.h
@@ -194,6 +194,15 @@ struct frame
/* Desired and current contents displayed in tool_bar_window. */
Lisp_Object desired_tool_bar_string, current_tool_bar_string;
+ /* A window used to display the tab-bar of a frame. */
+ Lisp_Object tab_bar_window;
+
+ /* Desired and current tab-bar items. */
+ Lisp_Object tab_bar_items;
+
+ /* Desired and current contents displayed in tab_bar_window. */
+ Lisp_Object desired_tab_bar_string, current_tab_bar_string;
+
/* Beyond here, there should be no more Lisp_Object components. */
/* Cache of realized faces. */
@@ -240,6 +249,10 @@ struct frame
auto-resize-tool-bar is set to grow-only. */
unsigned minimize_tool_bar_window_p : 1;
+ /* Set to non-zero to minimize tab-bar height even when
+ auto-resize-tab-bar is set to grow-only. */
+ unsigned minimize_tab_bar_window_p : 1;
+
#if defined (USE_GTK) || defined (HAVE_NS)
/* Nonzero means using a tool bar that comes from the toolkit. */
int external_tool_bar;
@@ -251,6 +264,12 @@ struct frame
int n_tool_bar_rows;
int n_tool_bar_items;
+ /* Margin at the top of the frame. Used to display the tab-bar. */
+ int tab_bar_lines;
+
+ int n_tab_bar_rows;
+ int n_tab_bar_items;
+
/* A buffer for decode_mode_line. */
char *decode_mode_spec_buffer;
@@ -551,15 +570,15 @@ typedef struct frame *FRAME_PTR;
(If this is 0, F must use some other minibuffer window.) */
#define FRAME_HAS_MINIBUF_P(f) ((f)->has_minibuffer)
-/* Pixel height of frame F, including non-toolkit menu bar and
- non-toolkit tool bar lines. */
+/* Pixel height of frame F, including non-toolkit menu bar,
+ non-toolkit tool bar and non-toolkit tab bar lines. */
#define FRAME_PIXEL_HEIGHT(f) ((f)->pixel_height)
/* Pixel width of frame F. */
#define FRAME_PIXEL_WIDTH(f) ((f)->pixel_width)
/* Height of frame F, measured in canonical lines, including
- non-toolkit menu bar and non-toolkit tool bar lines. */
+ non-toolkit menu bar, non-toolkit tool bar and non-toolkit tab bar lines. */
#define FRAME_LINES(f) (f)->text_lines
/* Width of frame F, measured in canonical character columns,
@@ -585,10 +604,23 @@ typedef struct frame *FRAME_PTR;
#define FRAME_TOOL_BAR_LINES(f) (f)->tool_bar_lines
+/* Nonzero if this frame should display a tab bar
+ in a way that does not use any text lines. */
+/* #if defined (USE_GTK) || defined (HAVE_NS) */
+/* #define FRAME_EXTERNAL_TAB_BAR(f) (f)->external_tab_bar */
+/* #else */
+#define FRAME_EXTERNAL_TAB_BAR(f) 0
+/* #endif */
+
+/* Number of lines of frame F used for the tab-bar. */
+
+#define FRAME_TAB_BAR_LINES(f) (f)->tab_bar_lines
+
+
/* Lines above the top-most window in frame F. */
#define FRAME_TOP_MARGIN(F) \
- (FRAME_MENU_BAR_LINES (F) + FRAME_TOOL_BAR_LINES (F))
+ (FRAME_MENU_BAR_LINES (F) + FRAME_TOOL_BAR_LINES (F) + FRAME_TAB_BAR_LINES (F))
/* Pixel height of the top margin above. */
@@ -1044,7 +1076,7 @@ extern Lisp_Object Qfont;
extern Lisp_Object Qbackground_color, Qforeground_color;
extern Lisp_Object Qicon, Qicon_name, Qicon_type, Qicon_left, Qicon_top;
extern Lisp_Object Qinternal_border_width;
-extern Lisp_Object Qmenu_bar_lines, Qtool_bar_lines;
+extern Lisp_Object Qmenu_bar_lines, Qtool_bar_lines, Qtab_bar_lines;
extern Lisp_Object Qmouse_color;
extern Lisp_Object Qname, Qtitle;
extern Lisp_Object Qparent_id;
@@ -1121,7 +1153,7 @@ extern void x_set_scroll_bar_width P_ ((struct frame *, Lisp_Object,
extern Lisp_Object x_icon_type P_ ((struct frame *));
-extern int x_figure_window_size P_ ((struct frame *, Lisp_Object, int));
+extern int x_figure_window_size P_ ((struct frame *, Lisp_Object, int, int));
extern Lisp_Object Vframe_alpha_lower_limit;
extern void x_set_alpha P_ ((struct frame *, Lisp_Object, Lisp_Object));
diff --git a/src/keyboard.c b/src/keyboard.c
index f2aeff89542..31bcb887f9f 100644
--- a/src/keyboard.c
+++ b/src/keyboard.c
@@ -597,7 +597,7 @@ static EMACS_TIME timer_idleness_start_time;
static EMACS_TIME timer_last_idleness_start_time;
-/* If non-nil, events produced by disabled menu items and tool-bar
+/* If non-nil, events produced by disabled menu items, tool-bar and tab-bar
buttons are not ignored. Help functions bind this to allow help on
those items and buttons. */
Lisp_Object Venable_disabled_menus_and_buttons;
@@ -2490,7 +2490,7 @@ read_char (commandflag, nmaps, maps, prev_event, used_mouse_menu, end_time)
if (used_mouse_menu
/* Also check was_disabled so last-nonmenu-event won't return
a bad value when submenus are involved. (Bug#447) */
- && (EQ (c, Qtool_bar) || EQ (c, Qmenu_bar) || was_disabled))
+ && (EQ (c, Qtool_bar) || EQ (c, Qtab_bar) || EQ (c, Qmenu_bar) || was_disabled))
*used_mouse_menu = 1;
goto reread_for_input_method;
@@ -2775,6 +2775,7 @@ read_char (commandflag, nmaps, maps, prev_event, used_mouse_menu, end_time)
&& EVENT_HAS_PARAMETERS (prev_event)
&& !EQ (XCAR (prev_event), Qmenu_bar)
&& !EQ (XCAR (prev_event), Qtool_bar)
+ && !EQ (XCAR (prev_event), Qtab_bar)
/* Don't bring up a menu if we already have another event. */
&& NILP (Vunread_command_events)
&& unread_command_char < 0)
@@ -3076,7 +3077,7 @@ read_char (commandflag, nmaps, maps, prev_event, used_mouse_menu, end_time)
posn = POSN_POSN (EVENT_START (c));
/* Handle menu-bar events:
insert the dummy prefix event `menu-bar'. */
- if (EQ (posn, Qmenu_bar) || EQ (posn, Qtool_bar))
+ if (EQ (posn, Qmenu_bar) || EQ (posn, Qtool_bar) || EQ (posn, Qtab_bar))
{
/* Change menu-bar to (menu-bar) as the event "position". */
POSN_SET_POSN (EVENT_START (c), Fcons (posn, Qnil));
@@ -4206,7 +4207,8 @@ kbd_buffer_get_event (kbp, used_mouse_menu, end_time)
if (used_mouse_menu
&& !EQ (event->frame_or_window, event->arg)
&& (event->kind == MENU_BAR_EVENT
- || event->kind == TOOL_BAR_EVENT))
+ || event->kind == TOOL_BAR_EVENT
+ || event->kind == TAB_BAR_EVENT))
*used_mouse_menu = 1;
#endif
#ifdef HAVE_NS
@@ -5269,7 +5271,7 @@ make_lispy_position (f, x, y, time)
/* Set `window' to the window under frame pixel coordinates (x,y) */
if (f)
window = window_from_coordinates (f, XINT (*x), XINT (*y),
- &part, &wx, &wy, 0);
+ &part, &wx, &wy, 0, 0);
else
window = Qnil;
@@ -6061,6 +6063,16 @@ make_lispy_event (event)
return apply_modifiers (event->modifiers, event->arg);
return event->arg;
+ case TAB_BAR_EVENT:
+ if (EQ (event->arg, event->frame_or_window))
+ /* This is the prefix key. We translate this to
+ `(tab_bar)' because the code in keyboard.c for tab bar
+ events, which we use, relies on this. */
+ return Fcons (Qtab_bar, Qnil);
+ else if (SYMBOLP (event->arg))
+ return apply_modifiers (event->modifiers, event->arg);
+ return event->arg;
+
case USER_SIGNAL_EVENT:
/* A user signal. */
{
@@ -8434,6 +8446,389 @@ append_tool_bar_item ()
+/***********************************************************************
+ Tab-bars
+ ***********************************************************************/
+
+/* A vector holding tab bar items while they are parsed in function
+ tab_bar_items. Each item occupies TAB_BAR_ITEM_NSCLOTS elements
+ in the vector. */
+
+static Lisp_Object tab_bar_items_vector;
+
+/* A vector holding the result of parse_tab_bar_item. Layout is like
+ the one for a single item in tab_bar_items_vector. */
+
+static Lisp_Object tab_bar_item_properties;
+
+/* Next free index in tab_bar_items_vector. */
+
+static int ntab_bar_items;
+
+/* The symbols `tab-bar', `:image' and `:rtl'. */
+
+extern Lisp_Object Qtab_bar;
+Lisp_Object QCimage;
+Lisp_Object Qrtl;
+
+/* Function prototypes. */
+
+static void init_tab_bar_items P_ ((Lisp_Object));
+static void process_tab_bar_item P_ ((Lisp_Object, Lisp_Object, Lisp_Object, void*));
+static int parse_tab_bar_item P_ ((Lisp_Object, Lisp_Object));
+static void append_tab_bar_item P_ ((void));
+
+
+/* Return a vector of tab bar items for keymaps currently in effect.
+ Reuse vector REUSE if non-nil. Return in *NITEMS the number of
+ tab bar items found. */
+
+Lisp_Object
+tab_bar_items (reuse, nitems)
+ Lisp_Object reuse;
+ int *nitems;
+{
+ Lisp_Object *maps;
+ int nmaps, i;
+ Lisp_Object oquit;
+ Lisp_Object *tmaps;
+
+ *nitems = 0;
+
+ /* In order to build the menus, we need to call the keymap
+ accessors. They all call QUIT. But this function is called
+ during redisplay, during which a quit is fatal. So inhibit
+ quitting while building the menus. We do this instead of
+ specbind because (1) errors will clear it anyway and (2) this
+ avoids risk of specpdl overflow. */
+ oquit = Vinhibit_quit;
+ Vinhibit_quit = Qt;
+
+ /* Initialize tab_bar_items_vector and protect it from GC. */
+ init_tab_bar_items (reuse);
+
+ /* Build list of keymaps in maps. Set nmaps to the number of maps
+ to process. */
+
+ /* Should overriding-terminal-local-map and overriding-local-map apply? */
+ if (!NILP (Voverriding_local_map_menu_flag))
+ {
+ /* Yes, use them (if non-nil) as well as the global map. */
+ maps = (Lisp_Object *) alloca (3 * sizeof (maps[0]));
+ nmaps = 0;
+ if (!NILP (current_kboard->Voverriding_terminal_local_map))
+ maps[nmaps++] = current_kboard->Voverriding_terminal_local_map;
+ if (!NILP (Voverriding_local_map))
+ maps[nmaps++] = Voverriding_local_map;
+ }
+ else
+ {
+ /* No, so use major and minor mode keymaps and keymap property.
+ Note that tab-bar bindings in the local-map and keymap
+ properties may not work reliable, as they are only
+ recognized when the tab-bar (or mode-line) is updated,
+ which does not normally happen after every command. */
+ Lisp_Object tem;
+ int nminor;
+ nminor = current_minor_maps (NULL, &tmaps);
+ maps = (Lisp_Object *) alloca ((nminor + 3) * sizeof (maps[0]));
+ nmaps = 0;
+ if (tem = get_local_map (PT, current_buffer, Qkeymap), !NILP (tem))
+ maps[nmaps++] = tem;
+ bcopy (tmaps, (void *) (maps + nmaps), nminor * sizeof (maps[0]));
+ nmaps += nminor;
+ maps[nmaps++] = get_local_map (PT, current_buffer, Qlocal_map);
+ }
+
+ /* Add global keymap at the end. */
+ maps[nmaps++] = current_global_map;
+
+ /* Process maps in reverse order and look up in each map the prefix
+ key `tab-bar'. */
+ for (i = nmaps - 1; i >= 0; --i)
+ if (!NILP (maps[i]))
+ {
+ Lisp_Object keymap;
+
+ keymap = get_keymap (access_keymap (maps[i], Qtab_bar, 1, 0, 1), 0, 1);
+ if (CONSP (keymap))
+ map_keymap (keymap, process_tab_bar_item, Qnil, NULL, 1);
+ }
+
+ Vinhibit_quit = oquit;
+ *nitems = ntab_bar_items / TAB_BAR_ITEM_NSLOTS;
+ return tab_bar_items_vector;
+}
+
+
+/* Process the definition of KEY which is DEF. */
+
+static void
+process_tab_bar_item (key, def, data, args)
+ Lisp_Object key, def, data;
+ void *args;
+{
+ int i;
+ extern Lisp_Object Qundefined;
+ struct gcpro gcpro1, gcpro2;
+
+ /* Protect KEY and DEF from GC because parse_tab_bar_item may call
+ eval. */
+ GCPRO2 (key, def);
+
+ if (EQ (def, Qundefined))
+ {
+ /* If a map has an explicit `undefined' as definition,
+ discard any previously made item. */
+ for (i = 0; i < ntab_bar_items; i += TAB_BAR_ITEM_NSLOTS)
+ {
+ Lisp_Object *v = XVECTOR (tab_bar_items_vector)->contents + i;
+
+ if (EQ (key, v[TAB_BAR_ITEM_KEY]))
+ {
+ if (ntab_bar_items > i + TAB_BAR_ITEM_NSLOTS)
+ bcopy (v + TAB_BAR_ITEM_NSLOTS, v,
+ ((ntab_bar_items - i - TAB_BAR_ITEM_NSLOTS)
+ * sizeof (Lisp_Object)));
+ ntab_bar_items -= TAB_BAR_ITEM_NSLOTS;
+ break;
+ }
+ }
+ }
+ else if (parse_tab_bar_item (key, def))
+ /* Append a new tab bar item to tab_bar_items_vector. Accept
+ more than one definition for the same key. */
+ append_tab_bar_item ();
+
+ UNGCPRO;
+}
+
+
+/* Parse a tab bar item specification ITEM for key KEY and return the
+ result in tab_bar_item_properties. Value is zero if ITEM is
+ invalid.
+
+ ITEM is a list `(menu-item CAPTION BINDING PROPS...)'.
+
+ CAPTION is the caption of the item, If it's not a string, it is
+ evaluated to get a string.
+
+ BINDING is the tab bar item's binding. Tab-bar items with keymaps
+ as binding are currently ignored.
+
+ The following properties are recognized:
+
+ - `:enable FORM'.
+
+ FORM is evaluated and specifies whether the tab bar item is
+ enabled or disabled.
+
+ - `:visible FORM'
+
+ FORM is evaluated and specifies whether the tab bar item is visible.
+
+ - `:filter FUNCTION'
+
+ FUNCTION is invoked with one parameter `(quote BINDING)'. Its
+ result is stored as the new binding.
+
+ - `:button (TYPE SELECTED)'
+
+ TYPE must be one of `:radio' or `:toggle'. SELECTED is evaluated
+ and specifies whether the button is selected (pressed) or not.
+
+ - `:image IMAGES'
+
+ IMAGES is either a single image specification or a vector of four
+ image specifications. See enum tab_bar_item_images.
+
+ - `:help HELP-STRING'.
+
+ Gives a help string to display for the tab bar item. */
+
+static int
+parse_tab_bar_item (key, item)
+ Lisp_Object key, item;
+{
+ /* Access slot with index IDX of vector tab_bar_item_properties. */
+#define PROP(IDX) XVECTOR (tab_bar_item_properties)->contents[IDX]
+
+ Lisp_Object filter = Qnil;
+ Lisp_Object caption;
+ int i;
+
+ /* Defininition looks like `(menu-item CAPTION BINDING PROPS...)'.
+ Rule out items that aren't lists, don't start with
+ `menu-item' or whose rest following `tab-bar-item' is not a
+ list. */
+ if (!CONSP (item)
+ || !EQ (XCAR (item), Qmenu_item)
+ || (item = XCDR (item),
+ !CONSP (item)))
+ return 0;
+
+ /* Create tab_bar_item_properties vector if necessary. Reset it to
+ defaults. */
+ if (VECTORP (tab_bar_item_properties))
+ {
+ for (i = 0; i < TAB_BAR_ITEM_NSLOTS; ++i)
+ PROP (i) = Qnil;
+ }
+ else
+ tab_bar_item_properties
+ = Fmake_vector (make_number (TAB_BAR_ITEM_NSLOTS), Qnil);
+
+ /* Set defaults. */
+ PROP (TAB_BAR_ITEM_KEY) = key;
+ PROP (TAB_BAR_ITEM_ENABLED_P) = Qt;
+
+ /* Get the caption of the item. If the caption is not a string,
+ evaluate it to get a string. If we don't get a string, skip this
+ item. */
+ caption = XCAR (item);
+ if (!STRINGP (caption))
+ {
+ caption = menu_item_eval_property (caption);
+ if (!STRINGP (caption))
+ return 0;
+ }
+ PROP (TAB_BAR_ITEM_CAPTION) = caption;
+
+ /* Give up if rest following the caption is not a list. */
+ item = XCDR (item);
+ if (!CONSP (item))
+ return 0;
+
+ /* Store the binding. */
+ PROP (TAB_BAR_ITEM_BINDING) = XCAR (item);
+ item = XCDR (item);
+
+ /* Ignore cached key binding, if any. */
+ if (CONSP (item) && CONSP (XCAR (item)))
+ item = XCDR (item);
+
+ /* Process the rest of the properties. */
+ for (; CONSP (item) && CONSP (XCDR (item)); item = XCDR (XCDR (item)))
+ {
+ Lisp_Object key, value;
+
+ key = XCAR (item);
+ value = XCAR (XCDR (item));
+
+ if (EQ (key, QCenable))
+ {
+ /* `:enable FORM'. */
+ if (!NILP (Venable_disabled_menus_and_buttons))
+ PROP (TAB_BAR_ITEM_ENABLED_P) = Qt;
+ else
+ PROP (TAB_BAR_ITEM_ENABLED_P) = value;
+ }
+ else if (EQ (key, QCvisible))
+ {
+ /* `:visible FORM'. If got a visible property and that
+ evaluates to nil then ignore this item. */
+ if (NILP (menu_item_eval_property (value)))
+ return 0;
+ }
+ else if (EQ (key, QChelp))
+ /* `:help HELP-STRING'. */
+ PROP (TAB_BAR_ITEM_HELP) = value;
+ else if (EQ (key, QCfilter))
+ /* ':filter FORM'. */
+ filter = value;
+ else if (EQ (key, QCbutton) && CONSP (value))
+ {
+ /* `:button (TYPE . SELECTED)'. */
+ Lisp_Object type, selected;
+
+ type = XCAR (value);
+ selected = XCDR (value);
+ if (EQ (type, QCtoggle) || EQ (type, QCradio))
+ {
+ PROP (TAB_BAR_ITEM_SELECTED_P) = selected;
+ PROP (TAB_BAR_ITEM_TYPE) = type;
+ }
+ }
+ else if (EQ (key, QCimage)
+ && (CONSP (value)
+ || (VECTORP (value) && XVECTOR (value)->size == 4)))
+ /* Value is either a single image specification or a vector
+ of 4 such specifications for the different button states. */
+ PROP (TAB_BAR_ITEM_IMAGES) = value;
+ else if (EQ (key, Qrtl))
+ /* ':rtl STRING' */
+ PROP (TAB_BAR_ITEM_RTL_IMAGE) = value;
+ }
+
+ /* If got a filter apply it on binding. */
+ if (!NILP (filter))
+ PROP (TAB_BAR_ITEM_BINDING)
+ = menu_item_eval_property (list2 (filter,
+ list2 (Qquote,
+ PROP (TAB_BAR_ITEM_BINDING))));
+
+ /* See if the binding is a keymap. Give up if it is. */
+ if (CONSP (get_keymap (PROP (TAB_BAR_ITEM_BINDING), 0, 1)))
+ return 0;
+
+ /* Enable or disable selection of item. */
+ if (!EQ (PROP (TAB_BAR_ITEM_ENABLED_P), Qt))
+ PROP (TAB_BAR_ITEM_ENABLED_P)
+ = menu_item_eval_property (PROP (TAB_BAR_ITEM_ENABLED_P));
+
+ /* Handle radio buttons or toggle boxes. */
+ if (!NILP (PROP (TAB_BAR_ITEM_SELECTED_P)))
+ PROP (TAB_BAR_ITEM_SELECTED_P)
+ = menu_item_eval_property (PROP (TAB_BAR_ITEM_SELECTED_P));
+
+ return 1;
+
+#undef PROP
+}
+
+
+/* Initialize tab_bar_items_vector. REUSE, if non-nil, is a vector
+ that can be reused. */
+
+static void
+init_tab_bar_items (reuse)
+ Lisp_Object reuse;
+{
+ if (VECTORP (reuse))
+ tab_bar_items_vector = reuse;
+ else
+ tab_bar_items_vector = Fmake_vector (make_number (64), Qnil);
+ ntab_bar_items = 0;
+}
+
+
+/* Append parsed tab bar item properties from
+ tab_bar_item_properties */
+
+static void
+append_tab_bar_item ()
+{
+ Lisp_Object *to, *from;
+
+ /* Enlarge tab_bar_items_vector if necessary. */
+ if (ntab_bar_items + TAB_BAR_ITEM_NSLOTS
+ >= XVECTOR (tab_bar_items_vector)->size)
+ tab_bar_items_vector
+ = larger_vector (tab_bar_items_vector,
+ 2 * XVECTOR (tab_bar_items_vector)->size, Qnil);
+
+ /* Append entries from tab_bar_item_properties to the end of
+ tab_bar_items_vector. */
+ to = XVECTOR (tab_bar_items_vector)->contents + ntab_bar_items;
+ from = XVECTOR (tab_bar_item_properties)->contents;
+ bcopy (from, to, TAB_BAR_ITEM_NSLOTS * sizeof *to);
+ ntab_bar_items += TAB_BAR_ITEM_NSLOTS;
+}
+
+
+
+
+
/* Read a character using menus based on maps in the array MAPS.
NMAPS is the length of MAPS. Return nil if there are no menus in the maps.
Return t if we displayed a menu but the user rejected it.
@@ -8483,7 +8878,8 @@ read_char_x_menu_prompt (nmaps, maps, prev_event, used_mouse_menu)
use a real menu for mouse selection. */
if (EVENT_HAS_PARAMETERS (prev_event)
&& !EQ (XCAR (prev_event), Qmenu_bar)
- && !EQ (XCAR (prev_event), Qtool_bar))
+ && !EQ (XCAR (prev_event), Qtool_bar)
+ && !EQ (XCAR (prev_event), Qtab_bar))
{
/* Display the menu and get the selection. */
Lisp_Object *realmaps
@@ -9691,7 +10087,7 @@ read_key_sequence (keybuf, bufsize, prompt, dont_downcase_last,
posn = POSN_POSN (EVENT_START (key));
/* Handle menu-bar events:
insert the dummy prefix event `menu-bar'. */
- if (EQ (posn, Qmenu_bar) || EQ (posn, Qtool_bar))
+ if (EQ (posn, Qmenu_bar) || EQ (posn, Qtool_bar) || EQ (posn, Qtab_bar))
{
if (t + 1 >= bufsize)
error ("Key sequence too long");
@@ -11622,6 +12018,12 @@ syms_of_keyboard ()
staticpro (&tool_bar_items_vector);
tool_bar_items_vector = Qnil;
+ /* Tab-bars. */
+ staticpro (&tab_bar_item_properties);
+ tab_bar_item_properties = Qnil;
+ staticpro (&tab_bar_items_vector);
+ tab_bar_items_vector = Qnil;
+
staticpro (&real_this_command);
real_this_command = Qnil;
@@ -12355,10 +12757,10 @@ and the Lisp function within which the error was signaled. */);
DEFVAR_LISP ("enable-disabled-menus-and-buttons",
&Venable_disabled_menus_and_buttons,
- doc: /* If non-nil, don't ignore events produced by disabled menu items and tool-bar.
+ doc: /* If non-nil, don't ignore events produced by disabled menu items, tool-bar and tab-bar.
Help functions bind this to allow help on disabled menu items
-and tool-bar buttons. */);
+and tool-bar and tab-bar buttons. */);
Venable_disabled_menus_and_buttons = Qnil;
/* Create the initial keyboard. */
diff --git a/src/keymap.c b/src/keymap.c
index 88e0687272f..71f9e10b48c 100644
--- a/src/keymap.c
+++ b/src/keymap.c
@@ -4041,6 +4041,7 @@ preferred. */);
staticpro (&Vmouse_events);
Vmouse_events = pure_cons (intern_c_string ("menu-bar"),
pure_cons (intern_c_string ("tool-bar"),
+ pure_cons (intern_c_string ("tab-bar"),
pure_cons (intern_c_string ("header-line"),
pure_cons (intern_c_string ("mode-line"),
pure_cons (intern_c_string ("mouse-1"),
@@ -4048,7 +4049,7 @@ preferred. */);
pure_cons (intern_c_string ("mouse-3"),
pure_cons (intern_c_string ("mouse-4"),
pure_cons (intern_c_string ("mouse-5"),
- Qnil)))))))));
+ Qnil))))))))));
Qsingle_key_description = intern_c_string ("single-key-description");
diff --git a/src/lisp.h b/src/lisp.h
index 7f5d5df66c6..a91174a7f0d 100644
--- a/src/lisp.h
+++ b/src/lisp.h
@@ -3115,6 +3115,7 @@ EXFUN (Fcommand_execute, 4);
EXFUN (Finput_pending_p, 0);
extern Lisp_Object menu_bar_items P_ ((Lisp_Object));
extern Lisp_Object tool_bar_items P_ ((Lisp_Object, int *));
+extern Lisp_Object tab_bar_items P_ ((Lisp_Object, int *));
extern Lisp_Object Qvertical_scroll_bar;
extern void discard_mouse_events P_ ((void));
EXFUN (Fevent_convert_list, 1);
diff --git a/src/menu.c b/src/menu.c
index ca00c06a98b..209c3f2e211 100644
--- a/src/menu.c
+++ b/src/menu.c
@@ -1110,7 +1110,8 @@ no quit occurs and `x-popup-menu' returns nil. */)
/* Decode the first argument: find the window and the coordinates. */
if (EQ (position, Qt)
|| (CONSP (position) && (EQ (XCAR (position), Qmenu_bar)
- || EQ (XCAR (position), Qtool_bar))))
+ || EQ (XCAR (position), Qtool_bar)
+ || EQ (XCAR (position), Qtab_bar))))
{
get_current_pos_p = 1;
}
diff --git a/src/msdos.c b/src/msdos.c
index 6cde7cd6ebc..68dd7d62e4d 100644
--- a/src/msdos.c
+++ b/src/msdos.c
@@ -1270,7 +1270,7 @@ IT_note_mouse_highlight (struct frame *f, int x, int y)
}
/* Which window is that in? */
- window = window_from_coordinates (f, x, y, &part, &x, &y, 0);
+ window = window_from_coordinates (f, x, y, &part, &x, &y, 0, 0);
/* If we were displaying active text in another window, clear that. */
if (! EQ (window, dpyinfo->mouse_face_window))
@@ -3202,7 +3202,7 @@ dos_rawgetc ()
mouse_window = window_from_coordinates (SELECTED_FRAME(),
mouse_last_x,
mouse_last_y,
- 0, 0, 0, 0);
+ 0, 0, 0, 0, 0);
/* A window will be selected only when it is not
selected now, and the last mouse movement event was
not in it. A minibuffer window will be selected iff
diff --git a/src/term.c b/src/term.c
index 718a20d4164..e512cc96ba3 100644
--- a/src/term.c
+++ b/src/term.c
@@ -2734,7 +2734,7 @@ term_mouse_highlight (struct frame *f, int x, int y)
return;
/* Which window is that in? */
- window = window_from_coordinates (f, x, y, &part, &x, &y, 0);
+ window = window_from_coordinates (f, x, y, &part, &x, &y, 0, 0);
/* Not on a window -> return. */
if (!WINDOWP (window))
diff --git a/src/termhooks.h b/src/termhooks.h
index 2b4011627c8..eb4d286e016 100644
--- a/src/termhooks.h
+++ b/src/termhooks.h
@@ -165,6 +165,11 @@ enum event_kind
and `arg' are equal, this is a prefix event. */
TOOL_BAR_EVENT,
+ /* An event from a tab-bar. Member `arg' of the input event
+ contains the tab-bar item selected. If `frame_or_window'
+ and `arg' are equal, this is a prefix event. */
+ TAB_BAR_EVENT,
+
/* Queued from XTread_socket on FocusIn events. Translated into
`switch-frame' events in kbd_buffer_get_event, if necessary. */
FOCUS_IN_EVENT,
diff --git a/src/w32fns.c b/src/w32fns.c
index 795e7208569..789b1fbb388 100644
--- a/src/w32fns.c
+++ b/src/w32fns.c
@@ -4485,7 +4485,7 @@ This function is an internal primitive--use `make-frame' instead. */)
f->output_data.w32->current_cursor = f->output_data.w32->nontext_cursor;
- window_prompting = x_figure_window_size (f, parameters, 1);
+ window_prompting = x_figure_window_size (f, parameters, 1, 1);
tem = x_get_arg (dpyinfo, parameters, Qunsplittable, 0, 0, RES_TYPE_BOOLEAN);
f->no_split = minibuffer_only || EQ (tem, Qt);
@@ -5553,7 +5553,7 @@ x_create_tip_frame (dpyinfo, parms, text)
f->output_data.w32->dwStyle = WS_BORDER | WS_POPUP | WS_DISABLED;
f->output_data.w32->parent_desc = FRAME_W32_DISPLAY_INFO (f)->root_window;
- window_prompting = x_figure_window_size (f, parms, 0);
+ window_prompting = x_figure_window_size (f, parms, 0, 0);
/* No fringes on tip frame. */
f->fringe_cols = 0;
diff --git a/src/w32term.c b/src/w32term.c
index 7222e26efd2..ce939dc9b54 100644
--- a/src/w32term.c
+++ b/src/w32term.c
@@ -4337,7 +4337,7 @@ w32_read_socket (sd, expected, hold_quit)
int x = LOWORD (msg.msg.lParam);
int y = HIWORD (msg.msg.lParam);
- window = window_from_coordinates (f, x, y, 0, 0, 0, 0);
+ window = window_from_coordinates (f, x, y, 0, 0, 0, 0, 0);
/* Window will be selected only when it is not
selected now and last mouse movement event was
@@ -4416,7 +4416,7 @@ w32_read_socket (sd, expected, hold_quit)
int x = XFASTINT (inev.x);
int y = XFASTINT (inev.y);
- window = window_from_coordinates (f, x, y, 0, 0, 0, 1);
+ window = window_from_coordinates (f, x, y, 0, 0, 0, 1, 1);
if (EQ (window, f->tool_bar_window))
{
diff --git a/src/window.c b/src/window.c
index f17a645f82c..6193f480461 100644
--- a/src/window.c
+++ b/src/window.c
@@ -1041,6 +1041,7 @@ check_window_containing (w, user_data)
If there is no window under X, Y return nil and leave *PART
unmodified. TOOL_BAR_P non-zero means detect tool-bar windows.
+ TAB_BAR_P non-zero means detect tab-bar windows.
This function was previously implemented with a loop cycling over
windows with Fnext_window, and starting with the frame's selected
@@ -1052,12 +1053,13 @@ check_window_containing (w, user_data)
case. */
Lisp_Object
-window_from_coordinates (f, x, y, part, wx, wy, tool_bar_p)
+window_from_coordinates (f, x, y, part, wx, wy, tool_bar_p, tab_bar_p)
struct frame *f;
int x, y;
enum window_part *part;
int *wx, *wy;
int tool_bar_p;
+ int tab_bar_p;
{
Lisp_Object window;
struct check_window_data cw;
@@ -1083,6 +1085,19 @@ window_from_coordinates (f, x, y, part, wx, wy, tool_bar_p)
window = f->tool_bar_window;
}
+ /* If not found above, see if it's in the tab bar window, if a tab
+ bar exists. */
+ if (NILP (window)
+ && tab_bar_p
+ && WINDOWP (f->tab_bar_window)
+ && WINDOW_TOTAL_LINES (XWINDOW (f->tab_bar_window)) > 0
+ && (coordinates_in_window (XWINDOW (f->tab_bar_window), &x, &y)
+ != ON_NOTHING))
+ {
+ *part = ON_TEXT;
+ window = f->tab_bar_window;
+ }
+
if (wx) *wx = x;
if (wy) *wy = y;
@@ -1113,7 +1128,7 @@ column 0. */)
+ FRAME_INTERNAL_BORDER_WIDTH (f)),
(FRAME_PIXEL_Y_FROM_CANON_Y (f, y)
+ FRAME_INTERNAL_BORDER_WIDTH (f)),
- 0, 0, 0, 0);
+ 0, 0, 0, 0, 0);
}
DEFUN ("window-point", Fwindow_point, Swindow_point, 0, 1, 0,
@@ -5636,8 +5651,10 @@ If ARG is omitted or nil, then recenter with point on the middle line of
the selected window; if the variable `recenter-redisplay' is non-nil,
also erase the entire frame and redraw it (when `auto-resize-tool-bars'
is set to `grow-only', this resets the tool-bar's height to the minimum
-height needed); if `recenter-redisplay' has the special value `tty',
-then only tty frame are redrawn.
+height needed; when `auto-resize-tab-bars' is set to `grow-only',
+this resets the tab-bar's height to the minimum height needed);
+if `recenter-redisplay' has the special value `tty', then only
+tty frame are redrawn.
Just C-u as prefix means put point in the center of the window
and redisplay normally--don't erase and redraw the frame. */)
@@ -5668,6 +5685,7 @@ and redisplay normally--don't erase and redraw the frame. */)
composition_table[i]->font = NULL;
WINDOW_XFRAME (w)->minimize_tool_bar_window_p = 1;
+ WINDOW_XFRAME (w)->minimize_tab_bar_window_p = 1;
Fredraw_frame (WINDOW_FRAME (w));
SET_FRAME_GARBAGED (WINDOW_XFRAME (w));
@@ -5929,6 +5947,7 @@ struct save_window_data
int frame_cols, frame_lines, frame_menu_bar_lines;
int frame_tool_bar_lines;
+ int frame_tab_bar_lines;
};
/* This is saved as a Lisp_Vector */
@@ -6063,6 +6082,7 @@ the return value is nil. Otherwise the value is t. */)
int previous_frame_cols = FRAME_COLS (f);
int previous_frame_menu_bar_lines = FRAME_MENU_BAR_LINES (f);
int previous_frame_tool_bar_lines = FRAME_TOOL_BAR_LINES (f);
+ int previous_frame_tab_bar_lines = FRAME_TAB_BAR_LINES (f);
/* The mouse highlighting code could get screwed up
if it runs during this. */
@@ -6082,6 +6102,10 @@ the return value is nil. Otherwise the value is t. */)
!= previous_frame_tool_bar_lines)
x_set_tool_bar_lines (f, make_number (data->frame_tool_bar_lines),
make_number (0));
+ if (data->frame_tab_bar_lines
+ != previous_frame_tab_bar_lines)
+ x_set_tab_bar_lines (f, make_number (data->frame_tab_bar_lines),
+ make_number (0));
#endif
#endif
@@ -6273,6 +6297,9 @@ the return value is nil. Otherwise the value is t. */)
if (previous_frame_tool_bar_lines != FRAME_TOOL_BAR_LINES (f))
x_set_tool_bar_lines (f, make_number (previous_frame_tool_bar_lines),
make_number (0));
+ if (previous_frame_tab_bar_lines != FRAME_TAB_BAR_LINES (f))
+ x_set_tab_bar_lines (f, make_number (previous_frame_tab_bar_lines),
+ make_number (0));
#endif
#endif
@@ -6524,6 +6551,7 @@ redirection (see `redirect-frame-focus'). */)
data->frame_lines = FRAME_LINES (f);
data->frame_menu_bar_lines = FRAME_MENU_BAR_LINES (f);
data->frame_tool_bar_lines = FRAME_TOOL_BAR_LINES (f);
+ data->frame_tab_bar_lines = FRAME_TAB_BAR_LINES (f);
data->selected_frame = selected_frame;
data->current_window = FRAME_SELECTED_WINDOW (f);
XSETBUFFER (data->current_buffer, current_buffer);
diff --git a/src/window.h b/src/window.h
index 17332f0af20..53419215883 100644
--- a/src/window.h
+++ b/src/window.h
@@ -789,7 +789,7 @@ extern Lisp_Object make_window P_ ((void));
extern void delete_window P_ ((Lisp_Object));
extern Lisp_Object window_from_coordinates P_ ((struct frame *, int, int,
enum window_part *,
- int *, int*, int));
+ int *, int*, int, int));
EXFUN (Fwindow_dedicated_p, 1);
extern int window_height P_ ((Lisp_Object));
extern int window_width P_ ((Lisp_Object));
diff --git a/src/xdisp.c b/src/xdisp.c
index baede013485..e5ef4b2f54c 100644
--- a/src/xdisp.c
+++ b/src/xdisp.c
@@ -279,6 +279,34 @@ EMACS_INT tool_bar_button_relief;
Lisp_Object Vauto_resize_tool_bars;
+
+/* Non-zero means draw tab bar buttons raised when the mouse moves
+ over them. */
+
+int auto_raise_tab_bar_buttons_p;
+
+/* Margin below tab bar in pixels. 0 or nil means no margin.
+ If value is `internal-border-width' or `border-width',
+ the corresponding frame parameter is used. */
+
+Lisp_Object Vtab_bar_border;
+
+/* Margin around tab bar buttons in pixels. */
+
+Lisp_Object Vtab_bar_button_margin;
+
+/* Thickness of shadow to draw around tab bar buttons. */
+
+EMACS_INT tab_bar_button_relief;
+
+/* Non-nil means automatically resize tab-bars so that all tab-bar
+ items are visible, and no blank lines remain.
+
+ If value is `grow-only', only make tab-bar bigger. */
+
+Lisp_Object Vauto_resize_tab_bars;
+
+
/* Non-zero means draw block and hollow cursor as wide as the glyph
under it. For example, if a block cursor is over a tab, it will be
drawn as wide as that tab on the display. */
@@ -882,6 +910,7 @@ static void x_consider_frame_title P_ ((Lisp_Object));
static void handle_stop P_ ((struct it *));
static void handle_stop_backwards P_ ((struct it *, EMACS_INT));
static int tool_bar_lines_needed P_ ((struct frame *, int *));
+static int tab_bar_lines_needed P_ ((struct frame *, int *));
static int single_display_spec_intangible_p P_ ((Lisp_Object));
static void ensure_echo_area_buffers P_ ((void));
static Lisp_Object unwind_with_echo_area_buffer P_ ((Lisp_Object));
@@ -996,6 +1025,10 @@ static void update_tool_bar P_ ((struct frame *, int));
static void build_desired_tool_bar_string P_ ((struct frame *f));
static int redisplay_tool_bar P_ ((struct frame *));
static void display_tool_bar_line P_ ((struct it *, int));
+static void update_tab_bar P_ ((struct frame *, int));
+static void build_desired_tab_bar_string P_ ((struct frame *f));
+static int redisplay_tab_bar P_ ((struct frame *));
+static void display_tab_bar_line P_ ((struct it *, int));
static void notice_overwritten_cursor P_ ((struct window *,
enum glyph_row_area,
int, int, int, int));
@@ -2141,7 +2174,7 @@ remember_mouse_glyph (f, gx, gy, rect)
frame pixel coordinates X/Y on frame F. */
if (!f->glyphs_initialized_p
- || (window = window_from_coordinates (f, gx, gy, &part, &x, &y, 0),
+ || (window = window_from_coordinates (f, gx, gy, &part, &x, &y, 0, 0),
NILP (window)))
{
width = FRAME_SMALLEST_CHAR_WIDTH (f);
@@ -2494,7 +2527,8 @@ check_window_end (w)
BASE_FACE_ID is the id of a base face to use. It must be one of
DEFAULT_FACE_ID for normal text, MODE_LINE_FACE_ID,
MODE_LINE_INACTIVE_FACE_ID, or HEADER_LINE_FACE_ID for displaying
- mode lines, or TOOL_BAR_FACE_ID for displaying the tool-bar.
+ mode lines, TOOL_BAR_FACE_ID for displaying the tool-bar or
+ TAB_BAR_FACE_ID for displaying the tab-bar.
If ROW is null and BASE_FACE_ID is equal to MODE_LINE_FACE_ID,
MODE_LINE_INACTIVE_FACE_ID, or HEADER_LINE_FACE_ID, the iterator
@@ -9835,6 +9869,7 @@ prepare_menu_bars ()
menu_bar_hooks_run = update_menu_bar (f, 0, menu_bar_hooks_run);
#ifdef HAVE_WINDOW_SYSTEM
update_tool_bar (f, 0);
+ update_tab_bar (f, 0);
#endif
#ifdef HAVE_NS
if (windows_or_buffers_changed)
@@ -9852,6 +9887,7 @@ prepare_menu_bars ()
update_menu_bar (sf, 1, 0);
#ifdef HAVE_WINDOW_SYSTEM
update_tool_bar (sf, 1);
+ update_tab_bar (sf, 1);
#endif
}
@@ -10968,6 +11004,937 @@ note_tool_bar_highlight (f, x, y)
+/***********************************************************************
+ Tab-bars
+ ***********************************************************************/
+
+#ifdef HAVE_WINDOW_SYSTEM
+
+/* Where the mouse was last time we reported a mouse event. */
+
+FRAME_PTR last_mouse_frame;
+
+/* Tab-bar item index of the item on which a mouse button was pressed
+ or -1. */
+
+int last_tab_bar_item;
+
+
+static Lisp_Object
+update_tab_bar_unwind (frame)
+ Lisp_Object frame;
+{
+ selected_frame = frame;
+ return Qnil;
+}
+
+/* Update the tab-bar item list for frame F. This has to be done
+ before we start to fill in any display lines. Called from
+ prepare_menu_bars. If SAVE_MATCH_DATA is non-zero, we must save
+ and restore it here. */
+
+static void
+update_tab_bar (f, save_match_data)
+ struct frame *f;
+ int save_match_data;
+{
+/* #if defined (USE_GTK) || defined (HAVE_NS) */
+/* int do_update = FRAME_EXTERNAL_TAB_BAR (f); */
+/* #else */
+ int do_update = WINDOWP (f->tab_bar_window)
+ && WINDOW_TOTAL_LINES (XWINDOW (f->tab_bar_window)) > 0;
+/* #endif */
+
+ if (do_update)
+ {
+ Lisp_Object window;
+ struct window *w;
+
+ window = FRAME_SELECTED_WINDOW (f);
+ w = XWINDOW (window);
+
+ /* If the user has switched buffers or windows, we need to
+ recompute to reflect the new bindings. But we'll
+ recompute when update_mode_lines is set too; that means
+ that people can use force-mode-line-update to request
+ that the menu bar be recomputed. The adverse effect on
+ the rest of the redisplay algorithm is about the same as
+ windows_or_buffers_changed anyway. */
+ if (windows_or_buffers_changed
+ || !NILP (w->update_mode_line)
+ || update_mode_lines
+ || ((BUF_SAVE_MODIFF (XBUFFER (w->buffer))
+ < BUF_MODIFF (XBUFFER (w->buffer)))
+ != !NILP (w->last_had_star))
+ || ((!NILP (Vtransient_mark_mode)
+ && !NILP (XBUFFER (w->buffer)->mark_active))
+ != !NILP (w->region_showing)))
+ {
+ struct buffer *prev = current_buffer;
+ int count = SPECPDL_INDEX ();
+ Lisp_Object frame, new_tab_bar;
+ int new_n_tab_bar;
+ struct gcpro gcpro1;
+
+ /* Set current_buffer to the buffer of the selected
+ window of the frame, so that we get the right local
+ keymaps. */
+ set_buffer_internal_1 (XBUFFER (w->buffer));
+
+ /* Save match data, if we must. */
+ if (save_match_data)
+ record_unwind_save_match_data ();
+
+ /* Make sure that we don't accidentally use bogus keymaps. */
+ if (NILP (Voverriding_local_map_menu_flag))
+ {
+ specbind (Qoverriding_terminal_local_map, Qnil);
+ specbind (Qoverriding_local_map, Qnil);
+ }
+
+ GCPRO1 (new_tab_bar);
+
+ /* We must temporarily set the selected frame to this frame
+ before calling tab_bar_items, because the calculation of
+ the tab-bar keymap uses the selected frame (see
+ `tab-bar-make-keymap' in tab-bar.el). */
+ record_unwind_protect (update_tab_bar_unwind, selected_frame);
+ XSETFRAME (frame, f);
+ selected_frame = frame;
+
+ /* Build desired tab-bar items from keymaps. */
+ new_tab_bar = tab_bar_items (Fcopy_sequence (f->tab_bar_items),
+ &new_n_tab_bar);
+
+ /* Redisplay the tab-bar if we changed it. */
+ if (new_n_tab_bar != f->n_tab_bar_items
+ || NILP (Fequal (new_tab_bar, f->tab_bar_items)))
+ {
+ /* Redisplay that happens asynchronously due to an expose event
+ may access f->tab_bar_items. Make sure we update both
+ variables within BLOCK_INPUT so no such event interrupts. */
+ BLOCK_INPUT;
+ f->tab_bar_items = new_tab_bar;
+ f->n_tab_bar_items = new_n_tab_bar;
+ w->update_mode_line = Qt;
+ UNBLOCK_INPUT;
+ }
+
+ UNGCPRO;
+
+ unbind_to (count, Qnil);
+ set_buffer_internal_1 (prev);
+ }
+ }
+}
+
+
+/* Set F->desired_tab_bar_string to a Lisp string representing frame
+ F's desired tab-bar contents. F->tab_bar_items must have
+ been set up previously by calling prepare_menu_bars. */
+
+static void
+build_desired_tab_bar_string (f)
+ struct frame *f;
+{
+ int i /*, size, size_needed */;
+ struct gcpro gcpro1, gcpro2, gcpro3;
+ Lisp_Object image, plist, props, caption;
+
+ image = plist = props = Qnil;
+ GCPRO3 (image, plist, props);
+
+ /* Prepare F->desired_tab_bar_string. If we can reuse it, do so.
+ Otherwise, make a new string. */
+
+ /* The size of the string we might be able to reuse. */
+ /* size = (STRINGP (f->desired_tab_bar_string) */
+ /* ? SCHARS (f->desired_tab_bar_string) */
+ /* : 0); */
+
+ /* We need one space in the string for each image. */
+ /* size_needed = f->n_tab_bar_items; */
+
+ /* Reuse f->desired_tab_bar_string, if possible. */
+ /* if (size < size_needed || NILP (f->desired_tab_bar_string)) */
+ /* f->desired_tab_bar_string = Fmake_string (make_number (size_needed + 10), */
+ /* make_number ('X')); */
+ /* else */
+ /* { */
+ /* props = list4 (Qdisplay, Qnil, Qmenu_item, Qnil); */
+ /* Fremove_text_properties (make_number (0), make_number (size), */
+ /* props, f->desired_tab_bar_string); */
+ /* } */
+ f->desired_tab_bar_string = build_string (" ");
+
+ /* Put a `display' property on the string for the images to display,
+ put a `menu_item' property on tab-bar items with a value that
+ is the index of the item in F's tab-bar item vector. */
+ for (i = 0; i < f->n_tab_bar_items; ++i)
+ {
+#define PROP(IDX) AREF (f->tab_bar_items, i * TAB_BAR_ITEM_NSLOTS + (IDX))
+
+ int caption_p = !NILP (PROP (TAB_BAR_ITEM_CAPTION))
+ && SCHARS (PROP (TAB_BAR_ITEM_CAPTION)) > 0;
+ int enabled_p = !NILP (PROP (TAB_BAR_ITEM_ENABLED_P));
+ int selected_p = !NILP (PROP (TAB_BAR_ITEM_SELECTED_P));
+ int hmargin, vmargin, relief, idx, end;
+ extern Lisp_Object QCrelief, QCmargin, QCconversion, QCascent, Qcenter;
+
+ /* If image is a vector, choose the image according to the
+ button state. */
+ image = PROP (TAB_BAR_ITEM_IMAGES);
+ if (VECTORP (image))
+ {
+ if (enabled_p)
+ idx = (selected_p
+ ? TAB_BAR_IMAGE_ENABLED_SELECTED
+ : TAB_BAR_IMAGE_ENABLED_DESELECTED);
+ else
+ idx = (selected_p
+ ? TAB_BAR_IMAGE_DISABLED_SELECTED
+ : TAB_BAR_IMAGE_DISABLED_DESELECTED);
+
+ xassert (ASIZE (image) >= idx);
+ image = AREF (image, idx);
+ }
+ else
+ idx = -1;
+
+ /* Ignore invalid image specifications. */
+ /* if (!valid_image_p (image)) */
+ /* continue; */
+
+ /* Display the tab-bar button pressed, or depressed. */
+ plist = Fcopy_sequence (XCDR (image));
+
+ /* Compute margin and relief to draw. */
+ relief = (tab_bar_button_relief >= 0
+ ? tab_bar_button_relief
+ : DEFAULT_TAB_BAR_BUTTON_RELIEF);
+ hmargin = vmargin = relief;
+
+ if (INTEGERP (Vtab_bar_button_margin)
+ && XINT (Vtab_bar_button_margin) > 0)
+ {
+ hmargin += XFASTINT (Vtab_bar_button_margin);
+ vmargin += XFASTINT (Vtab_bar_button_margin);
+ }
+ else if (CONSP (Vtab_bar_button_margin))
+ {
+ if (INTEGERP (XCAR (Vtab_bar_button_margin))
+ && XINT (XCAR (Vtab_bar_button_margin)) > 0)
+ hmargin += XFASTINT (XCAR (Vtab_bar_button_margin));
+
+ if (INTEGERP (XCDR (Vtab_bar_button_margin))
+ && XINT (XCDR (Vtab_bar_button_margin)) > 0)
+ vmargin += XFASTINT (XCDR (Vtab_bar_button_margin));
+ }
+
+ if (auto_raise_tab_bar_buttons_p)
+ {
+ /* Add a `:relief' property to the image spec if the item is
+ selected. */
+ if (selected_p)
+ {
+ plist = Fplist_put (plist, QCrelief, make_number (-relief));
+ hmargin -= relief;
+ vmargin -= relief;
+ }
+ }
+ else
+ {
+ /* If image is selected, display it pressed, i.e. with a
+ negative relief. If it's not selected, display it with a
+ raised relief. */
+ plist = Fplist_put (plist, QCrelief,
+ (selected_p
+ ? make_number (-relief)
+ : make_number (relief)));
+ hmargin -= relief;
+ vmargin -= relief;
+ }
+
+ /* Put a margin around the image. */
+ if (hmargin || vmargin)
+ {
+ if (hmargin == vmargin)
+ plist = Fplist_put (plist, QCmargin, make_number (hmargin));
+ else
+ plist = Fplist_put (plist, QCmargin,
+ Fcons (make_number (hmargin),
+ make_number (vmargin)));
+ }
+
+ /* If button is not enabled, and we don't have special images
+ for the disabled state, make the image appear disabled by
+ applying an appropriate algorithm to it. */
+ if (!enabled_p && idx < 0)
+ plist = Fplist_put (plist, QCconversion, Qdisabled);
+
+ plist = Fplist_put (plist, QCascent, Qcenter);
+
+ /* Put a `display' text property on the string for the image to
+ display. Put a `menu-item' property on the string that gives
+ the start of this item's properties in the tab-bar items
+ vector. */
+ props = list2 (Qmenu_item, make_number (i * TAB_BAR_ITEM_NSLOTS));
+
+ if (caption_p)
+ {
+ caption = concat3 (build_string (" "),
+ Fcopy_sequence (PROP (TAB_BAR_ITEM_CAPTION)),
+ build_string (" "));
+ }
+ else
+ {
+ caption = build_string (" ");
+ }
+
+ Fadd_text_properties (make_number (0), make_number (SCHARS (caption)),
+ props, caption);
+
+ image = Fcons (Qimage, plist);
+ if (valid_image_p (image))
+ {
+ props = list2 (Qdisplay, image);
+ Fadd_text_properties (make_number (SCHARS (caption) - (caption_p ? 1 : 2)),
+ make_number (SCHARS (caption) - (caption_p ? 0 : 1)),
+ props, caption);
+ }
+
+ f->desired_tab_bar_string =
+ concat2 (f->desired_tab_bar_string, caption);
+
+#undef PROP
+ }
+
+ UNGCPRO;
+}
+
+
+/* Display one line of the tab-bar of frame IT->f.
+
+ HEIGHT specifies the desired height of the tab-bar line.
+ If the actual height of the glyph row is less than HEIGHT, the
+ row's height is increased to HEIGHT, and the icons are centered
+ vertically in the new height.
+
+ If HEIGHT is -1, we are counting needed tab-bar lines, so don't
+ count a final empty row in case the tab-bar width exactly matches
+ the window width.
+*/
+
+static void
+display_tab_bar_line (it, height)
+ struct it *it;
+ int height;
+{
+ struct glyph_row *row = it->glyph_row;
+ int max_x = it->last_visible_x;
+ struct glyph *last;
+
+ prepare_desired_row (row);
+ row->y = it->current_y;
+
+ /* Note that this isn't made use of if the face hasn't a box,
+ so there's no need to check the face here. */
+ it->start_of_box_run_p = 1;
+
+ while (it->current_x < max_x)
+ {
+ int x, n_glyphs_before, i, nglyphs;
+ struct it it_before;
+
+ /* Get the next display element. */
+ if (!get_next_display_element (it))
+ {
+ /* Don't count empty row if we are counting needed tab-bar lines. */
+ if (height < 0 && !it->hpos)
+ return;
+ break;
+ }
+
+ /* Produce glyphs. */
+ n_glyphs_before = row->used[TEXT_AREA];
+ it_before = *it;
+
+ PRODUCE_GLYPHS (it);
+
+ nglyphs = row->used[TEXT_AREA] - n_glyphs_before;
+ i = 0;
+ x = it_before.current_x;
+ while (i < nglyphs)
+ {
+ struct glyph *glyph = row->glyphs[TEXT_AREA] + n_glyphs_before + i;
+
+ if (x + glyph->pixel_width > max_x)
+ {
+ /* Glyph doesn't fit on line. Backtrack. */
+ row->used[TEXT_AREA] = n_glyphs_before;
+ *it = it_before;
+ /* If this is the only glyph on this line, it will never fit on the
+ tabbar, so skip it. But ensure there is at least one glyph,
+ so we don't accidentally disable the tab-bar. */
+ if (n_glyphs_before == 0
+ && (it->vpos > 0 || IT_STRING_CHARPOS (*it) < it->end_charpos-1))
+ break;
+ goto out;
+ }
+
+ ++it->hpos;
+ x += glyph->pixel_width;
+ ++i;
+ }
+
+ /* Stop at line ends. */
+ if (ITERATOR_AT_END_OF_LINE_P (it))
+ break;
+
+ set_iterator_to_next (it, 1);
+ }
+
+ out:;
+
+ row->displays_text_p = row->used[TEXT_AREA] != 0;
+
+ /* Use default face for the border below the tab bar.
+
+ FIXME: When auto-resize-tab-bars is grow-only, there is
+ no additional border below the possibly empty tab-bar lines.
+ So to make the extra empty lines look "normal", we have to
+ use the tab-bar face for the border too. */
+ if (!row->displays_text_p && !EQ (Vauto_resize_tab_bars, Qgrow_only))
+ it->face_id = DEFAULT_FACE_ID;
+
+ extend_face_to_end_of_line (it);
+ last = row->glyphs[TEXT_AREA] + row->used[TEXT_AREA] - 1;
+ last->right_box_line_p = 1;
+ if (last == row->glyphs[TEXT_AREA])
+ last->left_box_line_p = 1;
+
+ /* Make line the desired height and center it vertically. */
+ if ((height -= it->max_ascent + it->max_descent) > 0)
+ {
+ /* Don't add more than one line height. */
+ height %= FRAME_LINE_HEIGHT (it->f);
+ it->max_ascent += height / 2;
+ it->max_descent += (height + 1) / 2;
+ }
+
+ compute_line_metrics (it);
+
+ /* If line is empty, make it occupy the rest of the tab-bar. */
+ if (!row->displays_text_p)
+ {
+ row->height = row->phys_height = it->last_visible_y - row->y;
+ row->visible_height = row->height;
+ row->ascent = row->phys_ascent = 0;
+ row->extra_line_spacing = 0;
+ }
+
+ row->full_width_p = 1;
+ row->continued_p = 0;
+ row->truncated_on_left_p = 0;
+ row->truncated_on_right_p = 0;
+
+ it->current_x = it->hpos = 0;
+ it->current_y += row->height;
+ ++it->vpos;
+ ++it->glyph_row;
+}
+
+
+/* Max tab-bar height. */
+
+#define MAX_FRAME_TAB_BAR_HEIGHT(f) \
+ ((FRAME_LINE_HEIGHT (f) * FRAME_LINES (f)))
+
+/* Value is the number of screen lines needed to make all tab-bar
+ items of frame F visible. The number of actual rows needed is
+ returned in *N_ROWS if non-NULL. */
+
+static int
+tab_bar_lines_needed (f, n_rows)
+ struct frame *f;
+ int *n_rows;
+{
+ struct window *w = XWINDOW (f->tab_bar_window);
+ struct it it;
+ /* tab_bar_lines_needed is called from redisplay_tab_bar after building
+ the desired matrix, so use (unused) mode-line row as temporary row to
+ avoid destroying the first tab-bar row. */
+ struct glyph_row *temp_row = MATRIX_MODE_LINE_ROW (w->desired_matrix);
+
+ /* Initialize an iterator for iteration over
+ F->desired_tab_bar_string in the tab-bar window of frame F. */
+ init_iterator (&it, w, -1, -1, temp_row, TAB_BAR_FACE_ID);
+ it.first_visible_x = 0;
+ it.last_visible_x = FRAME_TOTAL_COLS (f) * FRAME_COLUMN_WIDTH (f);
+ reseat_to_string (&it, NULL, f->desired_tab_bar_string, 0, 0, 0, -1);
+
+ while (!ITERATOR_AT_END_P (&it))
+ {
+ clear_glyph_row (temp_row);
+ it.glyph_row = temp_row;
+ display_tab_bar_line (&it, -1);
+ }
+ clear_glyph_row (temp_row);
+
+ /* f->n_tab_bar_rows == 0 means "unknown"; -1 means no tab-bar. */
+ if (n_rows)
+ *n_rows = it.vpos > 0 ? it.vpos : -1;
+
+ return (it.current_y + FRAME_LINE_HEIGHT (f) - 1) / FRAME_LINE_HEIGHT (f);
+}
+
+
+DEFUN ("tab-bar-lines-needed", Ftab_bar_lines_needed, Stab_bar_lines_needed,
+ 0, 1, 0,
+ doc: /* Return the number of lines occupied by the tab bar of FRAME. */)
+ (frame)
+ Lisp_Object frame;
+{
+ struct frame *f;
+ struct window *w;
+ int nlines = 0;
+
+ if (NILP (frame))
+ frame = selected_frame;
+ else
+ CHECK_FRAME (frame);
+ f = XFRAME (frame);
+
+ if (WINDOWP (f->tab_bar_window)
+ || (w = XWINDOW (f->tab_bar_window),
+ WINDOW_TOTAL_LINES (w) > 0))
+ {
+ update_tab_bar (f, 1);
+ if (f->n_tab_bar_items)
+ {
+ build_desired_tab_bar_string (f);
+ nlines = tab_bar_lines_needed (f, NULL);
+ }
+ }
+
+ return make_number (nlines);
+}
+
+
+/* Display the tab-bar of frame F. Value is non-zero if tab-bar's
+ height should be changed. */
+
+static int
+redisplay_tab_bar (f)
+ struct frame *f;
+{
+ struct window *w;
+ struct it it;
+ struct glyph_row *row;
+
+/* #if defined (USE_GTK) || defined (HAVE_NS) */
+/* if (FRAME_EXTERNAL_TAB_BAR (f)) */
+/* update_frame_tab_bar (f); */
+/* return 0; */
+/* #endif */
+
+ /* If frame hasn't a tab-bar window or if it is zero-height, don't
+ do anything. This means you must start with tab-bar-lines
+ non-zero to get the auto-sizing effect. Or in other words, you
+ can turn off tab-bars by specifying tab-bar-lines zero. */
+ if (!WINDOWP (f->tab_bar_window)
+ || (w = XWINDOW (f->tab_bar_window),
+ WINDOW_TOTAL_LINES (w) == 0))
+ return 0;
+
+ /* Set up an iterator for the tab-bar window. */
+ init_iterator (&it, w, -1, -1, w->desired_matrix->rows, TAB_BAR_FACE_ID);
+ it.first_visible_x = 0;
+ it.last_visible_x = FRAME_TOTAL_COLS (f) * FRAME_COLUMN_WIDTH (f);
+ row = it.glyph_row;
+
+ /* Build a string that represents the contents of the tab-bar. */
+ build_desired_tab_bar_string (f);
+ reseat_to_string (&it, NULL, f->desired_tab_bar_string, 0, 0, 0, -1);
+
+ if (f->n_tab_bar_rows == 0)
+ {
+ int nlines;
+
+ if ((nlines = tab_bar_lines_needed (f, &f->n_tab_bar_rows),
+ nlines != WINDOW_TOTAL_LINES (w)))
+ {
+ extern Lisp_Object Qtab_bar_lines;
+ Lisp_Object frame;
+ int old_height = WINDOW_TOTAL_LINES (w);
+
+ XSETFRAME (frame, f);
+ Fmodify_frame_parameters (frame,
+ Fcons (Fcons (Qtab_bar_lines,
+ make_number (nlines)),
+ Qnil));
+ if (WINDOW_TOTAL_LINES (w) != old_height)
+ {
+ clear_glyph_matrix (w->desired_matrix);
+ fonts_changed_p = 1;
+ return 1;
+ }
+ }
+ }
+
+ /* Display as many lines as needed to display all tab-bar items. */
+
+ if (f->n_tab_bar_rows > 0)
+ {
+ int border, rows, height, extra;
+
+ if (INTEGERP (Vtab_bar_border))
+ border = XINT (Vtab_bar_border);
+ else if (EQ (Vtab_bar_border, Qinternal_border_width))
+ border = FRAME_INTERNAL_BORDER_WIDTH (f);
+ else if (EQ (Vtab_bar_border, Qborder_width))
+ border = f->border_width;
+ else
+ border = 0;
+ if (border < 0)
+ border = 0;
+
+ rows = f->n_tab_bar_rows;
+ height = max (1, (it.last_visible_y - border) / rows);
+ extra = it.last_visible_y - border - height * rows;
+
+ while (it.current_y < it.last_visible_y)
+ {
+ int h = 0;
+ if (extra > 0 && rows-- > 0)
+ {
+ h = (extra + rows - 1) / rows;
+ extra -= h;
+ }
+ display_tab_bar_line (&it, height + h);
+ }
+ }
+ else
+ {
+ while (it.current_y < it.last_visible_y)
+ display_tab_bar_line (&it, 0);
+ }
+
+ /* It doesn't make much sense to try scrolling in the tab-bar
+ window, so don't do it. */
+ w->desired_matrix->no_scrolling_p = 1;
+ w->must_be_updated_p = 1;
+
+ if (!NILP (Vauto_resize_tab_bars))
+ {
+ int max_tab_bar_height = MAX_FRAME_TAB_BAR_HEIGHT (f);
+ int change_height_p = 0;
+
+ /* If we couldn't display everything, change the tab-bar's
+ height if there is room for more. */
+ if (IT_STRING_CHARPOS (it) < it.end_charpos
+ && it.current_y < max_tab_bar_height)
+ change_height_p = 1;
+
+ row = it.glyph_row - 1;
+
+ /* If there are blank lines at the end, except for a partially
+ visible blank line at the end that is smaller than
+ FRAME_LINE_HEIGHT, change the tab-bar's height. */
+ if (!row->displays_text_p
+ && row->height >= FRAME_LINE_HEIGHT (f))
+ change_height_p = 1;
+
+ /* If row displays tab-bar items, but is partially visible,
+ change the tab-bar's height. */
+ if (row->displays_text_p
+ && MATRIX_ROW_BOTTOM_Y (row) > it.last_visible_y
+ && MATRIX_ROW_BOTTOM_Y (row) < max_tab_bar_height)
+ change_height_p = 1;
+
+ /* Resize windows as needed by changing the `tab-bar-lines'
+ frame parameter. */
+ if (change_height_p)
+ {
+ extern Lisp_Object Qtab_bar_lines;
+ Lisp_Object frame;
+ int old_height = WINDOW_TOTAL_LINES (w);
+ int nrows;
+ int nlines = tab_bar_lines_needed (f, &nrows);
+
+ change_height_p = ((EQ (Vauto_resize_tab_bars, Qgrow_only)
+ && !f->minimize_tab_bar_window_p)
+ ? (nlines > old_height)
+ : (nlines != old_height));
+ f->minimize_tab_bar_window_p = 0;
+
+ if (change_height_p)
+ {
+ XSETFRAME (frame, f);
+ Fmodify_frame_parameters (frame,
+ Fcons (Fcons (Qtab_bar_lines,
+ make_number (nlines)),
+ Qnil));
+ if (WINDOW_TOTAL_LINES (w) != old_height)
+ {
+ clear_glyph_matrix (w->desired_matrix);
+ f->n_tab_bar_rows = nrows;
+ fonts_changed_p = 1;
+ return 1;
+ }
+ }
+ }
+ }
+
+ f->minimize_tab_bar_window_p = 0;
+ return 0;
+}
+
+
+/* Get information about the tab-bar item which is displayed in GLYPH
+ on frame F. Return in *PROP_IDX the index where tab-bar item
+ properties start in F->tab_bar_items. Value is zero if
+ GLYPH doesn't display a tab-bar item. */
+
+static int
+tab_bar_item_info (f, glyph, prop_idx)
+ struct frame *f;
+ struct glyph *glyph;
+ int *prop_idx;
+{
+ Lisp_Object prop;
+ int success_p;
+ int charpos;
+
+ /* This function can be called asynchronously, which means we must
+ exclude any possibility that Fget_text_property signals an
+ error. */
+ charpos = min (SCHARS (f->current_tab_bar_string), glyph->charpos);
+ charpos = max (0, charpos);
+
+ /* Get the text property `menu-item' at pos. The value of that
+ property is the start index of this item's properties in
+ F->tab_bar_items. */
+ prop = Fget_text_property (make_number (charpos),
+ Qmenu_item, f->current_tab_bar_string);
+ if (INTEGERP (prop))
+ {
+ *prop_idx = XINT (prop);
+ success_p = 1;
+ }
+ else
+ success_p = 0;
+
+ return success_p;
+}
+
+
+/* Get information about the tab-bar item at position X/Y on frame F.
+ Return in *GLYPH a pointer to the glyph of the tab-bar item in
+ the current matrix of the tab-bar window of F, or NULL if not
+ on a tab-bar item. Return in *PROP_IDX the index of the tab-bar
+ item in F->tab_bar_items. Value is
+
+ -1 if X/Y is not on a tab-bar item
+ 0 if X/Y is on the same item that was highlighted before.
+ 1 otherwise. */
+
+static int
+get_tab_bar_item (f, x, y, glyph, hpos, vpos, prop_idx)
+ struct frame *f;
+ int x, y;
+ struct glyph **glyph;
+ int *hpos, *vpos, *prop_idx;
+{
+ Display_Info *dpyinfo = FRAME_X_DISPLAY_INFO (f);
+ struct window *w = XWINDOW (f->tab_bar_window);
+ int area;
+
+ /* Find the glyph under X/Y. */
+ *glyph = x_y_to_hpos_vpos (w, x, y, hpos, vpos, 0, 0, &area);
+ if (*glyph == NULL)
+ return -1;
+
+ /* Get the start of this tab-bar item's properties in
+ f->tab_bar_items. */
+ if (!tab_bar_item_info (f, *glyph, prop_idx))
+ return -1;
+
+ /* Is mouse on the highlighted item? */
+ if (EQ (f->tab_bar_window, dpyinfo->mouse_face_window)
+ && *vpos >= dpyinfo->mouse_face_beg_row
+ && *vpos <= dpyinfo->mouse_face_end_row
+ && (*vpos > dpyinfo->mouse_face_beg_row
+ || *hpos >= dpyinfo->mouse_face_beg_col)
+ && (*vpos < dpyinfo->mouse_face_end_row
+ || *hpos < dpyinfo->mouse_face_end_col
+ || dpyinfo->mouse_face_past_end))
+ return 0;
+
+ return 1;
+}
+
+
+/* EXPORT:
+ Handle mouse button event on the tab-bar of frame F, at
+ frame-relative coordinates X/Y. DOWN_P is 1 for a button press,
+ 0 for button release. MODIFIERS is event modifiers for button
+ release. */
+
+void
+handle_tab_bar_click (f, x, y, down_p, modifiers)
+ struct frame *f;
+ int x, y, down_p;
+ unsigned int modifiers;
+{
+ Display_Info *dpyinfo = FRAME_X_DISPLAY_INFO (f);
+ struct window *w = XWINDOW (f->tab_bar_window);
+ int hpos, vpos, prop_idx;
+ struct glyph *glyph;
+ Lisp_Object enabled_p;
+
+ /* If not on the highlighted tab-bar item, return. */
+ frame_to_window_pixel_xy (w, &x, &y);
+ if (get_tab_bar_item (f, x, y, &glyph, &hpos, &vpos, &prop_idx) != 0)
+ return;
+
+ /* If item is disabled, do nothing. */
+ enabled_p = AREF (f->tab_bar_items, prop_idx + TAB_BAR_ITEM_ENABLED_P);
+ if (NILP (enabled_p))
+ return;
+
+ if (down_p)
+ {
+ /* Show item in pressed state. */
+ show_mouse_face (dpyinfo, DRAW_IMAGE_SUNKEN);
+ dpyinfo->mouse_face_image_state = DRAW_IMAGE_SUNKEN;
+ last_tab_bar_item = prop_idx;
+ }
+ else
+ {
+ Lisp_Object key, frame;
+ struct input_event event;
+ EVENT_INIT (event);
+
+ /* Show item in released state. */
+ show_mouse_face (dpyinfo, DRAW_IMAGE_RAISED);
+ dpyinfo->mouse_face_image_state = DRAW_IMAGE_RAISED;
+
+ key = AREF (f->tab_bar_items, prop_idx + TAB_BAR_ITEM_KEY);
+
+ XSETFRAME (frame, f);
+ event.kind = TAB_BAR_EVENT;
+ event.frame_or_window = frame;
+ event.arg = frame;
+ kbd_buffer_store_event (&event);
+
+ event.kind = TAB_BAR_EVENT;
+ event.frame_or_window = frame;
+ event.arg = key;
+ event.modifiers = modifiers;
+ kbd_buffer_store_event (&event);
+ last_tab_bar_item = -1;
+ }
+}
+
+
+/* Possibly highlight a tab-bar item on frame F when mouse moves to
+ tab-bar window-relative coordinates X/Y. Called from
+ note_mouse_highlight. */
+
+static void
+note_tab_bar_highlight (f, x, y)
+ struct frame *f;
+ int x, y;
+{
+ Lisp_Object window = f->tab_bar_window;
+ struct window *w = XWINDOW (window);
+ Display_Info *dpyinfo = FRAME_X_DISPLAY_INFO (f);
+ int hpos, vpos;
+ struct glyph *glyph;
+ struct glyph_row *row;
+ int i;
+ Lisp_Object enabled_p;
+ int prop_idx;
+ enum draw_glyphs_face draw = DRAW_IMAGE_RAISED;
+ int mouse_down_p, rc;
+
+ /* Function note_mouse_highlight is called with negative x(y
+ values when mouse moves outside of the frame. */
+ if (x <= 0 || y <= 0)
+ {
+ clear_mouse_face (dpyinfo);
+ return;
+ }
+
+ rc = get_tab_bar_item (f, x, y, &glyph, &hpos, &vpos, &prop_idx);
+ if (rc < 0)
+ {
+ /* Not on tab-bar item. */
+ clear_mouse_face (dpyinfo);
+ return;
+ }
+ else if (rc == 0)
+ /* On same tab-bar item as before. */
+ goto set_help_echo;
+
+ clear_mouse_face (dpyinfo);
+
+ /* Mouse is down, but on different tab-bar item? */
+ mouse_down_p = (dpyinfo->grabbed
+ && f == last_mouse_frame
+ && FRAME_LIVE_P (f));
+ if (mouse_down_p
+ && last_tab_bar_item != prop_idx)
+ return;
+
+ dpyinfo->mouse_face_image_state = DRAW_NORMAL_TEXT;
+ draw = mouse_down_p ? DRAW_IMAGE_SUNKEN : DRAW_IMAGE_RAISED;
+
+ /* If tab-bar item is not enabled, don't highlight it. */
+ enabled_p = AREF (f->tab_bar_items, prop_idx + TAB_BAR_ITEM_ENABLED_P);
+ if (!NILP (enabled_p))
+ {
+ /* Compute the x-position of the glyph. In front and past the
+ image is a space. We include this in the highlighted area. */
+ row = MATRIX_ROW (w->current_matrix, vpos);
+ for (i = x = 0; i < hpos; ++i)
+ x += row->glyphs[TEXT_AREA][i].pixel_width;
+
+ /* Record this as the current active region. */
+ dpyinfo->mouse_face_beg_col = hpos;
+ dpyinfo->mouse_face_beg_row = vpos;
+ dpyinfo->mouse_face_beg_x = x;
+ dpyinfo->mouse_face_beg_y = row->y;
+ dpyinfo->mouse_face_past_end = 0;
+
+ dpyinfo->mouse_face_end_col = hpos + 1;
+ dpyinfo->mouse_face_end_row = vpos;
+ dpyinfo->mouse_face_end_x = x + glyph->pixel_width;
+ dpyinfo->mouse_face_end_y = row->y;
+ dpyinfo->mouse_face_window = window;
+ dpyinfo->mouse_face_face_id = TAB_BAR_FACE_ID;
+
+ /* Display it as active. */
+ show_mouse_face (dpyinfo, draw);
+ dpyinfo->mouse_face_image_state = draw;
+ }
+
+ set_help_echo:
+
+ /* Set help_echo_string to a help string to display for this tab-bar item.
+ XTread_socket does the rest. */
+ help_echo_object = help_echo_window = Qnil;
+ help_echo_pos = -1;
+ help_echo_string = AREF (f->tab_bar_items, prop_idx + TAB_BAR_ITEM_HELP);
+ if (NILP (help_echo_string))
+ help_echo_string = AREF (f->tab_bar_items, prop_idx + TAB_BAR_ITEM_CAPTION);
+}
+
+#endif /* HAVE_WINDOW_SYSTEM */
+
+
+
/************************************************************************
Horizontal scrolling
************************************************************************/
@@ -11693,7 +12660,7 @@ redisplay_internal (preserve_echo_area)
if (frame_garbaged)
clear_garbaged_frames ();
- /* Build menubar and tool-bar items. */
+ /* Build menubar, tool-bar and tab-bar items. */
if (NILP (Vmemory_full))
prepare_menu_bars ();
@@ -13820,8 +14787,8 @@ redisplay_window (window, just_this_one_p)
&& !NILP (echo_area_buffer[0]))
{
if (update_mode_line)
- /* We may have to update a tty frame's menu bar or a
- tool-bar. Example `M-x C-h C-h C-g'. */
+ /* We may have to update a tty frame's menu bar, a
+ tool-bar or a tab-bar. Example `M-x C-h C-h C-g'. */
goto finish_menu_bars;
else
/* We've already displayed the echo area glyphs in this window. */
@@ -14466,6 +15433,7 @@ redisplay_window (window, just_this_one_p)
{
int redisplay_menu_p = 0;
int redisplay_tool_bar_p = 0;
+ int redisplay_tab_bar_p = 0;
if (FRAME_WINDOW_P (f))
{
@@ -14491,6 +15459,9 @@ redisplay_window (window, just_this_one_p)
redisplay_tool_bar_p = WINDOWP (f->tool_bar_window)
&& (FRAME_TOOL_BAR_LINES (f) > 0
|| !NILP (Vauto_resize_tool_bars));
+ redisplay_tab_bar_p = WINDOWP (f->tab_bar_window)
+ && (FRAME_TAB_BAR_LINES (f) > 0
+ || !NILP (Vauto_resize_tab_bars));
#endif
if (redisplay_tool_bar_p && redisplay_tool_bar (f))
@@ -14498,6 +15469,12 @@ redisplay_window (window, just_this_one_p)
extern int ignore_mouse_drag_p;
ignore_mouse_drag_p = 1;
}
+
+ if (redisplay_tab_bar_p && redisplay_tab_bar (f))
+ {
+ extern int ignore_mouse_drag_p;
+ ignore_mouse_drag_p = 1;
+ }
}
#endif
}
@@ -16452,6 +17429,26 @@ GLYPH > 1 or omitted means dump glyphs in long form. */)
return Qnil;
}
+DEFUN ("dump-tab-bar-row", Fdump_tab_bar_row, Sdump_tab_bar_row, 1, 2, "",
+ doc: /* Dump glyph row ROW of the tab-bar of the current frame to stderr.
+GLYPH 0 means don't dump glyphs.
+GLYPH 1 means dump glyphs in short form.
+GLYPH > 1 or omitted means dump glyphs in long form. */)
+ (row, glyphs)
+ Lisp_Object row, glyphs;
+{
+ struct frame *sf = SELECTED_FRAME ();
+ struct glyph_matrix *m = XWINDOW (sf->tab_bar_window)->current_matrix;
+ int vpos;
+
+ CHECK_NUMBER (row);
+ vpos = XINT (row);
+ if (vpos >= 0 && vpos < m->nrows)
+ dump_glyph_row (MATRIX_ROW (m, vpos), vpos,
+ INTEGERP (glyphs) ? XINT (glyphs) : 2);
+ return Qnil;
+}
+
DEFUN ("trace-redisplay", Ftrace_redisplay, Strace_redisplay, 0, 1, "P",
doc: /* Toggle tracing of redisplay.
@@ -23643,6 +24640,8 @@ show_mouse_face (dpyinfo, draw)
/* Change the mouse cursor. */
if (draw == DRAW_NORMAL_TEXT && !EQ (dpyinfo->mouse_face_window, f->tool_bar_window))
FRAME_RIF (f)->define_frame_cursor (f, FRAME_X_OUTPUT (f)->text_cursor);
+ else if (draw == DRAW_NORMAL_TEXT && !EQ (dpyinfo->mouse_face_window, f->tab_bar_window))
+ FRAME_RIF (f)->define_frame_cursor (f, FRAME_X_OUTPUT (f)->text_cursor);
else if (draw == DRAW_MOUSE_FACE)
FRAME_RIF (f)->define_frame_cursor (f, FRAME_X_OUTPUT (f)->hand_cursor);
else
@@ -24436,7 +25435,7 @@ note_mouse_highlight (f, x, y)
}
/* Which window is that in? */
- window = window_from_coordinates (f, x, y, &part, 0, 0, 1);
+ window = window_from_coordinates (f, x, y, &part, 0, 0, 1, 1);
/* If we were displaying active text in another window, clear that.
Also clear if we move out of text area in same window. */
@@ -24464,6 +25463,14 @@ note_mouse_highlight (f, x, y)
return;
}
+ /* Handle tab-bar window differently since it doesn't display a
+ buffer. */
+ if (EQ (window, f->tab_bar_window))
+ {
+ note_tab_bar_highlight (f, x, y);
+ return;
+ }
+
/* Mouse is on the mode, header line or margin? */
if (part == ON_MODE_LINE || part == ON_HEADER_LINE
|| part == ON_LEFT_MARGIN || part == ON_RIGHT_MARGIN)
@@ -25351,6 +26358,10 @@ expose_frame (f, x, y, w, h)
mouse_face_overwritten_p
|= expose_window (XWINDOW (f->tool_bar_window), &r);
+ if (WINDOWP (f->tab_bar_window))
+ mouse_face_overwritten_p
+ |= expose_window (XWINDOW (f->tab_bar_window), &r);
+
#ifdef HAVE_X_WINDOWS
#ifndef MSDOS
#ifndef USE_X_TOOLKIT
@@ -25473,11 +26484,13 @@ syms_of_xdisp ()
defsubr (&Sdump_glyph_matrix);
defsubr (&Sdump_glyph_row);
defsubr (&Sdump_tool_bar_row);
+ defsubr (&Sdump_tab_bar_row);
defsubr (&Strace_redisplay);
defsubr (&Strace_to_stderr);
#endif
#ifdef HAVE_WINDOW_SYSTEM
defsubr (&Stool_bar_lines_needed);
+ defsubr (&Stab_bar_lines_needed);
defsubr (&Slookup_image_map);
#endif
defsubr (&Sformat_mode_line);
@@ -25895,6 +26908,42 @@ vertical margin. */);
doc: /* *Relief thickness of tool-bar buttons. */);
tool_bar_button_relief = DEFAULT_TOOL_BAR_BUTTON_RELIEF;
+ DEFVAR_LISP ("auto-resize-tab-bars", &Vauto_resize_tab_bars,
+ doc: /* *Non-nil means automatically resize tab-bars.
+This dynamically changes the tab-bar's height to the minimum height
+that is needed to make all tab-bar items visible.
+If value is `grow-only', the tab-bar's height is only increased
+automatically; to decrease the tab-bar height, use \\[recenter]. */);
+ Vauto_resize_tab_bars = Qt;
+
+ DEFVAR_BOOL ("auto-raise-tab-bar-buttons", &auto_raise_tab_bar_buttons_p,
+ doc: /* *Non-nil means raise tab-bar buttons when the mouse moves over them. */);
+ auto_raise_tab_bar_buttons_p = 1;
+
+ DEFVAR_BOOL ("make-cursor-line-fully-visible", &make_cursor_line_fully_visible_p,
+ doc: /* *Non-nil means to scroll (recenter) cursor line if it is not fully visible. */);
+ make_cursor_line_fully_visible_p = 1;
+
+ DEFVAR_LISP ("tab-bar-border", &Vtab_bar_border,
+ doc: /* *Border below tab-bar in pixels.
+If an integer, use it as the height of the border.
+If it is one of `internal-border-width' or `border-width', use the
+value of the corresponding frame parameter.
+Otherwise, no border is added below the tab-bar. */);
+ Vtab_bar_border = Qinternal_border_width;
+
+ DEFVAR_LISP ("tab-bar-button-margin", &Vtab_bar_button_margin,
+ doc: /* *Margin around tab-bar buttons in pixels.
+If an integer, use that for both horizontal and vertical margins.
+Otherwise, value should be a pair of integers `(HORZ . VERT)' with
+HORZ specifying the horizontal margin, and VERT specifying the
+vertical margin. */);
+ Vtab_bar_button_margin = make_number (DEFAULT_TAB_BAR_BUTTON_MARGIN);
+
+ DEFVAR_INT ("tab-bar-button-relief", &tab_bar_button_relief,
+ doc: /* *Relief thickness of tab-bar buttons. */);
+ tab_bar_button_relief = DEFAULT_TAB_BAR_BUTTON_RELIEF;
+
DEFVAR_LISP ("fontification-functions", &Vfontification_functions,
doc: /* List of functions to call to fontify regions of text.
Each function is called with one argument POS. Functions must
diff --git a/src/xfaces.c b/src/xfaces.c
index 6bde1c121d2..01c610be987 100644
--- a/src/xfaces.c
+++ b/src/xfaces.c
@@ -348,7 +348,7 @@ Lisp_Object Qframe_set_background_mode;
/* Names of basic faces. */
-Lisp_Object Qdefault, Qtool_bar, Qregion, Qfringe;
+Lisp_Object Qdefault, Qtool_bar, Qtab_bar, Qregion, Qfringe;
Lisp_Object Qheader_line, Qscroll_bar, Qcursor, Qborder, Qmouse, Qmenu;
Lisp_Object Qmode_line_inactive, Qvertical_border;
extern Lisp_Object Qmode_line;
@@ -4834,6 +4834,7 @@ lookup_basic_face (f, face_id)
case MODE_LINE_INACTIVE_FACE_ID: name = Qmode_line_inactive; break;
case HEADER_LINE_FACE_ID: name = Qheader_line; break;
case TOOL_BAR_FACE_ID: name = Qtool_bar; break;
+ case TAB_BAR_FACE_ID: name = Qtab_bar; break;
case FRINGE_FACE_ID: name = Qfringe; break;
case SCROLL_BAR_FACE_ID: name = Qscroll_bar; break;
case BORDER_FACE_ID: name = Qborder; break;
@@ -5570,6 +5571,7 @@ realize_basic_faces (f)
realize_named_face (f, Qmode_line, MODE_LINE_FACE_ID);
realize_named_face (f, Qmode_line_inactive, MODE_LINE_INACTIVE_FACE_ID);
realize_named_face (f, Qtool_bar, TOOL_BAR_FACE_ID);
+ realize_named_face (f, Qtab_bar, TAB_BAR_FACE_ID);
realize_named_face (f, Qfringe, FRINGE_FACE_ID);
realize_named_face (f, Qheader_line, HEADER_LINE_FACE_ID);
realize_named_face (f, Qscroll_bar, SCROLL_BAR_FACE_ID);
@@ -6868,6 +6870,8 @@ syms_of_xfaces ()
staticpro (&Qdefault);
Qtool_bar = intern_c_string ("tool-bar");
staticpro (&Qtool_bar);
+ Qtab_bar = intern_c_string ("tab-bar");
+ staticpro (&Qtab_bar);
Qregion = intern_c_string ("region");
staticpro (&Qregion);
Qfringe = intern_c_string ("fringe");
diff --git a/src/xfns.c b/src/xfns.c
index b70f20fe644..3ac290d13ea 100644
--- a/src/xfns.c
+++ b/src/xfns.c
@@ -157,6 +157,10 @@ int x_gtk_file_dialog_help_text;
int x_gtk_whole_detached_tool_bar;
+/* If non-zero, don't collapse to tab bar when it is detached. */
+
+/* int x_gtk_whole_detached_tab_bar; */
+
/* The background and shape of the mouse pointer, and shape when not
over text or in the modeline. */
@@ -515,6 +519,7 @@ void x_explicitly_set_name P_ ((struct frame *, Lisp_Object, Lisp_Object));
void x_set_menu_bar_lines P_ ((struct frame *, Lisp_Object, Lisp_Object));
void x_set_title P_ ((struct frame *, Lisp_Object, Lisp_Object));
void x_set_tool_bar_lines P_ ((struct frame *, Lisp_Object, Lisp_Object));
+void x_set_tab_bar_lines P_ ((struct frame *, Lisp_Object, Lisp_Object));
void x_set_scroll_bar_foreground P_ ((struct frame *, Lisp_Object,
Lisp_Object));
void x_set_scroll_bar_background P_ ((struct frame *, Lisp_Object,
@@ -1455,6 +1460,103 @@ x_set_tool_bar_lines (f, value, oldval)
}
+/* Set the number of lines used for the tab bar of frame F to VALUE.
+ VALUE not an integer, or < 0 means set the lines to zero. OLDVAL
+ is the old number of tab bar lines. This function changes the
+ height of all windows on frame F to match the new tab bar height.
+ The frame's height doesn't change. */
+
+void
+x_set_tab_bar_lines (f, value, oldval)
+ struct frame *f;
+ Lisp_Object value, oldval;
+{
+ int delta, nlines, root_height;
+ Lisp_Object root_window;
+
+ /* Treat tab bars like menu bars. */
+ if (FRAME_MINIBUF_ONLY_P (f))
+ return;
+
+ /* Use VALUE only if an integer >= 0. */
+ if (INTEGERP (value) && XINT (value) >= 0)
+ nlines = XFASTINT (value);
+ else
+ nlines = 0;
+
+/* #ifdef USE_GTK */
+/* FRAME_TAB_BAR_LINES (f) = 0; */
+/* if (nlines) */
+/* { */
+/* FRAME_EXTERNAL_TAB_BAR (f) = 1; */
+/* if (FRAME_X_P (f) && f->output_data.x->tabbar_widget == 0) */
+/* /\* Make sure next redisplay shows the tab bar. *\/ */
+/* XWINDOW (FRAME_SELECTED_WINDOW (f))->update_mode_line = Qt; */
+/* update_frame_tab_bar (f); */
+/* } */
+/* else */
+/* { */
+/* if (FRAME_EXTERNAL_TAB_BAR (f)) */
+/* free_frame_tab_bar (f); */
+/* FRAME_EXTERNAL_TAB_BAR (f) = 0; */
+/* } */
+/* return; */
+/* #endif */
+
+ /* Make sure we redisplay all windows in this frame. */
+ ++windows_or_buffers_changed;
+
+ delta = nlines - FRAME_TAB_BAR_LINES (f);
+
+ /* Don't resize the tab-bar to more than we have room for. */
+ root_window = FRAME_ROOT_WINDOW (f);
+ root_height = WINDOW_TOTAL_LINES (XWINDOW (root_window));
+ if (root_height - delta < 1)
+ {
+ delta = root_height - 1;
+ nlines = FRAME_TAB_BAR_LINES (f) + delta;
+ }
+
+ FRAME_TAB_BAR_LINES (f) = nlines;
+ change_window_heights (root_window, delta);
+ adjust_glyphs (f);
+
+ /* We also have to make sure that the internal border at the top of
+ the frame, below the menu bar or tab bar, is redrawn when the
+ tab bar disappears. This is so because the internal border is
+ below the tab bar if one is displayed, but is below the menu bar
+ if there isn't a tab bar. The tab bar draws into the area
+ below the menu bar. */
+ if (FRAME_X_WINDOW (f) && FRAME_TAB_BAR_LINES (f) == 0)
+ {
+ clear_frame (f);
+ clear_current_matrices (f);
+ }
+
+ /* If the tab bar gets smaller, the internal border below it
+ has to be cleared. It was formerly part of the display
+ of the larger tab bar, and updating windows won't clear it. */
+ if (delta < 0)
+ {
+ int height = FRAME_INTERNAL_BORDER_WIDTH (f);
+ int width = FRAME_PIXEL_WIDTH (f);
+ int y = nlines * FRAME_LINE_HEIGHT (f);
+
+ /* height can be zero here. */
+ if (height > 0 && width > 0)
+ {
+ BLOCK_INPUT;
+ x_clear_area (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f),
+ 0, y, width, height, False);
+ UNBLOCK_INPUT;
+ }
+
+ if (WINDOWP (f->tab_bar_window))
+ clear_glyph_matrix (XWINDOW (f->tab_bar_window)->current_matrix);
+ }
+}
+
+
/* Set the foreground color for scroll bars on frame F to VALUE.
VALUE should be a string, a color name. If it isn't a string or
isn't a valid color name, do nothing. OLDVAL is the old value of
@@ -2414,6 +2516,7 @@ xic_set_statusarea (f)
area.y = (FRAME_PIXEL_HEIGHT (f) - area.height
- FRAME_MENUBAR_HEIGHT (f)
- FRAME_TOOLBAR_HEIGHT (f)
+ - FRAME_TABBAR_HEIGHT (f)
- FRAME_INTERNAL_BORDER_WIDTH (f));
XFree (needed);
@@ -3448,6 +3551,8 @@ This function is an internal primitive--use `make-frame' instead. */)
"menuBar", "MenuBar", RES_TYPE_BOOLEAN_NUMBER);
x_default_parameter (f, parms, Qtool_bar_lines, make_number (1),
"toolBar", "ToolBar", RES_TYPE_NUMBER);
+ x_default_parameter (f, parms, Qtab_bar_lines, make_number (1),
+ "tabBar", "TabBar", RES_TYPE_NUMBER);
x_default_parameter (f, parms, Qbuffer_predicate, Qnil,
"bufferPredicate", "BufferPredicate",
RES_TYPE_SYMBOL);
@@ -3459,7 +3564,7 @@ This function is an internal primitive--use `make-frame' instead. */)
"fullscreen", "Fullscreen", RES_TYPE_SYMBOL);
/* Compute the size of the X window. */
- window_prompting = x_figure_window_size (f, parms, 1);
+ window_prompting = x_figure_window_size (f, parms, 1, 1);
tem = x_get_arg (dpyinfo, parms, Qunsplittable, 0, 0, RES_TYPE_BOOLEAN);
f->no_split = minibuffer_only || EQ (tem, Qt);
@@ -4890,7 +4995,7 @@ x_create_tip_frame (dpyinfo, parms, text)
f->output_data.x->parent_desc = FRAME_X_DISPLAY_INFO (f)->root_window;
- window_prompting = x_figure_window_size (f, parms, 0);
+ window_prompting = x_figure_window_size (f, parms, 0, 0);
{
XSetWindowAttributes attrs;
@@ -5869,6 +5974,7 @@ frame_parm_handler x_frame_parm_handlers[] =
x_set_vertical_scroll_bars,
x_set_visibility,
x_set_tool_bar_lines,
+ x_set_tab_bar_lines,
x_set_scroll_bar_foreground,
x_set_scroll_bar_background,
x_set_screen_gamma,
diff --git a/src/xmenu.c b/src/xmenu.c
index de2f4eb6815..35a6b23caa0 100644
--- a/src/xmenu.c
+++ b/src/xmenu.c
@@ -274,7 +274,8 @@ for instance using the window manager, then this produces a quit and
/* Decode the first argument: find the window or frame to use. */
if (EQ (position, Qt)
|| (CONSP (position) && (EQ (XCAR (position), Qmenu_bar)
- || EQ (XCAR (position), Qtool_bar))))
+ || EQ (XCAR (position), Qtool_bar)
+ || EQ (XCAR (position), Qtab_bar))))
{
#if 0 /* Using the frame the mouse is on may not be right. */
/* Use the mouse's current position. */
diff --git a/src/xterm.c b/src/xterm.c
index 29ed5bb865c..50fbd6ce36d 100644
--- a/src/xterm.c
+++ b/src/xterm.c
@@ -751,6 +751,11 @@ x_after_update_window_line (desired_row)
{
int y = WINDOW_TO_FRAME_PIXEL_Y (w, max (0, desired_row->y));
+ /* Internal border is drawn below the tab bar. */
+ if (WINDOWP (f->tab_bar_window)
+ && w == XWINDOW (f->tab_bar_window))
+ y -= width;
+
BLOCK_INPUT;
x_clear_area (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f),
0, y, width, height, False);
@@ -2314,6 +2319,7 @@ x_draw_image_relief (s)
|| s->hl == DRAW_IMAGE_RAISED)
{
thick = tool_bar_button_relief >= 0 ? tool_bar_button_relief : DEFAULT_TOOL_BAR_BUTTON_RELIEF;
+ /* thick = tab_bar_button_relief >= 0 ? tab_bar_button_relief : DEFAULT_TAB_BAR_BUTTON_RELIEF; */
raised_p = s->hl == DRAW_IMAGE_RAISED;
}
else
@@ -6333,7 +6339,8 @@ handle_one_xevent (dpyinfo, eventp, finish, hold_quit)
mouse highlighting. */
if (!dpyinfo->mouse_face_hidden && INTEGERP (Vmouse_highlight)
&& (f == 0
- || !EQ (f->tool_bar_window, dpyinfo->mouse_face_window)))
+ || !EQ (f->tool_bar_window, dpyinfo->mouse_face_window)
+ || !EQ (f->tab_bar_window, dpyinfo->mouse_face_window)))
{
clear_mouse_face (dpyinfo);
dpyinfo->mouse_face_hidden = 1;
@@ -6752,7 +6759,7 @@ handle_one_xevent (dpyinfo, eventp, finish, hold_quit)
window = window_from_coordinates (f,
event.xmotion.x, event.xmotion.y,
- 0, 0, 0, 0);
+ 0, 0, 0, 0, 0);
/* Window will be selected only when it is not selected now and
last mouse movement event was not in it. Minibuffer window
@@ -6865,6 +6872,7 @@ handle_one_xevent (dpyinfo, eventp, finish, hold_quit)
/* If we decide we want to generate an event to be seen
by the rest of Emacs, we put it here. */
int tool_bar_p = 0;
+ int tab_bar_p = 0;
bzero (&compose_status, sizeof (compose_status));
last_mouse_glyph_frame = 0;
@@ -6891,7 +6899,7 @@ handle_one_xevent (dpyinfo, eventp, finish, hold_quit)
int x = event.xbutton.x;
int y = event.xbutton.y;
- window = window_from_coordinates (f, x, y, 0, 0, 0, 1);
+ window = window_from_coordinates (f, x, y, 0, 0, 0, 1, 1);
tool_bar_p = EQ (window, f->tool_bar_window);
if (tool_bar_p && event.xbutton.button < 4)
@@ -6903,7 +6911,27 @@ handle_one_xevent (dpyinfo, eventp, finish, hold_quit)
}
}
- if (!tool_bar_p)
+ /* Is this in the tab-bar? */
+ if (WINDOWP (f->tab_bar_window)
+ && WINDOW_TOTAL_LINES (XWINDOW (f->tab_bar_window)))
+ {
+ Lisp_Object window;
+ int x = event.xbutton.x;
+ int y = event.xbutton.y;
+
+ window = window_from_coordinates (f, x, y, 0, 0, 0, 1, 1);
+ tab_bar_p = EQ (window, f->tab_bar_window);
+
+ if (tab_bar_p && event.xbutton.button < 4)
+ {
+ handle_tab_bar_click (f, x, y,
+ event.xbutton.type == ButtonPress,
+ x_x_to_emacs_modifiers (dpyinfo,
+ event.xbutton.state));
+ }
+ }
+
+ if (!tool_bar_p && !tab_bar_p)
#if defined (USE_X_TOOLKIT) || defined (USE_GTK)
if (! popup_activated ())
#endif
@@ -6953,6 +6981,9 @@ handle_one_xevent (dpyinfo, eventp, finish, hold_quit)
if (!tool_bar_p)
last_tool_bar_item = -1;
+
+ if (!tab_bar_p)
+ last_tab_bar_item = -1;
}
else
dpyinfo->grabbed &= ~(1 << event.xbutton.button);
@@ -8933,7 +8964,7 @@ x_set_window_size_1 (f, change_gravity, cols, rows)
pixelwidth = FRAME_TEXT_COLS_TO_PIXEL_WIDTH (f, cols);
pixelheight = FRAME_TEXT_LINES_TO_PIXEL_HEIGHT (f, rows)
- + FRAME_MENUBAR_HEIGHT (f) + FRAME_TOOLBAR_HEIGHT (f);
+ + FRAME_MENUBAR_HEIGHT (f) + FRAME_TOOLBAR_HEIGHT (f) + FRAME_TABBAR_HEIGHT (f);
if (change_gravity) f->win_gravity = NorthWestGravity;
x_wm_set_size_hint (f, (long) 0, 0);
@@ -9003,7 +9034,7 @@ x_set_window_size (f, change_gravity, cols, rows)
int pixelh = FRAME_PIXEL_HEIGHT (f);
#ifdef USE_X_TOOLKIT
/* The menu bar is not part of text lines. The tool bar
- is however. */
+ and the tab bar is however. */
pixelh -= FRAME_MENUBAR_HEIGHT (f);
#endif
r = FRAME_PIXEL_HEIGHT_TO_TEXT_LINES (f, pixelh);
@@ -10893,6 +10924,7 @@ x_initialize ()
x_noop_count = 0;
last_tool_bar_item = -1;
+ last_tab_bar_item = -1;
any_help_event_p = 0;
ignore_next_mouse_click_timeout = 0;
#ifdef HAVE_X_SM
diff --git a/src/xterm.h b/src/xterm.h
index a766f863c4d..79fe2e16b12 100644
--- a/src/xterm.h
+++ b/src/xterm.h
@@ -431,6 +431,10 @@ struct x_output
Zero if not using an external tool bar. */
int toolbar_height;
+ /* Height of tab bar widget, in pixels.
+ Zero if not using an external tab bar. */
+ int tabbar_height;
+
/* The tiled border used when the mouse is out of the frame. */
Pixmap border_tile;
@@ -681,6 +685,7 @@ enum
#define FRAME_FONTSET(f) ((f)->output_data.x->fontset)
#define FRAME_MENUBAR_HEIGHT(f) ((f)->output_data.x->menubar_height)
#define FRAME_TOOLBAR_HEIGHT(f) ((f)->output_data.x->toolbar_height)
+#define FRAME_TABBAR_HEIGHT(f) ((f)->output_data.x->tabbar_height)
#define FRAME_BASELINE_OFFSET(f) ((f)->output_data.x->baseline_offset)
/* This gives the x_display_info structure for the display F is on. */
@@ -709,7 +714,7 @@ enum
((f)->output_data.x->x_pixels_outer_diff)
#define FRAME_OUTER_TO_INNER_DIFF_Y(f) \
((f)->output_data.x->y_pixels_outer_diff \
- + FRAME_MENUBAR_HEIGHT (f) + FRAME_TOOLBAR_HEIGHT (f))
+ + FRAME_MENUBAR_HEIGHT (f) + FRAME_TOOLBAR_HEIGHT (f) + FRAME_TABBAR_HEIGHT (f))
#define FRAME_XIC(f) ((f)->output_data.x->xic)
@@ -1034,6 +1039,7 @@ extern int x_defined_color P_ ((struct frame *, char *, XColor *, int));
extern void free_frame_xic P_ ((struct frame *));
#endif
extern void x_set_tool_bar_lines P_ ((struct frame *, Lisp_Object, Lisp_Object));
+extern void x_set_tab_bar_lines P_ ((struct frame *, Lisp_Object, Lisp_Object));
/* Defined in xfaces.c */