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