]> git.eshelyaron.com Git - emacs.git/commitdiff
Initial implementation of non-toolkit X tabs and tab-bar.
authorJuri Linkov <juri@jurta.org>
Fri, 9 Apr 2010 23:19:38 +0000 (02:19 +0300)
committerJuri Linkov <juri@jurta.org>
Fri, 9 Apr 2010 23:19:38 +0000 (02:19 +0300)
39 files changed:
etc/images/tab-delete.pbm [new file with mode: 0644]
etc/images/tab-delete.xpm [new file with mode: 0644]
etc/images/tab-left.pbm [new file with mode: 0644]
etc/images/tab-left.xpm [new file with mode: 0644]
etc/images/tab-right.pbm [new file with mode: 0644]
etc/images/tab-right.xpm [new file with mode: 0644]
lisp/bindings.el
lisp/dired.el
lisp/emacs-lisp/find-func.el
lisp/files.el
lisp/loadup.el
lisp/menu-bar.el
lisp/progmodes/etags.el
lisp/startup.el
lisp/subr.el
lisp/tab-bar.el [new file with mode: 0644]
lisp/tab.el [new file with mode: 0644]
src/Makefile.in
src/dispextern.h
src/dispnew.c
src/frame.c
src/frame.h
src/keyboard.c
src/keymap.c
src/lisp.h
src/menu.c
src/msdos.c
src/term.c
src/termhooks.h
src/w32fns.c
src/w32term.c
src/window.c
src/window.h
src/xdisp.c
src/xfaces.c
src/xfns.c
src/xmenu.c
src/xterm.c
src/xterm.h

diff --git a/etc/images/tab-delete.pbm b/etc/images/tab-delete.pbm
new file mode 100644 (file)
index 0000000..1b125d7
Binary files /dev/null and b/etc/images/tab-delete.pbm differ
diff --git a/etc/images/tab-delete.xpm b/etc/images/tab-delete.xpm
new file mode 100644 (file)
index 0000000..651c986
--- /dev/null
@@ -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 (file)
index 0000000..65376e2
Binary files /dev/null and b/etc/images/tab-left.pbm differ
diff --git a/etc/images/tab-left.xpm b/etc/images/tab-left.xpm
new file mode 100644 (file)
index 0000000..7fc0aba
--- /dev/null
@@ -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 (file)
index 0000000..3b85820
Binary files /dev/null and b/etc/images/tab-right.pbm differ
diff --git a/etc/images/tab-right.xpm b/etc/images/tab-right.xpm
new file mode 100644 (file)
index 0000000..f4231af
--- /dev/null
@@ -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.       ",
+"        ..        ",
+"        .         ",
+"                  "};
index a7f6643b2dbdcff922761bcef6f817d3d3eccde2..5e709a6b96893acc82116627b39f89f4d7645ec8 100644 (file)
@@ -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)
 \f
 
 (defvar ctl-x-r-map (make-sparse-keymap)
index 0dc53bf32c4a870dbd0b980743c3c2f5f354d0f5..26ed3957003bcf14fa7e26a676ddd1544ab3e07e 100644 (file)
@@ -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."
index 216d91baa7b8b0b46a3f464f32b20bae122aa605..b678d59ada3599c549e93daeb476188b5dc63c2c 100644 (file)
@@ -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)
 
index b341fe710767333e02a8f82c68f08fecc746bc4e..c65e1e87b5f3574eb92b127faf9127c68c7436ea 100644 (file)
@@ -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
index 85222ce7d9e98daedbda7b79dd3f87029fd2bf92..fa5e7ae1e8e0f2492c1a37c00183eb7fff63c085 100644 (file)
       (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"))
index d831744f31139d2aa7fd9a1a634a91723fef7255..3f8d6829fdc86f7de663e4a7e532feb26a574776 100644 (file)
@@ -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))
 
