1;;; tabbar.el --- Display a tab bar in the header line
3;; Copyright (C) 2003 David Ponce
5;; Author: David Ponce <david@dponce.com>
6;; Maintainer: David Ponce <david@dponce.com>
7;; Created: 25 February 2003
8;; Keywords: convenience
9;; Revision: $Id: tabbar.el,v 1.20 2003/06/05 08:15:49 ponced Exp $
11(defconst tabbar-version "1.3")
13;; This file is not part of GNU Emacs.
15;; This program is free software; you can redistribute it and/or
16;; modify it under the terms of the GNU General Public License as
17;; published by the Free Software Foundation; either version 2, or (at
18;; your option) any later version.
20;; This program is distributed in the hope that it will be useful, but
21;; WITHOUT ANY WARRANTY; without even the implied warranty of
22;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23;; General Public License for more details.
25;; You should have received a copy of the GNU General Public License
26;; along with this program; see the file COPYING. If not, write to
27;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
28;; Boston, MA 02111-1307, USA.
32;; This library provides a minor mode to display tabs in the header
33;; line. It works only on GNU Emacs 21.
35;; M-x `tabbar-mode' toggle the display of the tab bar, globally.
37;; M-x `tabbar-local-mode' toggle the display of the tab bar, locally
38;; in the current buffer, when the global mode in on. This mode
39;; permit to see the tab bar in a buffer where the header line is
40;; already used by another mode (like `info' buffers). That command
41;; is particularly useful when it is given a keyboard shortcut, like
44;; (global-set-key [(control f10)] 'tabbar-local-mode)
46;; It is possible to navigate through tabs using commands (that is,
47;; using the keyboard). The main commands to cycle through tabs are:
49;; - `tabbar-forward' select the next available tab.
50;; - `tabbar-backward' select the previous available tab.
52;; It is worth defining keys for them. For example:
54;; (global-set-key [(control shift tab)] 'tabbar-backward)
55;; (global-set-key [(control tab)] 'tabbar-forward)
57;; The default cycle is to first try to select the tab just
58;; after/before the selected tab. If this is the last/first tab, then
59;; the first/last tab of the next/previous group of tabs is selected.
60;; That behavior is controlled by the `tabbar-cycling-scope' option.
62;; The following specialized commands can be useful too:
64;; - `tabbar-forward-tab'/`tabbar-backward-tab'
65;; Navigate through visible tabs only.
67;; - `tabbar-forward-group'/`tabbar-backward-group'
68;; Navigate through tab groups only.
73;; The content of the tab bar is represented by an internal data
74;; structure: a tab set. A tab set is a collection of tabs,
75;; identified by an unique name. In a tab set, at any time, one and
76;; only one tab is designated as selected within the tab set.
78;; A tab is a simple data structure giving: the value of the tab, and
79;; a reference to its tab set container. A tab value can be any Lisp
80;; object, even if the most common value is probably a string. Each
81;; tab object is guaranteed to be unique.
83;; A tab set is displayed on the tab bar through a "view" defined by
84;; the index of the leftmost tab shown. Thus, it is possible to
85;; scroll the tab bar horizontally, by changing the start index of the
88;; The visual representation of a tab set is a list a
89;; `header-line-format' template elements. Each template element is
90;; the visual representation of a tab. When the visual representation
91;; of a tab is required, the function specified in the variable
92;; `tabbar-tab-label-function' is called to obtain a label (a text
93;; representation) for that tab. Also, the function specified in the
94;; variable `tabbar-help-on-tab-function' is called when the mouse is
95;; on a tab. That function is passed the tab and can return a help
96;; string to display. Finally, when a tab is selected by clicking on
97;; it, the function specified in the variable
98;; `tabbar-select-tab-function' is called with the mouse event
99;; received, and the tab.
101;; To increase performance, the tab set automatically maintains its
102;; visual representation in a cache. As far as possible, that cache
103;; is used to display the tab set, and refreshed only when necessary.
105;; Several tab sets can be maintained at the same time. Only one is
106;; displayed on the tab bar, it is obtained by calling the function
107;; specified in the variable `tabbar-current-tabset-function'.
109;; A special tab set is maintained, that contains the list of
110;; currently selected tabs, in existing tab sets. For example, a such
111;; tab set can be used to display a tab bar with a tab for each
112;; created tab set, allowing to switch to another tab set by clicking
113;; on the corresponding tab.
115;; Three buttons are displayed to the left, on the tab bar: the "home"
116;; button, the "scroll left" and the "scroll right" buttons. The
117;; "home" button is a general purpose button used to change something
118;; on the tab bar. The scroll left and scroll right buttons are used
119;; to scroll tabs horizontally. The following variables are
120;; available, for respectively the `home', `scroll-left' and
121;; `scroll-right' value of `<button>':
123;; `tabbar-<button>-function'
124;; Specify a function called when clicking on the button. The
125;; function is passed the mouse event received.
127;; `tabbar-<button>-help-function'
128;; Specify a function to obtain a help string displayed when the
129;; mouse is onto the button. The function is called with no
132;; The appearance of tabs and buttons is also customizable (see the
133;; code for more details).
138;; The default tab bar implementation provided, displays buffers in
139;; dedicated tabs. Selecting a tab, switch (mouse-1), or pop
140;; (mouse-2), to the buffer it contains.
142;; The list of buffers put in tabs is provided by the function
143;; specified in the variable `tabbar-buffer-list-function'. The
144;; default function: `tabbar-buffer-list', excludes buffers whose name
145;; starts with a space, when they are not visiting a file.
147;; Buffers are organized in groups, each one represented by a tab set.
148;; A buffer can have no group, or belong to more than one group. The
149;; function specified by the variable `tabbar-buffer-groups-function'
150;; is called for each buffer to obtain its groups. The default
151;; function provided: `tabbar-buffer-groups' organizes buffers
152;; depending on their major mode (see that function for details).
154;; The "home" button toggles display of buffer groups on the tab bar,
155;; allowing to easily choose another buffer group by clicking on its
158;; The scroll buttons permit to scroll tabs when some of them are
159;; outside the tab bar visible area.
169 "Display a tab bar in the header line."
172(defcustom tabbar-cycling-scope nil
173 "*Specify the scope of cyclic navigation through tabs.
174The following scopes are possible:
177 Navigate through visible tabs only.
179 Navigate through tab groups only.
181 Navigate through visible tabs, then through tab groups."
183 :type '(choice :tag "Cycle through..."
184 (const :tag "Visible Tabs Only" tabs)
185 (const :tag "Tab Groups Only" groups)
186 (const :tag "Visible Tabs then Tab Groups" nil)))
188(defcustom tabbar-inhibit-functions
189 '(tabbar-default-inhibit-function)
190 "List of functions to be called before displaying the tab bar.
191Those functions are called one by one, with no arguments, until one of
192them returns a non-nil value, and thus, prevent to display the tab
197(defcustom tabbar-current-tabset-function
199 "Function called with no argument to obtain the current tab set.
200This is the tab set displayed on the tab bar."
204(defcustom tabbar-tab-label-function
205 'tabbar-buffer-tab-label
206 "Function that obtains a tab label displayed on the tab bar.
207The function is passed a tab and should return a string."
211(defcustom tabbar-select-tab-function
212 'tabbar-buffer-select-tab
213 "Function that select a tab.
214The function is passed a mouse event and a tab, and should make it the
219(defcustom tabbar-help-on-tab-function
220 'tabbar-buffer-help-on-tab
221 "Function to obtain a help string for a tab.
222The help string is displayed when the mouse is onto the button. The
223function is passed the tab and should return a help string or nil for
228(defcustom tabbar-home-function
229 'tabbar-buffer-toggle-group-mode
230 "Function called when clicking on the tab bar home button.
231The function is passed the mouse event received."
235(defcustom tabbar-home-help-function
236 'tabbar-buffer-toggle-group-mode-help
237 "Function to obtain a help string for the tab bar home button.
238The help string is displayed when the mouse is onto the button.
239The function is called with no arguments."
243(defcustom tabbar-scroll-left-function
245 "Function that scrolls tabs on left.
246The function is passed the mouse event received when clicking on the
247scroll left button. It should scroll the current tab set."
251(defcustom tabbar-scroll-left-help-function
252 'tabbar-scroll-left-help
253 "Function to obtain a help string for the scroll left button.
254The help string is displayed when the mouse is onto the button.
255The function is called with no arguments."
259(defcustom tabbar-scroll-right-function
261 "Function that scrolls tabs on right.
262The function is passed the mouse event received when clicking on the
263scroll right button. It should scroll the current tab set."
267(defcustom tabbar-scroll-right-help-function
268 'tabbar-scroll-right-help
269 "Function to obtain a help string for the scroll right button.
270The help string is displayed when the mouse is onto the button.
271The function is called with no arguments."
277(defconst tabbar-tabsets-tabset-name "tabbar-tabsets-tabset"
278 "Name of the special tab set of existing tab sets.")
280(defsubst tabbar-make-tab (object tabset)
281 "Return a new tab with value OBJECT.
282TABSET is the tab set the tab belongs to."
283 (cons object tabset))
285(defsubst tabbar-tab-value (tab)
286 "Return the value of tab TAB."
289(defsubst tabbar-tab-tabset (tab)
290 "Return the tab set TAB belongs to."
293(defvar tabbar-tabsets nil
294 "The tab sets store.")
296(defvar tabbar-current-tabset nil
297 "The tab set currently displayed on the tab bar.")
298(make-variable-buffer-local 'tabbar-current-tabset)
300(defvar tabbar-last-selected-tab nil
301 "The last selected tab.")
303(defsubst tabbar-free-tabsets-store ()
304 "Free the tab set store."
305 (setq tabbar-tabsets nil
306 tabbar-current-tabset nil
307 tabbar-last-selected-tab nil))
309(defsubst tabbar-init-tabsets-store ()
310 "Initialize the tab set store."
311 (tabbar-free-tabsets-store)
312 (setq tabbar-tabsets (make-vector 31 0)))
314(defmacro tabbar-map-tabsets (function)
315 "Apply FUNCTION to each existing tab set.
316Return the list of the results."
317 (let ((result (make-symbol "result"))
318 (tabset (make-symbol "tabset")))
320 (mapatoms #'(lambda (,tabset)
322 (cons (funcall ,function ,tabset)
325 (nreverse ,result))))
327(defun tabbar-make-tabset (name &rest objects)
328 "Make a new tab set whose name is the string NAME.
329It is initialized with tabs build from the list of OBJECTS."
330 (let* ((tabset (intern name tabbar-tabsets))
331 (tabs (mapcar #'(lambda (object)
332 (tabbar-make-tab object tabset))
335 (put tabset 'select (car tabs))
336 (put tabset 'start 0)
339(defsubst tabbar-get-tabset (name)
340 "Return the tab set whose name is the string NAME.
341Return nil if not found."
342 (intern-soft name tabbar-tabsets))
344(defsubst tabbar-delete-tabset (tabset)
345 "Delete the tab set TABSET.
346That is, remove it from the tab sets store."
347 (unintern tabset tabbar-tabsets))
349(defsubst tabbar-tabs (tabset)
350 "Return the list of tabs in TABSET."
351 (symbol-value tabset))
353(defsubst tabbar-tab-values (tabset)
354 "Return the list of tab values in TABSET."
355 (mapcar 'tabbar-tab-value (tabbar-tabs tabset)))
357(defsubst tabbar-get-tab (object tabset)
358 "Search for a tab with value OBJECT in TABSET.
359Return the tab found, or nil if not found."
360 (assoc object (tabbar-tabs tabset)))
362(defsubst tabbar-member (tab tabset)
363 "Return non-nil if TAB is in TABSET."
364 (or (eq (tabbar-tab-tabset tab) tabset)
365 (memq tab (tabbar-tabs tabset))))
367(defsubst tabbar-template (tabset)
368 "Return the template to display TABSET in the header line."
369 (get tabset 'template))
371(defsubst tabbar-set-template (tabset template)
372 "Set the TABSET's header line format with TEMPLATE."
373 (put tabset 'template template))
375(defsubst tabbar-selected-tab (tabset)
376 "Return the tab selected in TABSET."
377 (get tabset 'select))
379(defsubst tabbar-selected-value (tabset)
380 "Return the value of the tab selected in TABSET."
381 (tabbar-tab-value (tabbar-selected-tab tabset)))
383(defsubst tabbar-selected-p (tab tabset)
384 "Return non-nil if TAB is the selected tab in TABSET."
385 (eq tab (tabbar-selected-tab tabset)))
387(defsubst tabbar-select-tab (tab tabset)
388 "Make TAB the selected tab in TABSET.
389Does nothing if TAB is not found in TABSET.
390Return TAB if selected, nil if not."
391 (when (tabbar-member tab tabset)
392 (or (tabbar-selected-p tab tabset)
393 (tabbar-set-template tabset nil))
394 (put tabset 'select tab)))
396(defsubst tabbar-select-tab-value (object tabset)
397 "Make the tab with value OBJECT, the selected tab in TABSET.
398Does nothing if a tab with value OBJECT is not found in TABSET.
399Return the tab selected, or nil if nothing was selected."
400 (tabbar-select-tab (tabbar-get-tab object tabset) tabset))
402(defsubst tabbar-start (tabset)
403 "Return the index of the first tab in the TABSET's view."
406(defsubst tabbar-view (tabset)
407 "Return the list of tabs in the TABSET's view."
408 (nthcdr (tabbar-start tabset) (tabbar-tabs tabset)))
410(defun tabbar-add-tab (tabset object &optional append)
411 "Add to TABSET a tab with value OBJECT if there isn't one there yet.
412If the tab is added, it is added at the beginning of the tab list,
413unless the optional argument APPEND is non-nil, in which case it is
415 (let ((tabs (tabbar-tabs tabset)))
416 (if (tabbar-get-tab object tabset)
418 (let ((tab (tabbar-make-tab object tabset)))
419 (tabbar-set-template tabset nil)
420 (set tabset (if append
421 (append tabs (list tab))
422 (cons tab tabs)))))))
424(defun tabbar-delete-tab (tab)
425 "Remove TAB from its TABSET."
426 (let* ((tabset (tabbar-tab-tabset tab))
427 (tabs (tabbar-tabs tabset)))
428 (tabbar-set-template tabset nil)
429 (when (eq tab (tabbar-selected-tab tabset))
430 (tabbar-select-tab (car (or (cdr (memq tab tabs)) (last tabs)))
432 (set tabset (delq tab tabs))))
434(defun tabbar-scroll (tabset count)
435 "Scroll the TABSET's view of COUNT tabs.
436If COUNT is positive move the view on right. If COUNT is negative,
437move the view on left."
438 (let ((start (min (max 0 (+ (tabbar-start tabset) count))
439 (1- (length (tabbar-tabs tabset))))))
440 (when (/= start (tabbar-start tabset))
441 (tabbar-set-template tabset nil)
442 (put tabset 'start start))))
444(defun tabbar-tab-next (tabset tab &optional before)
445 "Search in TABSET for the tab after TAB.
446If optional argument BEFORE is non-nil, search for the tab before
447TAB. Return the tab found, or nil otherwise."
448 (let* (last (tabs (tabbar-tabs tabset)))
449 (while (and tabs (not (eq tab (car tabs))))
450 (setq last (car tabs)
452 (and tabs (if before last (nth 1 tabs)))))
454(defun tabbar-current-tabset (&optional update)
455 "Return the current tab set, that will be displayed on the tab bar.
456If optional argument UPDATE is non-nil, call the user defined function
457`tabbar-current-tabset-function' to obtain it. Otherwise return the
459 (when (and update tabbar-current-tabset-function)
460 (setq tabbar-current-tabset
461 (funcall tabbar-current-tabset-function))
462 (or tabbar-last-selected-tab
463 (setq tabbar-last-selected-tab
464 (tabbar-selected-tab tabbar-current-tabset))))
465 tabbar-current-tabset)
467(defun tabbar-get-tabsets-tabset ()
468 "Return the tab set of selected tabs in existing tab sets."
469 (let ((tabsets-tabset
470 (or (tabbar-get-tabset tabbar-tabsets-tabset-name)
471 (tabbar-make-tabset tabbar-tabsets-tabset-name))))
476 (or (eq tabset tabsets-tabset)
477 (tabbar-selected-tab tabset))))))
478 (tabbar-scroll tabsets-tabset 0)
479 (tabbar-set-template tabsets-tabset nil)
482;;; Buttons and separators
484(defun tabbar-find-image (specs)
485 "Find an image, choosing one of a list of image specifications.
486SPECS is a list of image specifications. See also `find-image'."
487 (when (display-images-p)
492(defconst tabbar-separator-widget
496 (restricted-sexp :tag "Spec"
497 :match-alternatives (listp))))
498 "Widget for editing a tab bar separator.
499A separator is specified as a pair (STRING . IMAGE) where STRING is a
500string value, and IMAGE a list of image specifications.
501If IMAGE is non-nil, try to use that image, else use STRING.
502The value (\"\") hide separators.")
504(defun tabbar-setup-separator (variable value)
505 "Set VARIABLE with specification of tab separator in VALUE.
506Initialize `VARIABLE-value' with the template element to use in header
507line, to display a separator on the tab bar."
508 (let ((text (intern (format "%s-value" variable)))
509 (image (tabbar-find-image (cdr value))))
510 (set text (propertize (if image " " (car value))
511 'face 'tabbar-separator-face
513 (custom-set-default variable value)
516(defvar tabbar-separator-value nil
517 "Text of the separator used between tabs.")
519(defcustom tabbar-separator (list " ")
520 "Separator used between tabs.
521See the variable `tabbar-separator-widget' for details."
523 :type tabbar-separator-widget
524 :set 'tabbar-setup-separator)
526(defconst tabbar-button-widget
532 (restricted-sexp :tag "Spec"
533 :match-alternatives (listp))))
534 (cons :tag "Disabled"
538 (restricted-sexp :tag "Spec"
539 :match-alternatives (listp))))
541 "Widget for editing a tab bar button.
542A button is specified as a pair (ENABLED-BUTTON . DISABLED-BUTTON),
543where ENABLED-BUTTON and DISABLED-BUTTON specify the value used when
544the button is respectively enabled and disabled. Each button value is
545a pair (STRING . IMAGE) where STRING is a string value, and IMAGE a
546list of image specifications.
547If IMAGE is non-nil, try to use that image, else use STRING.")
549(defun tabbar-setup-button (variable value)
550 "Set VARIABLE with the button specification in VALUE.
551Initialize `VARIABLE-enable' and `VARIABLE-disable' with the template
552elements to use in the header line, to respectively display an enabled
553and a disabled button on the tab bar.
554The variable `VARIABLE-keymap' must be set with the keymap used for the
556The function `VARIABLE-help' must be defined to return the `help-echo'
557string shown when the mouse is on the button."
558 (let ((enabled (intern (format "%s-enabled" variable)))
559 (disabled (intern (format "%s-disabled" variable)))
560 (keymap (intern (format "%s-keymap" variable)))
561 (help (intern (format "%s-help" variable)))
562 (image-en (tabbar-find-image (cdar value)))
563 (image-di (tabbar-find-image (cddr value))))
564 (set enabled (propertize (if image-en " " (caar value))
566 'face 'tabbar-button-face
567 'local-map (symbol-value keymap)
569 (set disabled (propertize (if image-di " " (cadr value))
571 'face 'tabbar-button-face))
572 (custom-set-default variable value)
575(defun tabbar-make-button-keymap (callback)
576 "Return a button keymap that call CALLBACK on mouse events.
577CALLBACK is passed the received mouse event."
578 (let ((keymap (make-sparse-keymap)))
579 ;; Pass mouse-1, mouse-2 and mouse-3 events to CALLBACK.
580 (define-key keymap [header-line down-mouse-1] 'ignore)
581 (define-key keymap [header-line mouse-1] callback)
582 (define-key keymap [header-line down-mouse-2] 'ignore)
583 (define-key keymap [header-line mouse-2] callback)
584 (define-key keymap [header-line down-mouse-3] 'ignore)
585 (define-key keymap [header-line mouse-3] callback)
588(defvar tabbar-home-button-enabled nil
589 "Text of the enabled home button.")
591(defvar tabbar-home-button-disabled nil
592 "Text of the disabled home button.")
594(defconst tabbar-home-button-keymap
595 (tabbar-make-button-keymap 'tabbar-home-button-callback)
596 "Keymap of the home button.")
598(defun tabbar-home-button-callback (event)
599 "Handle a mouse EVENT on the home button.
600Call `tabbar-home-function'."
602 (when tabbar-home-function
603 (save-selected-window
604 (select-window (posn-window (event-start event)))
605 (funcall tabbar-home-function event)
606 (force-mode-line-update)
610(defun tabbar-home-button-help (window object position)
611 "Return a help string or nil for none, for the home button.
612Call `tabbar-home-help-function'.
613Arguments WINDOW, OBJECT and POSITION, are not used."
614 (when tabbar-home-help-function
615 (funcall tabbar-home-help-function)))
617(defconst tabbar-home-button-enabled-image
618 '((:type pbm :ascent center :data "\
622184 184 184 184 0 184 184 184 184 184 184 184 184 0 0 0 184 184 184 184
623184 184 0 0 0 0 0 184 184 184 184 0 0 0 0 0 0 0 184 184 184 184 255 0 0
6240 255 255 255 184 184 0 0 0 0 0 0 0 184 184 184 184 0 0 0 0 0 255 255 184
625184 184 184 0 0 0 255 255 184 184 184 184 184 184 0 255 255 184 184 184
626184 184 184 184 184 255 184 184 184 184
628 "Default image for the enabled home button.")
630(defconst tabbar-home-button-disabled-image
631 '((:type pbm :ascent center :data "\
635184 184 184 184 120 184 184 184 184 184 184 184 184 120 120 120 184 184
636184 184 184 184 120 184 184 184 120 184 184 184 184 120 120 160 184 160
637120 120 184 184 184 184 255 120 184 120 255 255 255 184 184 120 120 160
638184 160 120 120 184 184 184 184 120 184 184 184 120 255 255 184 184 184
639184 120 120 120 255 255 184 184 184 184 184 184 120 255 255 184 184 184
640184 184 184 184 184 255 184 184 184 184
642 "Default image for the disabled home button.")
644(defcustom tabbar-home-button
645 (cons (cons "[o]" tabbar-home-button-enabled-image)
646 (cons "[x]" tabbar-home-button-disabled-image))
648See the variable `tabbar-button-widget' for details."
650 :type tabbar-button-widget
651 :set 'tabbar-setup-button)
653(defvar tabbar-scroll-left-button-enabled nil
654 "Text of the enabled scroll left button.")
656(defvar tabbar-scroll-left-button-disabled nil
657 "Text of the disabled scroll left button.")
659(defconst tabbar-scroll-left-button-keymap
660 (tabbar-make-button-keymap 'tabbar-scroll-left-button-callback)
661 "Keymap of the scroll left button.")
663(defun tabbar-scroll-left-button-callback (event)
664 "Handle a mouse EVENT on the scroll left button.
665Call `tabbar-scroll-left-function'."
667 (when tabbar-scroll-left-function
668 (save-selected-window
669 (select-window (posn-window (event-start event)))
670 (funcall tabbar-scroll-left-function event)
671 (force-mode-line-update)
675(defun tabbar-scroll-left-button-help (window object position)
676 "Return a help string or nil for none, for the scroll left button.
677Call `tabbar-scroll-left-help-function'.
678Arguments WINDOW, OBJECT and POSITION, are not used."
679 (when tabbar-scroll-left-help-function
680 (funcall tabbar-scroll-left-help-function)))
682(defconst tabbar-scroll-left-button-enabled-image
683 '((:type pbm :ascent center :data "\
687184 184 184 184 184 184 184 184 184 184 184 184 184 0 184 184 184 184 184
688184 0 0 255 184 184 184 184 0 0 0 255 184 184 184 0 0 0 0 255 184 184 184
689184 0 0 0 255 184 184 184 184 184 0 0 255 184 184 184 184 184 184 0 255
690184 184 184 184 184 184 184 255 184 184 184 184 184 184 184 184 184
692 "Default image for the enabled scroll left button.")
694(defconst tabbar-scroll-left-button-disabled-image
695 '((:type pbm :ascent center :data "\
699184 184 184 184 184 184 184 184 184 184 184 184 184 120 184 184 184 184
700184 184 120 120 255 184 184 184 184 120 184 120 255 184 184 184 120 184
701184 120 255 184 184 184 184 120 184 120 255 184 184 184 184 184 120 120
702255 184 184 184 184 184 184 120 255 184 184 184 184 184 184 184 255 184
703184 184 184 184 184 184 184 184
705 "Default image for the disabled scroll left button.")
707(defcustom tabbar-scroll-left-button
708 (cons (cons " <" tabbar-scroll-left-button-enabled-image)
709 (cons " =" tabbar-scroll-left-button-disabled-image))
710 "The scroll left button.
711See the variable `tabbar-button-widget' for details."
713 :type tabbar-button-widget
714 :set 'tabbar-setup-button)
716(defvar tabbar-scroll-right-button-enabled nil
717 "Text of the enabled scroll right button.")
719(defvar tabbar-scroll-right-button-disabled nil
720 "Text of the disabled scroll right button.")
722(defconst tabbar-scroll-right-button-keymap
723 (tabbar-make-button-keymap 'tabbar-scroll-right-button-callback)
724 "Keymap of the scroll right button.")
726(defun tabbar-scroll-right-button-callback (event)
727 "Handle a mouse EVENT on the scroll right button.
728Call `tabbar-scroll-right-function'."
730 (when tabbar-scroll-right-function
731 (save-selected-window
732 (select-window (posn-window (event-start event)))
733 (funcall tabbar-scroll-right-function event)
734 (force-mode-line-update)
738(defun tabbar-scroll-right-button-help (window object position)
739 "Return a help string or nil for none, for the scroll right button.
740Call `tabbar-scroll-right-help-function'.
741Arguments WINDOW, OBJECT and POSITION, are not used."
742 (when tabbar-scroll-right-help-function
743 (funcall tabbar-scroll-right-help-function)))
745(defconst tabbar-scroll-right-button-enabled-image
746 '((:type pbm :ascent center :data "\
750184 184 184 184 184 184 184 184 184 0 184 184 184 184 184 184 184 0 0 184
751184 184 184 184 184 0 0 0 184 184 184 184 184 0 0 0 0 184 184 184 184 0
7520 0 255 255 184 184 184 0 0 255 255 184 184 184 184 0 255 255 184 184 184
753184 184 184 255 184 184 184 184 184 184 184 184 184 184 184 184 184
755 "Default image for the enabled scroll right button.")
757(defconst tabbar-scroll-right-button-disabled-image
758 '((:type pbm :ascent center :data "\
762184 184 184 184 184 184 184 184 184 120 184 184 184 184 184 184 184 120
763120 184 184 184 184 184 184 120 184 120 184 184 184 184 184 120 184 184
764120 184 184 184 184 120 184 120 255 255 184 184 184 120 120 255 255 184
765184 184 184 120 255 255 184 184 184 184 184 184 255 184 184 184 184 184
766184 184 184 184 184 184 184 184
768 "Default image for the disabled scroll right button.")
770(defcustom tabbar-scroll-right-button
771 (cons (cons " >" tabbar-scroll-right-button-enabled-image)
772 (cons " =" tabbar-scroll-right-button-disabled-image))
773 "The scroll right button.
774See the variable `tabbar-button-widget' for details."
776 :type tabbar-button-widget
777 :set 'tabbar-setup-button)
781(defface tabbar-default-face
784 (:inherit variable-pitch
791 "Default face used in the tab bar."
794(defface tabbar-unselected-face
797 (:inherit tabbar-default-face
798 :box (:line-width 2 :color "white" :style pressed-button)
802 "Face used for uselected tabs."
805(defface tabbar-selected-face
808 (:inherit tabbar-default-face
809 :box (:line-width 2 :color "white" :style released-button)
814 "Face used for the selected tab."
817(defface tabbar-separator-face
820 (:inherit tabbar-default-face
825 "Face used for the select mode button."
828(defface tabbar-button-face
831 (:inherit tabbar-default-face
832 :box (:line-width 2 :color "white" :style released-button)
833 :foreground "dark red"
837 "Face used for the select mode button."
842(defun tabbar-scroll-left (event)
843 "On mouse EVENT, scroll current tab set on left."
844 (when (eq (event-basic-type event) 'mouse-1)
845 (tabbar-scroll (tabbar-current-tabset) -1)
848(defun tabbar-scroll-left-help ()
849 "Return the help string shown when mouse is onto the scroll left button."
850 "mouse-1: scroll tabs left.")
852(defun tabbar-scroll-right (event)
853 "On mouse EVENT, scroll current tab set on right."
854 (when (eq (event-basic-type event) 'mouse-1)
855 (tabbar-scroll (tabbar-current-tabset) 1)
858(defun tabbar-scroll-right-help ()
859 "Return the help string shown when mouse is onto the scroll right button."
860 "mouse-1: scroll tabs right.")
862 ;; These functions can be called at compilation time.
865 (defun tabbar-make-select-tab-command (tab)
866 "Return a command to handle TAB selection.
867That command calls `tabbar-select-tab-function' with the received
869 (let ((event (make-symbol "event")))
872 (setq tabbar-last-selected-tab ,tab)
873 (when tabbar-select-tab-function
874 (select-window (posn-window (event-start ,event)))
875 (funcall tabbar-select-tab-function ,event ,tab)
876 (force-mode-line-update)
879 (defun tabbar-make-help-on-tab-function (tab)
880 "Return a function that return a help string on TAB.
881That command calls `tabbar-help-on-tab-function' with TAB."
882 (let ((window (make-symbol "window"))
883 (object (make-symbol "object"))
884 (position (make-symbol "position"))
886 `(lambda (,window ,object ,position)
887 (when tabbar-help-on-tab-function
888 (funcall tabbar-help-on-tab-function ,tab)))))
892(defun tabbar-line-element (tab)
893 "Return an `header-line-format' template element from TAB.
894Call `tabbar-tab-label-function' to obtain a label for TAB."
895 (let* ((keymap (make-sparse-keymap))
896 (qtab (list 'quote tab))
897 (select (tabbar-make-select-tab-command qtab))
898 (help (tabbar-make-help-on-tab-function qtab))
899 (label (if tabbar-tab-label-function
900 (funcall tabbar-tab-label-function tab)
902 ;; Call `tabbar-select-tab-function' on mouse events.
903 (define-key keymap [header-line down-mouse-1] 'ignore)
904 (define-key keymap [header-line mouse-1] select)
905 (define-key keymap [header-line down-mouse-2] 'ignore)
906 (define-key keymap [header-line mouse-2] select)
907 (define-key keymap [header-line down-mouse-3] 'ignore)
908 (define-key keymap [header-line mouse-3] select)
909 ;; Return the tab followed by a separator.
910 (list (propertize label 'local-map keymap 'help-echo help
911 'face (if (tabbar-selected-p
912 tab(tabbar-current-tabset))
913 'tabbar-selected-face
914 'tabbar-unselected-face))
915 tabbar-separator-value)))
918 "Return the header line templates that represent the tab bar.
919Call `tabbar-current-tabset-function' to obtain the current tab set to
920display. Then call `tabbar-line-element' on each tab in current tab
921set's view to build a list of template elements for
922`header-line-format'."
923 (if (run-hook-with-args-until-success 'tabbar-inhibit-functions)
924 (setq header-line-format nil)
925 (let ((tabset (tabbar-current-tabset t))
926 (padcolor (face-background 'tabbar-default-face)))
928 (list (format "%s%s%s"
929 (if tabbar-home-function
930 tabbar-home-button-enabled
931 tabbar-home-button-disabled)
932 (if (> (tabbar-start tabset) 0)
933 tabbar-scroll-left-button-enabled
934 tabbar-scroll-left-button-disabled)
935 (if (< (tabbar-start tabset)
936 (1- (length (tabbar-tabs tabset))))
937 tabbar-scroll-right-button-enabled
938 tabbar-scroll-right-button-disabled))
939 tabbar-separator-value
941 ;; If a cached template exists, use it.
942 (tabbar-template tabset)
943 ;; Otherwise use a refeshed value.
944 (tabbar-set-template tabset
945 (mapcar 'tabbar-line-element
946 (tabbar-view tabset))))
947 (propertize "%-" 'face (list :background padcolor
948 :foreground padcolor))))
951;;; Cyclic navigation through tabs
953(defsubst tabbar-make-mouse-event (&optional type)
954 "Return a basic mouse event.
955Optional argument TYPE is a mouse event type. That is one of the
956symbols `mouse-1', `mouse-2' or `mouse-3'. The default is `mouse-1'."
957 (list (or (memq type '(mouse-2 mouse-3)) 'mouse-1)
958 (or (event-start nil) ;; Emacs 21.4
959 (list (selected-window) (point) '(0 . 0) 0))))
961(defmacro tabbar-click-on-tab (tab &optional type)
962 "Simulate a mouse click event on tab TAB.
963Optional argument TYPE is a mouse event type (see the function
964`tabbar-make-mouse-event' for details)."
965 `(,(tabbar-make-select-tab-command tab)
966 (tabbar-make-mouse-event ,type)))
968(defun tabbar-cycle (&optional backward)
969 "Cycle to the next available tab.
970If optional argument BACKWARD is non-nil, cycle to the previous tab
972The scope of the cyclic navigation through tabs is specified by the
973option `tabbar-cycling-scope'."
974 (let ((tabset (tabbar-current-tabset t))
977 (setq selected (tabbar-selected-tab tabset))
979 ;; Cycle through visible tabs only.
980 ((eq tabbar-cycling-scope 'tabs)
981 (setq tab (tabbar-tab-next tabset selected backward))
982 ;; When there is no tab after/before the selected one, cycle
983 ;; to the first/last visible tab.
985 (setq tabset (tabbar-tabs tabset)
986 tab (car (if backward (last tabset) tabset))))
988 ;; Cycle through tab groups only.
989 ((eq tabbar-cycling-scope 'groups)
990 (setq tabset (tabbar-get-tabsets-tabset)
991 tab (tabbar-tab-next tabset selected backward))
992 ;; When there is no group after/before the selected one, cycle
993 ;; to the first/last available group.
995 (setq tabset (tabbar-tabs tabset)
996 tab (car (if backward (last tabset) tabset))))
999 ;; Cycle through visible tabs then tab groups.
1000 (setq tab (tabbar-tab-next tabset selected backward))
1001 ;; When there is no visible tab after/before the selected one,
1002 ;; cycle to the next/previous available group.
1004 (setq tabset (tabbar-get-tabsets-tabset)
1005 tab (tabbar-tab-next tabset selected backward))
1006 ;; When there is no next/previous group, cycle to the
1007 ;; first/last available group.
1009 (setq tabset (tabbar-tabs tabset)
1010 tab (car (if backward (last tabset) tabset))))
1011 ;; Select the first/last visible tab of the new group.
1012 (setq tabset (tabbar-tabs (tabbar-tab-tabset tab))
1013 tab (car (if backward (last tabset) tabset))))
1015 (tabbar-click-on-tab tab))))
1018(defun tabbar-backward ()
1019 "Select the previous available tab.
1020Depend on the setting of the option `tabbar-cycling-scope'."
1025(defun tabbar-forward ()
1026 "Select the next available tab.
1027Depend on the setting of the option `tabbar-cycling-scope'."
1032(defun tabbar-backward-group ()
1033 "Go to selected tab in the previous available group."
1035 (let ((tabbar-cycling-scope 'groups))
1039(defun tabbar-forward-group ()
1040 "Go to selected tab in the next available group."
1042 (let ((tabbar-cycling-scope 'groups))
1046(defun tabbar-backward-tab ()
1047 "Select the previous visible tab."
1049 (let ((tabbar-cycling-scope 'tabs))
1053(defun tabbar-forward-tab ()
1054 "Select the next visible tab."
1056 (let ((tabbar-cycling-scope 'tabs))
1061(defvar tabbar-old-global-hlf nil
1062 "Global value of the header line when entering tab bar mode.")
1064(defconst tabbar-header-line-format '(:eval (tabbar-line))
1065 "The tab bar header line format.")
1068(define-minor-mode tabbar-mode
1069 "Toggle display of a tab bar in the header line.
1070With prefix argument ARG, turn on if positive, otherwise off.
1071Returns non-nil if the new state is enabled."
1076 (unless (eq header-line-format tabbar-header-line-format)
1077 ;; Save current default value of `header-line-format'.
1078 (setq tabbar-old-global-hlf (default-value 'header-line-format))
1079 (add-hook 'kill-buffer-hook 'tabbar-buffer-kill-buffer-hook)
1080 (tabbar-init-tabsets-store)
1081 (setq-default header-line-format tabbar-header-line-format))
1083 ;; Restore previous `header-line-format', if it has not changed.
1084 (when (eq (default-value 'header-line-format)
1085 tabbar-header-line-format)
1086 (setq-default header-line-format tabbar-old-global-hlf))
1087 (remove-hook 'kill-buffer-hook 'tabbar-buffer-kill-buffer-hook)
1088 (tabbar-free-tabsets-store)
1089 ;; Turn off locals tab bar mode
1091 (with-current-buffer b
1092 (tabbar-local-mode -1)))
1096(defvar tabbar-old-local-hlf nil
1097 "Local value of the header line when entering tab bar local mode.")
1098(make-variable-buffer-local 'tabbar-old-local-hlf)
1101(define-minor-mode tabbar-local-mode
1102 "Toggle local display of the tab bar.
1103With prefix argument ARG, turn on if positive, otherwise off.
1104Returns non-nil if the new state is enabled.
1105When on and tab bar global mode is on, if a buffer local value of
1106`header-line-format' exists, it is saved, then the local header line
1107is killed to show the tab bar. When off, the saved local value of the
1108header line is restored, hiding the tab bar."
1112 (if tabbar-local-mode
1113 (if (and tabbar-mode (local-variable-p 'header-line-format)
1114 (not (local-variable-p 'tabbar-old-local-hlf)))
1116 (setq tabbar-old-local-hlf header-line-format)
1117 (kill-local-variable 'header-line-format))
1118 (setq tabbar-local-mode nil))
1120 (when (local-variable-p 'tabbar-old-local-hlf)
1121 (setq header-line-format tabbar-old-local-hlf)
1122 (kill-local-variable 'tabbar-old-local-hlf))
1127(defun tabbar-default-inhibit-function ()
1128 "Inhibit display of the tab bar in specified windows.
1129That is dedicated windows, and `checkdoc' status windows."
1130 (or (window-dedicated-p (selected-window))
1131 (member (buffer-name)
1132 '(" *Checkdoc Status*"))))
1134(defun tabbar-buffer-kill-buffer-hook ()
1135 "Hook run just before actually killing a buffer.
1136In tab bar mode, try to switch to a buffer in the current tab bar,
1137after the current buffer has been killed. Try first the buffer in tab
1138after the current one, then the buffer in tab before. On success, put
1139the sibling buffer in front of the buffer list, so it will be selected
1142 (eq tabbar-current-tabset-function 'tabbar-buffer-tabs)
1143 (eq (current-buffer) (window-buffer (selected-window)))
1144 (let ((bl (tabbar-tab-values (tabbar-current-tabset)))
1147 (while (and bl (not found))
1148 (if (equal bn (car bl))
1150 (setq sibling (car bl)))
1152 (when (setq sibling (or (car bl) sibling))
1153 ;; Move sibling buffer in front of the buffer list.
1154 (save-current-buffer
1155 (switch-to-buffer sibling))))))
1159(defcustom tabbar-buffer-list-function
1161 "*Function that returns the list of buffers to show in tabs.
1162That function is called with no arguments and must return a list of
1167(defcustom tabbar-buffer-groups-function
1168 'tabbar-buffer-groups
1169 "*Function that gives the group names a buffer belongs to.
1170That function is passed a buffer and must return a list of group
1171names, or nil if the buffer has no group.
1172Notice that it is better that a buffer belongs to one group."
1176(defun tabbar-buffer-list ()
1177 "Return the list of buffers to show in tabs.
1178Exclude buffers whose name starts with a space, when they are not
1181 (mapcar #'(lambda (b)
1183 ((buffer-file-name b) b)
1184 ((char-equal ?\ (aref (buffer-name b) 0)))
1188(defun tabbar-buffer-groups (buffer)
1189 "Return the list of group names BUFFER belongs to.
1190Return only one group for each buffer."
1191 (with-current-buffer (get-buffer buffer)
1193 ((or (get-buffer-process (current-buffer))
1195 '(comint-mode compilation-mode)))
1198 ((member (buffer-name)
1199 '("*scratch*" "*Messages*"))
1202 ((eq major-mode 'dired-mode)
1206 '(help-mode apropos-mode Info-mode Man-mode))
1211 rmail-edit-mode vm-summary-mode vm-mode mail-mode
1212 mh-letter-mode mh-show-mode mh-folder-mode
1213 gnus-summary-mode message-mode gnus-group-mode
1214 gnus-article-mode score-mode gnus-browse-killed-mode))
1219 (if (and (stringp mode-name) (string-match "[^ ]" mode-name))
1221 (symbol-name major-mode)))
1225;;; Group buffers in tab sets.
1227(defun tabbar-buffer-cleanup-tabsets (buffers)
1228 "Remove obsolete tabs from existing tab sets.
1229That is tabs whose value is a killed buffer or a buffer not in
1230BUFFERS. Delete tab sets that no more contain tabs."
1231 (mapc 'tabbar-delete-tabset
1234 (mapc #'(lambda (tab)
1235 (let ((b (get-buffer (tabbar-tab-value tab))))
1236 (unless (and b (memq b buffers))
1237 (tabbar-delete-tab tab))))
1238 (tabbar-tabs tabset))
1239 (unless (tabbar-tabs tabset)
1242(defun tabbar-buffer-update-groups ()
1243 "Update group of buffers.
1244Return the the first group where the current buffer is."
1245 ;; Ensure that the current buffer will always have a tab!
1246 (let ((buffers (cons (current-buffer)
1247 (funcall tabbar-buffer-list-function)))
1251 (let* ((name (buffer-name buffer))
1252 (groups (funcall tabbar-buffer-groups-function name)))
1253 (when (eq buffer (current-buffer))
1254 (setq current-group (car groups)))
1255 (mapc #'(lambda (group)
1256 (let ((tabset (tabbar-get-tabset group)))
1258 (tabbar-add-tab tabset name t)
1259 (tabbar-make-tabset group name))))
1262 (tabbar-buffer-cleanup-tabsets buffers)
1265;;; Tab bar callbacks
1267(defvar tabbar-buffer-group-mode nil
1268 "Display tabs for group of buffers, when non-nil.")
1269(make-variable-buffer-local 'tabbar-buffer-group-mode)
1271(defun tabbar-buffer-tabs ()
1272 "Return the buffers to display on the tab bar, in a tab set."
1273 (let ((group (tabbar-buffer-update-groups))
1274 (buffer (buffer-name))
1276 (if tabbar-buffer-group-mode
1278 (setq tabset (tabbar-get-tabsets-tabset)
1279 curtab (tabbar-selected-tab (tabbar-current-tabset)))
1280 (unless (and (equal buffer (tabbar-tab-value curtab))
1281 (tabbar-select-tab curtab tabset))
1282 (tabbar-select-tab-value buffer tabset)))
1283 (setq tabset (tabbar-tab-tabset tabbar-last-selected-tab))
1284 (unless (and tabset (tabbar-get-tab buffer tabset))
1285 (setq tabset (tabbar-get-tabset group)))
1286 (tabbar-select-tab-value buffer tabset))
1289(defun tabbar-buffer-tab-label (tab)
1290 "Return the label to display TAB.
1291Must be a valid `header-line-format' template element."
1292 (if tabbar-buffer-group-mode
1293 (format "[%s]" (tabbar-tab-tabset tab))
1294 (format " %s " (tabbar-tab-value tab))))
1296(defun tabbar-buffer-help-on-tab (tab)
1297 "Return the help string shown when mouse is onto TAB."
1298 (if tabbar-buffer-group-mode
1299 "mouse-1: switch to selected tab in group"
1301mouse-1: switch to buffer, \
1302mouse-2: pop to buffer, \
1303mouse-3: delete other windows"
1306(defun tabbar-buffer-select-tab (event tab)
1307 "On mouse EVENT, select TAB."
1308 (let ((mouse-button (event-basic-type event))
1309 (buffer (tabbar-tab-value tab)))
1311 ((eq mouse-button 'mouse-1)
1312 (switch-to-buffer buffer))
1313 ((eq mouse-button 'mouse-2)
1314 (pop-to-buffer buffer t))
1315 ((eq mouse-button 'mouse-3)
1316 (delete-other-windows)))
1317 ;; Disable group mode.
1318 (setq tabbar-buffer-group-mode nil)
1321(defun tabbar-buffer-toggle-group-mode (event)
1322 "On mouse EVENT, toggle group mode.
1323When enabled, display tabs for group of buffers, instead of buffer
1325 (setq tabbar-buffer-group-mode (not tabbar-buffer-group-mode)))
1327(defun tabbar-buffer-toggle-group-mode-help ()
1328 "Return the help string shown when mouse is onto the toggle button."
1329 (if tabbar-buffer-group-mode
1330 "mouse-1: show buffers in selected group"
1331 "mouse-1: show groups of buffers"
1336;;; tabbar.el ends here