2 * (C) Copyright 2008 Jeremy Maitin-Shepard
3 * (C) Copyright 2008 Nicholas A. Zigarovich
4 * (C) Copyright 2008 John J. Foerch
5 * (C) Copyright 2011 Peter Lunicks
7 * Use, modification, and distribution are subject to the terms specified in the
10 * This is a tab bar which is based on tab-bar.js but makes customization of the
11 * tabs much easier. It provides sensible default styles but lets the user
12 * simply override these defaults by normal CSS.
17 define_variable("tab_bar_button_select", 0,
18 "The mouse button that selects tabs." +
19 "0 = left, 1 = middle, 2 = right, null = disabled.");
21 define_variable("tab_bar_button_close", 2,
22 "The mouse button that closes tabs." +
23 "0 = left, 1 = middle, 2 = right, null = disabled.");
25 define_variable("tab_bar_show_icon", false,
26 "Whether or not to show buffer icons in tabs.");
28 define_variable("tab_bar_show_index", true,
29 "Whether or not to show the tab index in each tab.");
32 * Constructs a tab bar for the given window.
34 function tab_bar (window) {
35 window.tab_bar = this;
36 var scrollbox = create_XUL(window, "arrowscrollbox");
37 scrollbox.setAttribute("id", "tab2-bar");
38 scrollbox.setAttribute("orient", "horizontal");
39 var after = window.buffers.container;
41 this.element = scrollbox;
42 after.parentNode.insertBefore(scrollbox, after);
44 add_hook.call(window, "select_buffer_hook", tab_bar_select_buffer);
45 add_hook.call(window, "create_buffer_early_hook", tab_bar_add_buffer);
46 add_hook.call(window, "kill_buffer_hook", tab_bar_kill_buffer);
47 add_hook.call(window, "move_buffer_hook", tab_bar_move_buffer);
48 add_hook.call(window, "create_buffer_hook", tab_bar_update_buffer_title);
49 add_hook.call(window, "buffer_title_change_hook", tab_bar_update_buffer_title);
50 add_hook.call(window, "buffer_description_change_hook", tab_bar_update_buffer_title);
51 add_hook.call(window, "buffer_icon_change_hook", tab_bar_update_buffer_icon);
53 window.buffers.for_each(function (b) tab_bar_add_buffer(b, true));
54 this.update_multiple_attribute();
55 if (window.buffers.current != null)
56 tab_bar_select_buffer(window.buffers.current);
61 * Destroys the tab bar.
63 tab_bar.prototype.destroy = function () {
64 remove_hook.call(this.window, "select_buffer_hook", tab_bar_select_buffer);
65 remove_hook.call(this.window, "create_buffer_early_hook", tab_bar_add_buffer);
66 remove_hook.call(this.window, "kill_buffer_hook", tab_bar_kill_buffer);
67 remove_hook.call(this.window, "move_buffer_hook", tab_bar_move_buffer);
68 remove_hook.call(this.window, "create_buffer_hook", tab_bar_update_buffer_title);
69 remove_hook.call(this.window, "buffer_title_change_hook", tab_bar_update_buffer_title);
70 remove_hook.call(this.window, "buffer_description_change_hook", tab_bar_update_buffer_title);
71 remove_hook.call(this.window, "buffer_icon_change_hook", tab_bar_update_buffer_icon);
72 this.window.buffers.for_each(function (b) { delete b.tab; });
73 this.selected_buffer = null;
74 this.element.parentNode.removeChild(this.element);
75 delete this.window.tab_bar;
80 * Updates the "index" node and "ordinal" attribute of all tabs.
82 tab_bar.prototype.update_ordinals = function () {
83 var buffers = this.window.buffers;
84 for (var i = 0, n = this.element.childNodes.length; i < n; i++) {
85 var ordinal = buffers.index_of(this.element.childNodes[i].buffer) + 1;
86 this.element.childNodes[i].setAttribute("ordinal", ordinal);
87 this.element.childNodes[i].index.setAttribute("value", ordinal);
93 * Updates the "multiple" attribute of the tab bar.
95 tab_bar.prototype.update_multiple_attribute = function () {
96 if (this.window.buffers.count > 1)
97 this.element.setAttribute("multiple", "true");
99 this.element.setAttribute("multiple", "false");
104 * Adds a tab for the given buffer. When second argument 'noupdate' is
105 * true, a new tab in the middle of the buffer list will not cause the
106 * ordinals of other tabs to be updated. This is used during
107 * initialization of the tab bar.
109 function tab_bar_add_buffer (buffer, noupdate) {
112 var tabbar = buffer.window.tab_bar;
113 tabbar.update_multiple_attribute();
115 var ordinal = buffer.window.buffers.index_of(buffer) + 1;
116 if (ordinal < buffer.window.buffers.buffer_list.length && ! noupdate)
117 tabbar.update_ordinals();
119 // Create a tab and add it to the tab bar
120 var tab = create_XUL(buffer.window, "hbox");
122 tab.setAttribute("class", "tab2");
123 tab.addEventListener("click", function (event) {
124 if (event.button == tab_bar_button_select) {
125 if (!tab.buffer.dead)
126 tab.buffer.window.buffers.current = tab.buffer;
128 }, false /* not capturing */);
129 tab.setAttribute("selected", "false");
131 // Create the label to hold the buffer icon
132 var image = create_XUL(buffer.window, "image");
133 image.setAttribute("class", "tab2-icon");
134 if (buffer.icon != null)
135 image.setAttribute("src", buffer.icon);
137 // Create the label to hold the tab number
138 var index = create_XUL(buffer.window, "label");
139 index.setAttribute("class", "tab2-index");
140 index.setAttribute("value", ordinal);
142 // Create the label to hold the tab title
143 var label = create_XUL(buffer.window, "label");
144 label.setAttribute("class", "tab2-label");
145 label.setAttribute("crop", "end");
147 // No close button, just use the designated mouse button.
148 tab.addEventListener("click", function (event) {
149 if (event.button == tab_bar_button_close) {
150 kill_buffer(tab.buffer);
151 event.stopPropagation();
153 }, false /* not capturing */);
155 // Add all the stuff to the new tab
159 if (tab_bar_show_index)
160 tab.appendChild(index);
161 if (tab_bar_show_icon)
162 tab.appendChild(image);
163 tab.appendChild(label);
164 tabbar.element.appendChild(tab);
166 tab_bar_update_buffer_title(buffer);
168 // Note, XULRunner 1.9.x puts the tab in the wrong location if we set
169 // the ordinal before adding the tab to the tab-bar.
170 tab.setAttribute("ordinal", ordinal);
175 * Removes the tab for the given buffer.
177 function tab_bar_kill_buffer (b) {
178 var t = b.window.tab_bar;
179 t.update_multiple_attribute();
180 if (t.selected_buffer == b)
181 t.selected_buffer = null;
182 b.tab.parentNode.removeChild(b.tab);
183 t = b.window.tab_bar;
186 // Renumber the tabs.
192 * Updates all tab indices and ensure that the current tab is still visible.
194 function tab_bar_move_buffer (b) {
195 var t = b.window.tab_bar;
197 t.element.ensureElementIsVisible(b.window.buffers.current.tab);
202 * Updates the tab of the given buffer to indicate it is the currently open one.
204 function tab_bar_select_buffer (b) {
205 var t = b.window.tab_bar;
206 if (t.selected_buffer != null)
207 t.selected_buffer.tab.setAttribute("selected", "false");
208 t.selected_buffer = b;
209 b.tab.setAttribute("selected", "true");
210 t.element.ensureElementIsVisible(b.tab);
215 * Updates the tab title for the given buffer.
217 function tab_bar_update_buffer_title (b) {
219 if (title == null || title.length == 0)
220 title = b.description;
221 b.tab.label.setAttribute("value", title);
226 * Updates the tab icon for the given buffer.
228 function tab_bar_update_buffer_icon (b) {
230 b.tab.image.setAttribute("src", b.icon);
232 b.tab.image.removeAttribute("src");
237 * Inserts the tab bar in the given window.
239 function tab_bar_install (window) {
241 throw new Error("tab bar already initialized for window");
247 * Removes the tab bar from the given window.
248 * If the tab bar is not installed, throws an error.
250 function tab_bar_uninstall (window) {
252 throw new Error("tab bar not initialized for window");
253 window.tab_bar.destroy();
257 define_global_mode("tab_bar_mode",
258 function () { // enable
259 add_hook("window_initialize_hook", tab_bar_install);
260 for_each_window(tab_bar_install);
262 function () { // disable
263 remove_hook("window_initialize_hook", tab_bar_install);
264 for_each_window(tab_bar_uninstall);