index 23e175cbe7d67509c403af8b79e6cdd1a5a888fd..fd354b05e18f4c6c200226eaa715261787a15e9d 100644 (file)
@@ -994,6 +994,31 @@ See documentation of variable `tags-file-name'."
     (find-tag-other-window tagname next-p)))
 ;;;###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.
index 87f1a00bd542ca89b596c32023a2e6441a13bc3f..e8a6dc3dbb5c46e3bb9989bd31c026e82a519072 100644 (file)
@@ -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))
index 6118c49530c4119cfddf4f8186d2890834c1f1d7..eab82cfe86a1e484cc127745c16cd4bb3fd7b49c 100644 (file)
@@ -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)
+
 \f
 ;;;; Event manipulation functions.
 
diff --git a/lisp/tab-bar.el b/lisp/tab-bar.el
new file mode 100644 (file)
index 0000000..35698d8
--- /dev/null
@@ -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")
+
+\f
+(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 (file)
index 0000000..09e9c45
--- /dev/null
@@ -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))))
+
+\f
+;;; 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))))
+
+\f
+;;; 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)))
+
+\f
+;;; 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)
+
+\f
+;;;; 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
index cd5027dc4d83f23a6dc4fd2931ce7736b242d839..254f8344bbed1303fd0ea79c53f74483cacee0f8 100644 (file)
@@ -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 \
index b8f68ec0e700adade03826ada457cc048fabc654..e9846c81bb9d010ff3f57ff2c1e3e8451993bc71 100644 (file)
@@ -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,
@@ -2817,6 +2818,80 @@ extern EMACS_INT tool_bar_button_relief;
 
 #define DEFAULT_TOOL_BAR_IMAGE_HEIGHT 24
 
+\f
+/***********************************************************************
+                              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
+
 \f
 /***********************************************************************
                         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 *));
index 2be00c9c3b02f54e66e4976bb6d5e772e39c69af..30ef5b533f42e5fbbb12e1feb3dae8d22a6c117d 100644 (file)
@@ -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;
index 757ed8f01a364f3f2c86c3c4f506a1f2feec2d7e..9c427720103fe01cc8f295a03ba2be78c23b7c80 100644 (file)
@@ -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));
index 36b0b6b069cabdb0debc17ce58c8fe48675d33b6..3e94366cbefc201f525b90ecbae770ed062785a1 100644 (file)
@@ -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));
index f2aeff895425b7587624af9cc8402f2740a57955..31bcb887f9fd01a07f758f1363c9a19ab662d229 100644 (file)
@@ -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.  */
       {
@@ -8433,6 +8445,389 @@ append_tool_bar_item ()
 
 
 
+\f
+/***********************************************************************
+                              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;
+}
+
+
+
+
 \f
 /* 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.
@@ -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. */
index 88e0687272f0620f6a673359bf8c136b650f5c53..71f9e10b48c29fe74923654ddce9174688fdf5c3 100644 (file)
@@ -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");
index 7f5d5df66c61106d5caf3223a929aa077ce4a2d7..a91174a7f0dcc22a3ea8b27eb84cf451d0950c4f 100644 (file)
@@ -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);
index ca00c06a98b08bfbe26bb8f4e91e180aa7127e09..209c3f2e211d87ec0806671eea9576b8fd8eed5c 100644 (file)
@@ -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;
       }
index 6cde7cd6ebc301460d6f47eeba788aede0aee82d..68dd7d62e4d5c5cb00638237c82f54d493178d58 100644 (file)
@@ -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
index 718a20d4164d849af0b6be8cf36d8464184c14f9..e512cc96ba385ff3ba571ed668ced6bb763faed2 100644 (file)
@@ -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))
index 2b4011627c83fb1f15c5f0863b8d61437a5a6206..eb4d286e01631b752f4b317ced1cb423354b1b42 100644 (file)
@@ -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,
index 795e7208569109102e4b58746a207c8c2ade5cb3..789b1fbb388b013514c4ab6e939d8136726cf650 100644 (file)
@@ -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;
index 7222e26efd22fdcbf90161068ec3b72e6e1e0bf6..ce939dc9b547ad0bb587da5de18c5719494f5e5b 100644 (file)
@@ -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))
                       {
index f17a645f82ce3105a694c510d183c78ecfc1e555..6193f480461e1d8d14d417f47e82583441caa40d 100644 (file)
@@ -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);
index 17332f0af20ae38db238547b4ac95bd63b53b179..53419215883e4655d57996cb9b862d4e7c833730 100644 (file)
@@ -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));
index baede013485b8c8811c9192045d3e58f2c5e84a7..e5ef4b2f54c5751637e337b3c793b79a5d6ff976 100644 (file)
@@ -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
     }
 
@@ -10159,16 +10195,927 @@ update_tool_bar (f, save_match_data)
           new_tool_bar = tool_bar_items (Fcopy_sequence (f->tool_bar_items),
                                          &new_n_tool_bar);
 
-         /* Redisplay the tool-bar if we changed it.  */
-         if (new_n_tool_bar != f->n_tool_bar_items
-             || NILP (Fequal (new_tool_bar, f->tool_bar_items)))
+         /* Redisplay the tool-bar if we changed it.  */
+         if (new_n_tool_bar != f->n_tool_bar_items
+             || NILP (Fequal (new_tool_bar, f->tool_bar_items)))
+            {
+              /* Redisplay that happens asynchronously due to an expose event
+                 may access f->tool_bar_items.  Make sure we update both
+                 variables within BLOCK_INPUT so no such event interrupts.  */
+              BLOCK_INPUT;
+              f->tool_bar_items = new_tool_bar;
+              f->n_tool_bar_items = new_n_tool_bar;
+              w->update_mode_line = Qt;
+              UNBLOCK_INPUT;
+            }
+
+         UNGCPRO;
+
+         unbind_to (count, Qnil);
+         set_buffer_internal_1 (prev);
+       }
+    }
+}
+
+
+/* Set F->desired_tool_bar_string to a Lisp string representing frame
+   F's desired tool-bar contents.  F->tool_bar_items must have
+   been set up previously by calling prepare_menu_bars.  */
+
+static void
+build_desired_tool_bar_string (f)
+     struct frame *f;
+{
+  int i, size, size_needed;
+  struct gcpro gcpro1, gcpro2, gcpro3;
+  Lisp_Object image, plist, props;
+
+  image = plist = props = Qnil;
+  GCPRO3 (image, plist, props);
+
+  /* Prepare F->desired_tool_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_tool_bar_string)
+         ? SCHARS (f->desired_tool_bar_string)
+         : 0);
+
+  /* We need one space in the string for each image.  */
+  size_needed = f->n_tool_bar_items;
+
+  /* Reuse f->desired_tool_bar_string, if possible.  */
+  if (size < size_needed || NILP (f->desired_tool_bar_string))
+    f->desired_tool_bar_string = Fmake_string (make_number (size_needed),
+                                              make_number (' '));
+  else
+    {
+      props = list4 (Qdisplay, Qnil, Qmenu_item, Qnil);
+      Fremove_text_properties (make_number (0), make_number (size),
+                              props, f->desired_tool_bar_string);
+    }
+
+  /* Put a `display' property on the string for the images to display,
+     put a `menu_item' property on tool-bar items with a value that
+     is the index of the item in F's tool-bar item vector.  */
+  for (i = 0; i < f->n_tool_bar_items; ++i)
+    {
+#define PROP(IDX) AREF (f->tool_bar_items, i * TOOL_BAR_ITEM_NSLOTS + (IDX))
+
+      int enabled_p = !NILP (PROP (TOOL_BAR_ITEM_ENABLED_P));
+      int selected_p = !NILP (PROP (TOOL_BAR_ITEM_SELECTED_P));
+      int hmargin, vmargin, relief, idx, end;
+      extern Lisp_Object QCrelief, QCmargin, QCconversion;
+
+      /* If image is a vector, choose the image according to the
+        button state.  */
+      image = PROP (TOOL_BAR_ITEM_IMAGES);
+      if (VECTORP (image))
+       {
+         if (enabled_p)
+           idx = (selected_p
+                  ? TOOL_BAR_IMAGE_ENABLED_SELECTED
+                  : TOOL_BAR_IMAGE_ENABLED_DESELECTED);
+         else
+           idx = (selected_p
+                  ? TOOL_BAR_IMAGE_DISABLED_SELECTED
+                  : TOOL_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 tool-bar button pressed, or depressed.  */
+      plist = Fcopy_sequence (XCDR (image));
+
+      /* Compute margin and relief to draw.  */
+      relief = (tool_bar_button_relief >= 0
+               ? tool_bar_button_relief
+               : DEFAULT_TOOL_BAR_BUTTON_RELIEF);
+      hmargin = vmargin = relief;
+
+      if (INTEGERP (Vtool_bar_button_margin)
+         && XINT (Vtool_bar_button_margin) > 0)
+       {
+         hmargin += XFASTINT (Vtool_bar_button_margin);
+         vmargin += XFASTINT (Vtool_bar_button_margin);
+       }
+      else if (CONSP (Vtool_bar_button_margin))
+       {
+         if (INTEGERP (XCAR (Vtool_bar_button_margin))
+             && XINT (XCAR (Vtool_bar_button_margin)) > 0)
+           hmargin += XFASTINT (XCAR (Vtool_bar_button_margin));
+
+         if (INTEGERP (XCDR (Vtool_bar_button_margin))
+             && XINT (XCDR (Vtool_bar_button_margin)) > 0)
+           vmargin += XFASTINT (XCDR (Vtool_bar_button_margin));
+       }
+
+      if (auto_raise_tool_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);
+
+      /* 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 tool-bar items
+        vector.  */
+      image = Fcons (Qimage, plist);
+      props = list4 (Qdisplay, image,
+                    Qmenu_item, make_number (i * TOOL_BAR_ITEM_NSLOTS));
+
+      /* Let the last image hide all remaining spaces in the tool bar
+         string.  The string can be longer than needed when we reuse a
+         previous string.  */
+      if (i + 1 == f->n_tool_bar_items)
+       end = SCHARS (f->desired_tool_bar_string);
+      else
+       end = i + 1;
+      Fadd_text_properties (make_number (i), make_number (end),
+                           props, f->desired_tool_bar_string);
+#undef PROP
+    }
+
+  UNGCPRO;
+}
+
+
+/* Display one line of the tool-bar of frame IT->f.
+
+   HEIGHT specifies the desired height of the tool-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 tool-bar lines, so don't
+   count a final empty row in case the tool-bar width exactly matches
+   the window width.
+*/
+
+static void
+display_tool_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 tool-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
+                toolbar, so skip it.  But ensure there is at least one glyph,
+                so we don't accidentally disable the tool-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 tool bar.
+
+     FIXME: When auto-resize-tool-bars is grow-only, there is
+     no additional border below the possibly empty tool-bar lines.
+     So to make the extra empty lines look "normal", we have to
+     use the tool-bar face for the border too.  */
+  if (!row->displays_text_p && !EQ (Vauto_resize_tool_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 tool-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 tool-bar height.  */
+
+#define MAX_FRAME_TOOL_BAR_HEIGHT(f) \
+  ((FRAME_LINE_HEIGHT (f) * FRAME_LINES (f)))
+
+/* Value is the number of screen lines needed to make all tool-bar
+   items of frame F visible.  The number of actual rows needed is
+   returned in *N_ROWS if non-NULL.  */
+
+static int
+tool_bar_lines_needed (f, n_rows)
+     struct frame *f;
+     int *n_rows;
+{
+  struct window *w = XWINDOW (f->tool_bar_window);
+  struct it it;
+  /* tool_bar_lines_needed is called from redisplay_tool_bar after building
+     the desired matrix, so use (unused) mode-line row as temporary row to
+     avoid destroying the first tool-bar row.  */
+  struct glyph_row *temp_row = MATRIX_MODE_LINE_ROW (w->desired_matrix);
+
+  /* Initialize an iterator for iteration over
+     F->desired_tool_bar_string in the tool-bar window of frame F.  */
+  init_iterator (&it, w, -1, -1, temp_row, TOOL_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_tool_bar_string, 0, 0, 0, -1);
+
+  while (!ITERATOR_AT_END_P (&it))
+    {
+      clear_glyph_row (temp_row);
+      it.glyph_row = temp_row;
+      display_tool_bar_line (&it, -1);
+    }
+  clear_glyph_row (temp_row);
+
+  /* f->n_tool_bar_rows == 0 means "unknown"; -1 means no tool-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 ("tool-bar-lines-needed", Ftool_bar_lines_needed, Stool_bar_lines_needed,
+       0, 1, 0,
+       doc: /* Return the number of lines occupied by the tool 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->tool_bar_window)
+      || (w = XWINDOW (f->tool_bar_window),
+         WINDOW_TOTAL_LINES (w) > 0))
+    {
+      update_tool_bar (f, 1);
+      if (f->n_tool_bar_items)
+       {
+         build_desired_tool_bar_string (f);
+         nlines = tool_bar_lines_needed (f, NULL);
+       }
+    }
+
+  return make_number (nlines);
+}
+
+
+/* Display the tool-bar of frame F.  Value is non-zero if tool-bar's
+   height should be changed.  */
+
+static int
+redisplay_tool_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_TOOL_BAR (f))
+    update_frame_tool_bar (f);
+  return 0;
+#endif
+
+  /* If frame hasn't a tool-bar window or if it is zero-height, don't
+     do anything.  This means you must start with tool-bar-lines
+     non-zero to get the auto-sizing effect.  Or in other words, you
+     can turn off tool-bars by specifying tool-bar-lines zero.  */
+  if (!WINDOWP (f->tool_bar_window)
+      || (w = XWINDOW (f->tool_bar_window),
+          WINDOW_TOTAL_LINES (w) == 0))
+    return 0;
+
+  /* Set up an iterator for the tool-bar window.  */
+  init_iterator (&it, w, -1, -1, w->desired_matrix->rows, TOOL_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 tool-bar.  */
+  build_desired_tool_bar_string (f);
+  reseat_to_string (&it, NULL, f->desired_tool_bar_string, 0, 0, 0, -1);
+
+  if (f->n_tool_bar_rows == 0)
+    {
+      int nlines;
+
+      if ((nlines = tool_bar_lines_needed (f, &f->n_tool_bar_rows),
+          nlines != WINDOW_TOTAL_LINES (w)))
+       {
+         extern Lisp_Object Qtool_bar_lines;
+         Lisp_Object frame;
+         int old_height = WINDOW_TOTAL_LINES (w);
+
+         XSETFRAME (frame, f);
+         Fmodify_frame_parameters (frame,
+                                   Fcons (Fcons (Qtool_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 tool-bar items.  */
+
+  if (f->n_tool_bar_rows > 0)
+    {
+      int border, rows, height, extra;
+
+      if (INTEGERP (Vtool_bar_border))
+       border = XINT (Vtool_bar_border);
+      else if (EQ (Vtool_bar_border, Qinternal_border_width))
+       border = FRAME_INTERNAL_BORDER_WIDTH (f);
+      else if (EQ (Vtool_bar_border, Qborder_width))
+       border = f->border_width;
+      else
+       border = 0;
+      if (border < 0)
+       border = 0;
+
+      rows = f->n_tool_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_tool_bar_line (&it, height + h);
+       }
+    }
+  else
+    {
+      while (it.current_y < it.last_visible_y)
+       display_tool_bar_line (&it, 0);
+    }
+
+  /* It doesn't make much sense to try scrolling in the tool-bar
+     window, so don't do it.  */
+  w->desired_matrix->no_scrolling_p = 1;
+  w->must_be_updated_p = 1;
+
+  if (!NILP (Vauto_resize_tool_bars))
+    {
+      int max_tool_bar_height = MAX_FRAME_TOOL_BAR_HEIGHT (f);
+      int change_height_p = 0;
+
+      /* If we couldn't display everything, change the tool-bar's
+        height if there is room for more.  */
+      if (IT_STRING_CHARPOS (it) < it.end_charpos
+         && it.current_y < max_tool_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 tool-bar's height.  */
+      if (!row->displays_text_p
+         && row->height >= FRAME_LINE_HEIGHT (f))
+       change_height_p = 1;
+
+      /* If row displays tool-bar items, but is partially visible,
+        change the tool-bar's height.  */
+      if (row->displays_text_p
+         && MATRIX_ROW_BOTTOM_Y (row) > it.last_visible_y
+         && MATRIX_ROW_BOTTOM_Y (row) < max_tool_bar_height)
+       change_height_p = 1;
+
+      /* Resize windows as needed by changing the `tool-bar-lines'
+        frame parameter.  */
+      if (change_height_p)
+       {
+         extern Lisp_Object Qtool_bar_lines;
+         Lisp_Object frame;
+         int old_height = WINDOW_TOTAL_LINES (w);
+         int nrows;
+         int nlines = tool_bar_lines_needed (f, &nrows);
+
+         change_height_p = ((EQ (Vauto_resize_tool_bars, Qgrow_only)
+                             && !f->minimize_tool_bar_window_p)
+                            ? (nlines > old_height)
+                            : (nlines != old_height));
+         f->minimize_tool_bar_window_p = 0;
+
+         if (change_height_p)
+           {
+             XSETFRAME (frame, f);
+             Fmodify_frame_parameters (frame,
+                                       Fcons (Fcons (Qtool_bar_lines,
+                                                     make_number (nlines)),
+                                              Qnil));
+             if (WINDOW_TOTAL_LINES (w) != old_height)
+               {
+                 clear_glyph_matrix (w->desired_matrix);
+                 f->n_tool_bar_rows = nrows;
+                 fonts_changed_p = 1;
+                 return 1;
+               }
+           }
+       }
+    }
+
+  f->minimize_tool_bar_window_p = 0;
+  return 0;
+}
+
+
+/* Get information about the tool-bar item which is displayed in GLYPH
+   on frame F.  Return in *PROP_IDX the index where tool-bar item
+   properties start in F->tool_bar_items.  Value is zero if
+   GLYPH doesn't display a tool-bar item.  */
+
+static int
+tool_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_tool_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->tool_bar_items.  */
+  prop = Fget_text_property (make_number (charpos),
+                            Qmenu_item, f->current_tool_bar_string);
+  if (INTEGERP (prop))
+    {
+      *prop_idx = XINT (prop);
+      success_p = 1;
+    }
+  else
+    success_p = 0;
+
+  return success_p;
+}
+
+\f
+/* Get information about the tool-bar item at position X/Y on frame F.
+   Return in *GLYPH a pointer to the glyph of the tool-bar item in
+   the current matrix of the tool-bar window of F, or NULL if not
+   on a tool-bar item.  Return in *PROP_IDX the index of the tool-bar
+   item in F->tool_bar_items.  Value is
+
+   -1  if X/Y is not on a tool-bar item
+   0   if X/Y is on the same item that was highlighted before.
+   1   otherwise.  */
+
+static int
+get_tool_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->tool_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 tool-bar item's properties in
+     f->tool_bar_items.  */
+  if (!tool_bar_item_info (f, *glyph, prop_idx))
+    return -1;
+
+  /* Is mouse on the highlighted item?  */
+  if (EQ (f->tool_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 tool-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_tool_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->tool_bar_window);
+  int hpos, vpos, prop_idx;
+  struct glyph *glyph;
+  Lisp_Object enabled_p;
+
+  /* If not on the highlighted tool-bar item, return.  */
+  frame_to_window_pixel_xy (w, &x, &y);
+  if (get_tool_bar_item (f, x, y, &glyph, &hpos, &vpos, &prop_idx) != 0)
+    return;
+
+  /* If item is disabled, do nothing.  */
+  enabled_p = AREF (f->tool_bar_items, prop_idx + TOOL_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_tool_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->tool_bar_items, prop_idx + TOOL_BAR_ITEM_KEY);
+
+      XSETFRAME (frame, f);
+      event.kind = TOOL_BAR_EVENT;
+      event.frame_or_window = frame;
+      event.arg = frame;
+      kbd_buffer_store_event (&event);
+
+      event.kind = TOOL_BAR_EVENT;
+      event.frame_or_window = frame;
+      event.arg = key;
+      event.modifiers = modifiers;
+      kbd_buffer_store_event (&event);
+      last_tool_bar_item = -1;
+    }
+}
+
+
+/* Possibly highlight a tool-bar item on frame F when mouse moves to
+   tool-bar window-relative coordinates X/Y.  Called from
+   note_mouse_highlight.  */
+
+static void
+note_tool_bar_highlight (f, x, y)
+     struct frame *f;
+     int x, y;
+{
+  Lisp_Object window = f->tool_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_tool_bar_item (f, x, y, &glyph, &hpos, &vpos, &prop_idx);
+  if (rc < 0)
+    {
+      /* Not on tool-bar item.  */
+      clear_mouse_face (dpyinfo);
+      return;
+    }
+  else if (rc == 0)
+    /* On same tool-bar item as before.  */
+    goto set_help_echo;
+
+  clear_mouse_face (dpyinfo);
+
+  /* Mouse is down, but on different tool-bar item?  */
+  mouse_down_p = (dpyinfo->grabbed
+                 && f == last_mouse_frame
+                 && FRAME_LIVE_P (f));
+  if (mouse_down_p
+      && last_tool_bar_item != prop_idx)
+    return;
+
+  dpyinfo->mouse_face_image_state = DRAW_NORMAL_TEXT;
+  draw = mouse_down_p ? DRAW_IMAGE_SUNKEN : DRAW_IMAGE_RAISED;
+
+  /* If tool-bar item is not enabled, don't highlight it.  */
+  enabled_p = AREF (f->tool_bar_items, prop_idx + TOOL_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 = TOOL_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 tool-bar item.
+     XTread_socket does the rest.  */
+  help_echo_object = help_echo_window = Qnil;
+  help_echo_pos = -1;
+  help_echo_string = AREF (f->tool_bar_items, prop_idx + TOOL_BAR_ITEM_HELP);
+  if (NILP (help_echo_string))
+    help_echo_string = AREF (f->tool_bar_items, prop_idx + TOOL_BAR_ITEM_CAPTION);
+}
+
+#endif /* HAVE_WINDOW_SYSTEM */
+
+
+\f
+/***********************************************************************
+                              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->tool_bar_items.  Make sure we update both
+                 may access f->tab_bar_items.  Make sure we update both
                  variables within BLOCK_INPUT so no such event interrupts.  */
               BLOCK_INPUT;
-              f->tool_bar_items = new_tool_bar;
-              f->n_tool_bar_items = new_n_tool_bar;
+              f->tab_bar_items = new_tab_bar;
+              f->n_tab_bar_items = new_n_tab_bar;
               w->update_mode_line = Qt;
               UNBLOCK_INPUT;
             }
@@ -10182,68 +11129,71 @@ update_tool_bar (f, save_match_data)
 }
 
 
-/* Set F->desired_tool_bar_string to a Lisp string representing frame
-   F's desired tool-bar contents.  F->tool_bar_items must have
+/* 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_tool_bar_string (f)
+build_desired_tab_bar_string (f)
      struct frame *f;
 {
-  int i, size, size_needed;
+  int i /*, size, size_needed */;
   struct gcpro gcpro1, gcpro2, gcpro3;
-  Lisp_Object image, plist, props;
+  Lisp_Object image, plist, props, caption;
 
   image = plist = props = Qnil;
   GCPRO3 (image, plist, props);
 
-  /* Prepare F->desired_tool_bar_string.  If we can reuse it, do so.
+  /* 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_tool_bar_string)
-         ? SCHARS (f->desired_tool_bar_string)
-         : 0);
+  /* 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_tool_bar_items;
-
-  /* Reuse f->desired_tool_bar_string, if possible.  */
-  if (size < size_needed || NILP (f->desired_tool_bar_string))
-    f->desired_tool_bar_string = Fmake_string (make_number (size_needed),
-                                              make_number (' '));
-  else
-    {
-      props = list4 (Qdisplay, Qnil, Qmenu_item, Qnil);
-      Fremove_text_properties (make_number (0), make_number (size),
-                              props, f->desired_tool_bar_string);
-    }
+  /* 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 tool-bar items with a value that
-     is the index of the item in F's tool-bar item vector.  */
-  for (i = 0; i < f->n_tool_bar_items; ++i)
+     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->tool_bar_items, i * TOOL_BAR_ITEM_NSLOTS + (IDX))
+#define PROP(IDX) AREF (f->tab_bar_items, i * TAB_BAR_ITEM_NSLOTS + (IDX))
 
-      int enabled_p = !NILP (PROP (TOOL_BAR_ITEM_ENABLED_P));
-      int selected_p = !NILP (PROP (TOOL_BAR_ITEM_SELECTED_P));
+      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;
+      extern Lisp_Object QCrelief, QCmargin, QCconversion, QCascent, Qcenter;
 
       /* If image is a vector, choose the image according to the
         button state.  */
-      image = PROP (TOOL_BAR_ITEM_IMAGES);
+      image = PROP (TAB_BAR_ITEM_IMAGES);
       if (VECTORP (image))
        {
          if (enabled_p)
            idx = (selected_p
-                  ? TOOL_BAR_IMAGE_ENABLED_SELECTED
-                  : TOOL_BAR_IMAGE_ENABLED_DESELECTED);
+                  ? TAB_BAR_IMAGE_ENABLED_SELECTED
+                  : TAB_BAR_IMAGE_ENABLED_DESELECTED);
          else
            idx = (selected_p
-                  ? TOOL_BAR_IMAGE_DISABLED_SELECTED
-                  : TOOL_BAR_IMAGE_DISABLED_DESELECTED);
+                  ? TAB_BAR_IMAGE_DISABLED_SELECTED
+                  : TAB_BAR_IMAGE_DISABLED_DESELECTED);
 
          xassert (ASIZE (image) >= idx);
          image = AREF (image, idx);
@@ -10252,36 +11202,36 @@ build_desired_tool_bar_string (f)
        idx = -1;
 
       /* Ignore invalid image specifications.  */
-      if (!valid_image_p (image))
-       continue;
+      /* if (!valid_image_p (image)) */
+      /*       continue; */
 
-      /* Display the tool-bar button pressed, or depressed.  */
+      /* Display the tab-bar button pressed, or depressed.  */
       plist = Fcopy_sequence (XCDR (image));
 
       /* Compute margin and relief to draw.  */
-      relief = (tool_bar_button_relief >= 0
-               ? tool_bar_button_relief
-               : DEFAULT_TOOL_BAR_BUTTON_RELIEF);
+      relief = (tab_bar_button_relief >= 0
+               ? tab_bar_button_relief
+               : DEFAULT_TAB_BAR_BUTTON_RELIEF);
       hmargin = vmargin = relief;
 
-      if (INTEGERP (Vtool_bar_button_margin)
-         && XINT (Vtool_bar_button_margin) > 0)
+      if (INTEGERP (Vtab_bar_button_margin)
+         && XINT (Vtab_bar_button_margin) > 0)
        {
-         hmargin += XFASTINT (Vtool_bar_button_margin);
-         vmargin += XFASTINT (Vtool_bar_button_margin);
+         hmargin += XFASTINT (Vtab_bar_button_margin);
+         vmargin += XFASTINT (Vtab_bar_button_margin);
        }
-      else if (CONSP (Vtool_bar_button_margin))
+      else if (CONSP (Vtab_bar_button_margin))
        {
-         if (INTEGERP (XCAR (Vtool_bar_button_margin))
-             && XINT (XCAR (Vtool_bar_button_margin)) > 0)
-           hmargin += XFASTINT (XCAR (Vtool_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 (Vtool_bar_button_margin))
-             && XINT (XCDR (Vtool_bar_button_margin)) > 0)
-           vmargin += XFASTINT (XCDR (Vtool_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_tool_bar_buttons_p)
+      if (auto_raise_tab_bar_buttons_p)
        {
          /* Add a `:relief' property to the image spec if the item is
             selected.  */
@@ -10320,25 +11270,42 @@ build_desired_tool_bar_string (f)
         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, 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 tool-bar items
+        the start of this item's properties in the tab-bar items
         vector.  */
-      image = Fcons (Qimage, plist);
-      props = list4 (Qdisplay, image,
-                    Qmenu_item, make_number (i * TOOL_BAR_ITEM_NSLOTS));
+      props = list2 (Qmenu_item, make_number (i * TAB_BAR_ITEM_NSLOTS));
 
-      /* Let the last image hide all remaining spaces in the tool bar
-         string.  The string can be longer than needed when we reuse a
-         previous string.  */
-      if (i + 1 == f->n_tool_bar_items)
-       end = SCHARS (f->desired_tool_bar_string);
+      if (caption_p)
+       {
+         caption = concat3 (build_string (" "),
+                            Fcopy_sequence (PROP (TAB_BAR_ITEM_CAPTION)),
+                            build_string (" "));
+       }
       else
-       end = i + 1;
-      Fadd_text_properties (make_number (i), make_number (end),
-                           props, f->desired_tool_bar_string);
+       {
+         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
     }
 
@@ -10346,20 +11313,20 @@ build_desired_tool_bar_string (f)
 }
 
 
-/* Display one line of the tool-bar of frame IT->f.
+/* Display one line of the tab-bar of frame IT->f.
 
-   HEIGHT specifies the desired height of the tool-bar line.
+   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 tool-bar lines, so don't
-   count a final empty row in case the tool-bar width exactly matches
+   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_tool_bar_line (it, height)
+display_tab_bar_line (it, height)
      struct it *it;
      int height;
 {
@@ -10382,7 +11349,7 @@ display_tool_bar_line (it, height)
       /* Get the next display element.  */
       if (!get_next_display_element (it))
        {
-         /* Don't count empty row if we are counting needed tool-bar lines.  */
+         /* Don't count empty row if we are counting needed tab-bar lines.  */
          if (height < 0 && !it->hpos)
            return;
          break;
@@ -10407,8 +11374,8 @@ display_tool_bar_line (it, height)
              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
-                toolbar, so skip it.  But ensure there is at least one glyph,
-                so we don't accidentally disable the tool-bar.  */
+                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;
@@ -10431,13 +11398,13 @@ display_tool_bar_line (it, height)
 
   row->displays_text_p = row->used[TEXT_AREA] != 0;
 
-  /* Use default face for the border below the tool bar.
+  /* Use default face for the border below the tab bar.
 
-     FIXME: When auto-resize-tool-bars is grow-only, there is
-     no additional border below the possibly empty tool-bar lines.
+     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 tool-bar face for the border too.  */
-  if (!row->displays_text_p && !EQ (Vauto_resize_tool_bars, Qgrow_only))
+     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);
@@ -10457,7 +11424,7 @@ display_tool_bar_line (it, height)
 
   compute_line_metrics (it);
 
-  /* If line is empty, make it occupy the rest of the tool-bar.  */
+  /* 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;
@@ -10478,43 +11445,43 @@ display_tool_bar_line (it, height)
 }
 
 
-/* Max tool-bar height.  */
+/* Max tab-bar height.  */
 
-#define MAX_FRAME_TOOL_BAR_HEIGHT(f) \
+#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 tool-bar
+/* 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
-tool_bar_lines_needed (f, n_rows)
+tab_bar_lines_needed (f, n_rows)
      struct frame *f;
      int *n_rows;
 {
-  struct window *w = XWINDOW (f->tool_bar_window);
+  struct window *w = XWINDOW (f->tab_bar_window);
   struct it it;
-  /* tool_bar_lines_needed is called from redisplay_tool_bar after building
+  /* 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 tool-bar row.  */
+     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_tool_bar_string in the tool-bar window of frame F.  */
-  init_iterator (&it, w, -1, -1, temp_row, TOOL_BAR_FACE_ID);
+     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_tool_bar_string, 0, 0, 0, -1);
+  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_tool_bar_line (&it, -1);
+      display_tab_bar_line (&it, -1);
     }
   clear_glyph_row (temp_row);
 
-  /* f->n_tool_bar_rows == 0 means "unknown"; -1 means no tool-bar.  */
+  /* f->n_tab_bar_rows == 0 means "unknown"; -1 means no tab-bar.  */
   if (n_rows)
     *n_rows = it.vpos > 0 ? it.vpos : -1;
 
@@ -10522,9 +11489,9 @@ tool_bar_lines_needed (f, n_rows)
 }
 
 
-DEFUN ("tool-bar-lines-needed", Ftool_bar_lines_needed, Stool_bar_lines_needed,
+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 tool bar of FRAME.  */)
+       doc: /* Return the number of lines occupied by the tab bar of FRAME.  */)
      (frame)
      Lisp_Object frame;
 {
@@ -10538,15 +11505,15 @@ DEFUN ("tool-bar-lines-needed", Ftool_bar_lines_needed, Stool_bar_lines_needed,
     CHECK_FRAME (frame);
   f = XFRAME (frame);
 
-  if (WINDOWP (f->tool_bar_window)
-      || (w = XWINDOW (f->tool_bar_window),
+  if (WINDOWP (f->tab_bar_window)
+      || (w = XWINDOW (f->tab_bar_window),
          WINDOW_TOTAL_LINES (w) > 0))
     {
-      update_tool_bar (f, 1);
-      if (f->n_tool_bar_items)
+      update_tab_bar (f, 1);
+      if (f->n_tab_bar_items)
        {
-         build_desired_tool_bar_string (f);
-         nlines = tool_bar_lines_needed (f, NULL);
+         build_desired_tab_bar_string (f);
+         nlines = tab_bar_lines_needed (f, NULL);
        }
     }
 
@@ -10554,56 +11521,56 @@ DEFUN ("tool-bar-lines-needed", Ftool_bar_lines_needed, Stool_bar_lines_needed,
 }
 
 
-/* Display the tool-bar of frame F.  Value is non-zero if tool-bar's
+/* Display the tab-bar of frame F.  Value is non-zero if tab-bar's
    height should be changed.  */
 
 static int
-redisplay_tool_bar (f)
+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_TOOL_BAR (f))
-    update_frame_tool_bar (f);
-  return 0;
-#endif
+/* #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 tool-bar window or if it is zero-height, don't
-     do anything.  This means you must start with tool-bar-lines
+  /* 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 tool-bars by specifying tool-bar-lines zero.  */
-  if (!WINDOWP (f->tool_bar_window)
-      || (w = XWINDOW (f->tool_bar_window),
+     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 tool-bar window.  */
-  init_iterator (&it, w, -1, -1, w->desired_matrix->rows, TOOL_BAR_FACE_ID);
+  /* 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 tool-bar.  */
-  build_desired_tool_bar_string (f);
-  reseat_to_string (&it, NULL, f->desired_tool_bar_string, 0, 0, 0, -1);
+  /* 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_tool_bar_rows == 0)
+  if (f->n_tab_bar_rows == 0)
     {
       int nlines;
 
-      if ((nlines = tool_bar_lines_needed (f, &f->n_tool_bar_rows),
+      if ((nlines = tab_bar_lines_needed (f, &f->n_tab_bar_rows),
           nlines != WINDOW_TOTAL_LINES (w)))
        {
-         extern Lisp_Object Qtool_bar_lines;
+         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 (Qtool_bar_lines,
+                                   Fcons (Fcons (Qtab_bar_lines,
                                                  make_number (nlines)),
                                           Qnil));
          if (WINDOW_TOTAL_LINES (w) != old_height)
@@ -10615,24 +11582,24 @@ redisplay_tool_bar (f)
        }
     }
 
-  /* Display as many lines as needed to display all tool-bar items.  */
+  /* Display as many lines as needed to display all tab-bar items.  */
 
-  if (f->n_tool_bar_rows > 0)
+  if (f->n_tab_bar_rows > 0)
     {
       int border, rows, height, extra;
 
-      if (INTEGERP (Vtool_bar_border))
-       border = XINT (Vtool_bar_border);
-      else if (EQ (Vtool_bar_border, Qinternal_border_width))
+      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 (Vtool_bar_border, Qborder_width))
+      else if (EQ (Vtab_bar_border, Qborder_width))
        border = f->border_width;
       else
        border = 0;
       if (border < 0)
        border = 0;
 
-      rows = f->n_tool_bar_rows;
+      rows = f->n_tab_bar_rows;
       height = max (1, (it.last_visible_y - border) / rows);
       extra = it.last_visible_y - border - height * rows;
 
@@ -10644,74 +11611,74 @@ redisplay_tool_bar (f)
              h = (extra + rows - 1) / rows;
              extra -= h;
            }
-         display_tool_bar_line (&it, height + h);
+         display_tab_bar_line (&it, height + h);
        }
     }
   else
     {
       while (it.current_y < it.last_visible_y)
-       display_tool_bar_line (&it, 0);
+       display_tab_bar_line (&it, 0);
     }
 
-  /* It doesn't make much sense to try scrolling in the tool-bar
+  /* 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_tool_bars))
+  if (!NILP (Vauto_resize_tab_bars))
     {
-      int max_tool_bar_height = MAX_FRAME_TOOL_BAR_HEIGHT (f);
+      int max_tab_bar_height = MAX_FRAME_TAB_BAR_HEIGHT (f);
       int change_height_p = 0;
 
-      /* If we couldn't display everything, change the tool-bar's
+      /* 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_tool_bar_height)
+         && 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 tool-bar's height.  */
+        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 tool-bar items, but is partially visible,
-        change the tool-bar's height.  */
+      /* 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_tool_bar_height)
+         && MATRIX_ROW_BOTTOM_Y (row) < max_tab_bar_height)
        change_height_p = 1;
 
-      /* Resize windows as needed by changing the `tool-bar-lines'
+      /* Resize windows as needed by changing the `tab-bar-lines'
         frame parameter.  */
       if (change_height_p)
        {
-         extern Lisp_Object Qtool_bar_lines;
+         extern Lisp_Object Qtab_bar_lines;
          Lisp_Object frame;
          int old_height = WINDOW_TOTAL_LINES (w);
          int nrows;
-         int nlines = tool_bar_lines_needed (f, &nrows);
+         int nlines = tab_bar_lines_needed (f, &nrows);
 
-         change_height_p = ((EQ (Vauto_resize_tool_bars, Qgrow_only)
-                             && !f->minimize_tool_bar_window_p)
+         change_height_p = ((EQ (Vauto_resize_tab_bars, Qgrow_only)
+                             && !f->minimize_tab_bar_window_p)
                             ? (nlines > old_height)
                             : (nlines != old_height));
-         f->minimize_tool_bar_window_p = 0;
+         f->minimize_tab_bar_window_p = 0;
 
          if (change_height_p)
            {
              XSETFRAME (frame, f);
              Fmodify_frame_parameters (frame,
-                                       Fcons (Fcons (Qtool_bar_lines,
+                                       Fcons (Fcons (Qtab_bar_lines,
                                                      make_number (nlines)),
                                               Qnil));
              if (WINDOW_TOTAL_LINES (w) != old_height)
                {
                  clear_glyph_matrix (w->desired_matrix);
-                 f->n_tool_bar_rows = nrows;
+                 f->n_tab_bar_rows = nrows;
                  fonts_changed_p = 1;
                  return 1;
                }
@@ -10719,18 +11686,18 @@ redisplay_tool_bar (f)
        }
     }
 
-  f->minimize_tool_bar_window_p = 0;
+  f->minimize_tab_bar_window_p = 0;
   return 0;
 }
 
 
-/* Get information about the tool-bar item which is displayed in GLYPH
-   on frame F.  Return in *PROP_IDX the index where tool-bar item
-   properties start in F->tool_bar_items.  Value is zero if
-   GLYPH doesn't display a tool-bar item.  */
+/* 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
-tool_bar_item_info (f, glyph, prop_idx)
+tab_bar_item_info (f, glyph, prop_idx)
      struct frame *f;
      struct glyph *glyph;
      int *prop_idx;
@@ -10742,14 +11709,14 @@ tool_bar_item_info (f, glyph, prop_idx)
   /* 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_tool_bar_string), glyph->charpos);
+  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->tool_bar_items.  */
+     F->tab_bar_items.  */
   prop = Fget_text_property (make_number (charpos),
-                            Qmenu_item, f->current_tool_bar_string);
+                            Qmenu_item, f->current_tab_bar_string);
   if (INTEGERP (prop))
     {
       *prop_idx = XINT (prop);
@@ -10762,25 +11729,25 @@ tool_bar_item_info (f, glyph, prop_idx)
 }
 
 \f
-/* Get information about the tool-bar item at position X/Y on frame F.
-   Return in *GLYPH a pointer to the glyph of the tool-bar item in
-   the current matrix of the tool-bar window of F, or NULL if not
-   on a tool-bar item.  Return in *PROP_IDX the index of the tool-bar
-   item in F->tool_bar_items.  Value is
+/* 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 tool-bar item
+   -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_tool_bar_item (f, x, y, glyph, hpos, vpos, prop_idx)
+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->tool_bar_window);
+  struct window *w = XWINDOW (f->tab_bar_window);
   int area;
 
   /* Find the glyph under X/Y.  */
@@ -10788,13 +11755,13 @@ get_tool_bar_item (f, x, y, glyph, hpos, vpos, prop_idx)
   if (*glyph == NULL)
     return -1;
 
-  /* Get the start of this tool-bar item's properties in
-     f->tool_bar_items.  */
-  if (!tool_bar_item_info (f, *glyph, prop_idx))
+  /* 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->tool_bar_window, dpyinfo->mouse_face_window)
+  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
@@ -10809,30 +11776,30 @@ get_tool_bar_item (f, x, y, glyph, hpos, vpos, prop_idx)
 
 
 /* EXPORT:
-   Handle mouse button event on the tool-bar of frame F, at
+   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_tool_bar_click (f, x, y, down_p, modifiers)
+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->tool_bar_window);
+  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 tool-bar item, return.  */
+  /* If not on the highlighted tab-bar item, return.  */
   frame_to_window_pixel_xy (w, &x, &y);
-  if (get_tool_bar_item (f, x, y, &glyph, &hpos, &vpos, &prop_idx) != 0)
+  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->tool_bar_items, prop_idx + TOOL_BAR_ITEM_ENABLED_P);
+  enabled_p = AREF (f->tab_bar_items, prop_idx + TAB_BAR_ITEM_ENABLED_P);
   if (NILP (enabled_p))
     return;
 
@@ -10841,7 +11808,7 @@ handle_tool_bar_click (f, x, y, down_p, modifiers)
       /* Show item in pressed state.  */
       show_mouse_face (dpyinfo, DRAW_IMAGE_SUNKEN);
       dpyinfo->mouse_face_image_state = DRAW_IMAGE_SUNKEN;
-      last_tool_bar_item = prop_idx;
+      last_tab_bar_item = prop_idx;
     }
   else
     {
@@ -10853,34 +11820,34 @@ handle_tool_bar_click (f, x, y, down_p, modifiers)
       show_mouse_face (dpyinfo, DRAW_IMAGE_RAISED);
       dpyinfo->mouse_face_image_state = DRAW_IMAGE_RAISED;
 
-      key = AREF (f->tool_bar_items, prop_idx + TOOL_BAR_ITEM_KEY);
+      key = AREF (f->tab_bar_items, prop_idx + TAB_BAR_ITEM_KEY);
 
       XSETFRAME (frame, f);
-      event.kind = TOOL_BAR_EVENT;
+      event.kind = TAB_BAR_EVENT;
       event.frame_or_window = frame;
       event.arg = frame;
       kbd_buffer_store_event (&event);
 
-      event.kind = TOOL_BAR_EVENT;
+      event.kind = TAB_BAR_EVENT;
       event.frame_or_window = frame;
       event.arg = key;
       event.modifiers = modifiers;
       kbd_buffer_store_event (&event);
-      last_tool_bar_item = -1;
+      last_tab_bar_item = -1;
     }
 }
 
 
-/* Possibly highlight a tool-bar item on frame F when mouse moves to
-   tool-bar window-relative coordinates X/Y.  Called from
+/* 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_tool_bar_highlight (f, x, y)
+note_tab_bar_highlight (f, x, y)
      struct frame *f;
      int x, y;
 {
-  Lisp_Object window = f->tool_bar_window;
+  Lisp_Object window = f->tab_bar_window;
   struct window *w = XWINDOW (window);
   Display_Info *dpyinfo = FRAME_X_DISPLAY_INFO (f);
   int hpos, vpos;
@@ -10900,32 +11867,32 @@ note_tool_bar_highlight (f, x, y)
       return;
     }
 
-  rc = get_tool_bar_item (f, x, y, &glyph, &hpos, &vpos, &prop_idx);
+  rc = get_tab_bar_item (f, x, y, &glyph, &hpos, &vpos, &prop_idx);
   if (rc < 0)
     {
-      /* Not on tool-bar item.  */
+      /* Not on tab-bar item.  */
       clear_mouse_face (dpyinfo);
       return;
     }
   else if (rc == 0)
-    /* On same tool-bar item as before.  */
+    /* On same tab-bar item as before.  */
     goto set_help_echo;
 
   clear_mouse_face (dpyinfo);
 
-  /* Mouse is down, but on different tool-bar item?  */
+  /* 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_tool_bar_item != prop_idx)
+      && 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 tool-bar item is not enabled, don't highlight it.  */
-  enabled_p = AREF (f->tool_bar_items, prop_idx + TOOL_BAR_ITEM_ENABLED_P);
+  /* 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
@@ -10946,7 +11913,7 @@ note_tool_bar_highlight (f, x, y)
       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 = TOOL_BAR_FACE_ID;
+      dpyinfo->mouse_face_face_id = TAB_BAR_FACE_ID;
 
       /* Display it as active.  */
       show_mouse_face (dpyinfo, draw);
@@ -10955,13 +11922,13 @@ note_tool_bar_highlight (f, x, y)
 
  set_help_echo:
 
-  /* Set help_echo_string to a help string to display for this tool-bar item.
+  /* 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->tool_bar_items, prop_idx + TOOL_BAR_ITEM_HELP);
+  help_echo_string = AREF (f->tab_bar_items, prop_idx + TAB_BAR_ITEM_HELP);
   if (NILP (help_echo_string))
-    help_echo_string = AREF (f->tool_bar_items, prop_idx + TOOL_BAR_ITEM_CAPTION);
+    help_echo_string = AREF (f->tab_bar_items, prop_idx + TAB_BAR_ITEM_CAPTION);
 }
 
 #endif /* HAVE_WINDOW_SYSTEM */
@@ -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
index 6bde1c121d2c3942094359e91fc4bbfbc15572ad..01c610be9879c134fa9ba932e2713e99dc6b97b1 100644 (file)
@@ -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");
index b70f20fe6441395e1f01e573aeb8425656432993..3ac290d13eaaf8272b0e2869f6eada43f64d9ca9 100644 (file)
@@ -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,
index de2f4eb681509854fddc5bb4b7daf3859ae5074c..35a6b23caa0f53efc03c4f439e2f246dd07cc908 100644 (file)
@@ -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.  */
index 29ed5bb865c77352a281af65098062334a8e50fa..50fbd6ce36df0244ab4cdd5601f7c27aedf1ee0c 100644 (file)
@@ -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
index a766f863c4dd5dde90483d9c8a3dc8d2747b5805..79fe2e16b12f8d63ba479b5c106093d86c0bc003 100644 (file)
@@ -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 */