From 8cc96654f166c625c5b4224e7cea365604fbb8d7 Mon Sep 17 00:00:00 2001 From: John Foerch Date: Sat, 14 May 2011 00:18:28 -0400 Subject: [PATCH] buffer ordering This patch introduces a design for spatial ordering (one-dimensional) of buffers, a canonical order independent of (but not in conflict with) the temporal ordering (based on last-access time) that was there before. Spatial ordering of buffers lends itself especially well to a tabs-based UI or other on-screen visualization of the buffer-list. Central to this is the introduction of the idea of "buffer position" as regards the place where new buffers are inserted or buffers are buried. If buffers have a spatial order, we must be able to say where in that ordering a new buffer is initially inserted, recognizing that we may like to configure this based on circumstances such as whether the new buffer has an opener in the same window, or the type of the new buffer. Position is represented either as a simple index number or in a generalized way, as a function that, given a buffer list, a buffer to insert, and the index of another buffer appropriate to the type of action doing the insertion, returns the index at which to insert the new buffer. Several position functions are provided: * buffer_position_before Before the other buffer. * buffer_position_after After the other buffer. * buffer_position_end At the end of the list. * buffer_position_end_by_type After the last buffer of the same type in the list. Several variables have positions as their value, to configure the behavior of various ways in which buffers are inserted into a buffer list: * new_buffer_position, new_buffer_with_opener_position These two user variables both have a position as their value, and determine the basic behavior of where new buffers get inserted when there is or is not an opener. * buffer $position keyword The keyword '$position', given to the buffer constructor, overrides new_buffer_position or new_buffer_with_opener_position. * buffer.default_position The property of a buffer, 'default_position' also overrides new_buffer_position and new_buffer_with_opener_position, but is itself overridden by the keyword '$position'. This property provides a way to configure position based on buffer type. For instance, to make all special buffers be inserted at the end of the buffer list: special_buffer.prototype.default_position = buffer_position_end; * bury_buffer_position This variable controls the default position for buried buffers. Manual buffer moving is also provided, by the following commands: * buffer-move-forward (M-N) * buffer-move-backward (M-P) Other technical details follow: move_buffer_hook: new hook, called when a buffer is moved. buffer_container.buffer_list is now a list of the canonical ordering of buffers. last-accessed order is now tracked in buffer_container.buffer_history. buffer_container.insert: new method of buffer_container that manages inserting a new buffer in a given position. tab-bar.js, new-tabs.js, buffer_count_widget, session_load, session-auto-save updated. This patch is partly based on the work of Ben Spencer and Peter Lunicks. --- modules/bindings/default/global.js | 2 + modules/buffer.js | 177 +++++++++++++++++++++++++++++++------ modules/mode-line.js | 1 + modules/new-tabs.js | 56 +++++++++--- modules/session.js | 9 +- modules/tab-bar.js | 49 ++++++++-- 6 files changed, 247 insertions(+), 47 deletions(-) diff --git a/modules/bindings/default/global.js b/modules/bindings/default/global.js index f609d22..ed06c23 100644 --- a/modules/bindings/default/global.js +++ b/modules/bindings/default/global.js @@ -49,6 +49,8 @@ define_key(default_global_keymap, "C-x left", "buffer-previous"); define_key(default_global_keymap, "C-x right", "buffer-next"); define_key(default_global_keymap, "M-p", "buffer-previous"); define_key(default_global_keymap, "M-n", "buffer-next"); +define_key(default_global_keymap, "M-P", "buffer-move-backward"); +define_key(default_global_keymap, "M-N", "buffer-move-forward"); define_key(default_global_keymap, "C-x C-f", "find-url-new-buffer"); define_key(default_global_keymap, "C-s", "isearch-forward"); define_key(default_global_keymap, "C-r", "isearch-backward"); diff --git a/modules/buffer.js b/modules/buffer.js index 2f31eaa..1a22570 100644 --- a/modules/buffer.js +++ b/modules/buffer.js @@ -1,6 +1,6 @@ /** * (C) Copyright 2004-2007 Shawn Betts - * (C) Copyright 2007-2010,2012 John J. Foerch + * (C) Copyright 2007-2012 John J. Foerch * (C) Copyright 2007-2008 Jeremy Maitin-Shepard * * Use, modification, and distribution are subject to the terms specified in the @@ -29,6 +29,7 @@ define_buffer_local_hook("create_buffer_early_hook"); define_buffer_local_hook("create_buffer_late_hook"); define_buffer_local_hook("create_buffer_hook"); define_buffer_local_hook("kill_buffer_hook"); +define_buffer_local_hook("move_buffer_hook"); define_buffer_local_hook("buffer_scroll_hook"); define_buffer_local_hook("buffer_dom_content_loaded_hook"); define_buffer_local_hook("buffer_loaded_hook"); @@ -41,14 +42,67 @@ define_current_buffer_hook("current_buffer_scroll_hook", "buffer_scroll_hook"); define_current_buffer_hook("current_buffer_dom_content_loaded_hook", "buffer_dom_content_loaded_hook"); -define_keywords("$opener"); -function buffer_creator (type) { - var args = forward_keywords(arguments); - return function (window) { - return new type(window, args); - }; +function buffer_position_before (container, b, i) { + return i; +} + +function buffer_position_after (container, b, i) { + return i + 1; +} + +function buffer_position_end (container, b, i) { + return container.count; +} + +function buffer_position_end_by_type (container, b, i) { + // after last buffer of same type + var count = container.count; + var p = count - 1; + while (p >= 0 && + container.get_buffer(p).constructor != b.constructor) + { + p--; + } + if (p == -1) + return count; + else + return p + 1; } +define_variable("new_buffer_position", buffer_position_end, + "Used to compute the position in the buffer-list at which "+ + "to insert new buffers which do not have an opener. These "+ + "include buffers created by typing an url or webjump, or "+ + "buffers created via command-line remoting. The value "+ + "should be a number giving the index or a function of three "+ + "arguments that returns the index at which to insert the "+ + "new buffer. The first argument is the buffer_container "+ + "into which the new buffer is being inserted. The second "+ + "argument is the buffer to be inserted. The third argument "+ + "is the position of the currently selected buffer. Several "+ + "such functions are provided, including, buffer_position_before, "+ + "buffer_position_after, buffer_position_end, and "+ + "buffer_position_end_by_type."); + +define_variable("new_buffer_with_opener_position", buffer_position_after, + "Used to compute the position in the buffer-list at which "+ + "to insert new buffers which have an opener in the same "+ + "window. These include buffers created by following a link "+ + "or frame, and contextual help buffers. The allowed values "+ + "are the same as those for `new_buffer_position', except that "+ + "the second argument passed to the function is the index of "+ + "the opener instead of the index of the current buffer (often "+ + "one and the same)."); + +define_variable("bury_buffer_position", null, + "Used to compute the position in the buffer-list to move a "+ + "buried buffer to. A value of null prevents bury-buffer "+ + "from moving the buffer at all. Other allowed values are "+ + "the same as those for `new_buffer_position', except that "+ + "the second argument passed to the function is the index of "+ + "the new buffer that will be selected after burying the "+ + "current buffer."); + define_variable("allow_browser_window_close", true, "If this is set to true, if a content buffer page calls " + "window.close() from JavaScript and is not prevented by the " + @@ -56,13 +110,21 @@ define_variable("allow_browser_window_close", true, "a window that was not opened by a script, the buffer will be " + "killed, deleting the window as well if it is the only buffer."); +define_keywords("$opener", "$position"); +function buffer_creator (type) { + var args = forward_keywords(arguments); + return function (window) { + return new type(window, args); + }; +} + function buffer_modality (buffer) { buffer.keymaps.push(default_global_keymap); } function buffer (window) { this.constructor_begin(); - keywords(arguments); + keywords(arguments, $position = this.default_position); this.opener = arguments.$opener; this.window = window; var element = create_XUL(window, "vbox"); @@ -73,7 +135,8 @@ function buffer (window) { browser.setAttribute("autocompletepopup", "popup_autocomplete"); element.appendChild(browser); this.window.buffers.container.appendChild(element); - this.window.buffers.buffer_list.push(this); + this.window.buffers.insert(this, arguments.$position, this.opener); + this.window.buffers.buffer_history.push(this); this.element = element; this.browser = element.firstChild; this.element.conkeror_buffer_object = this; @@ -135,6 +198,12 @@ function buffer (window) { buffer.prototype = { constructor: buffer, + // default_position is the default value for the $position keyword to + // the buffer constructor. This property can be set on the prototype + // of a subclass in order to override new_buffer_position and + // new_buffer_with_opener_position for specific types of buffers. + default_position: null, + /* Saved focus state */ saved_focused_frame: null, saved_focused_element: null, @@ -324,12 +393,36 @@ function buffer_container (window, create_initial_buffer) { this.window = window; this.container = window.document.getElementById("buffer-container"); this.buffer_list = []; + this.buffer_history = []; window.buffers = this; create_initial_buffer(window); } buffer_container.prototype = { constructor: buffer_container, + insert: function (buffer, position, opener) { + var i = this.index_of(opener); + if (position == null) { + if (i == null) + position = new_buffer_position; + else + position = new_buffer_with_opener_position; + } + if (i == null) + i = this.selected_index || 0; + try { + if (position instanceof Function) + var p = position(this, buffer, i); + else + p = position; + this.buffer_list.splice(p, 0, buffer); + } catch (e) { + this.buffer_list.splice(0, 0, buffer); + dumpln("Error inserting buffer, inserted at 0."); + dump_error(e); + } + }, + get current () { return this.container.selectedPanel.conkeror_buffer_object; }, @@ -339,8 +432,8 @@ buffer_container.prototype = { if (old_value == buffer) return; - this.buffer_list.splice(this.buffer_list.indexOf(buffer), 1); - this.buffer_list.unshift(buffer); + this.buffer_history.splice(this.buffer_history.indexOf(buffer), 1); + this.buffer_history.unshift(buffer); this._switch_away_from(this.current); this._switch_to(buffer); @@ -386,29 +479,29 @@ buffer_container.prototype = { }, get count () { - return this.container.childNodes.length; + return this.buffer_list.length; }, get_buffer: function (index) { if (index >= 0 && index < this.count) - return this.container.childNodes.item(index).conkeror_buffer_object; + return this.buffer_list[index] return null; }, get selected_index () { - var nodes = this.container.childNodes; + var nodes = this.buffer_list; var count = nodes.length; for (var i = 0; i < count; ++i) - if (nodes.item(i) == this.container.selectedPanel) + if (nodes[i] == this.container.selectedPanel.conkeror_buffer_object) return i; return null; }, index_of: function (b) { - var nodes = this.container.childNodes; + var nodes = this.buffer_list; var count = nodes.length; for (var i = 0; i < count; ++i) - if (nodes.item(i) == b.element) + if (nodes[i] == b) return i; return null; }, @@ -436,10 +529,10 @@ buffer_container.prototype = { var count = this.count; if (count <= 1) return false; - var new_buffer = this.buffer_list[0]; + var new_buffer = this.buffer_history[0]; var changed = false; if (b == new_buffer) { - new_buffer = this.buffer_list[1]; + new_buffer = this.buffer_history[1]; changed = true; } this._switch_away_from(this.current); @@ -452,25 +545,32 @@ buffer_container.prototype = { b.destroy(); this.container.removeChild(element); this.buffer_list.splice(this.buffer_list.indexOf(b), 1); + this.buffer_history.splice(this.buffer_history.indexOf(b), 1); this._switch_to(new_buffer); if (changed) { select_buffer_hook.run(new_buffer); - this.buffer_list.splice(this.buffer_list.indexOf(new_buffer), 1); - this.buffer_list.unshift(new_buffer); + this.buffer_history.splice(this.buffer_history.indexOf(new_buffer), 1); + this.buffer_history.unshift(new_buffer); } kill_buffer_hook.run(b); return true; }, bury_buffer: function (b) { - var new_buffer = this.buffer_list[0]; + var new_buffer = this.buffer_history[0]; if (b == new_buffer) - new_buffer = this.buffer_list[1]; + new_buffer = this.buffer_history[1]; if (! new_buffer) throw interactive_error("No other buffer"); - this.buffer_list.splice(this.buffer_list.indexOf(b), 1); - this.buffer_list.push(b); + if (bury_buffer_position != null) { + this.buffer_list.splice(this.buffer_list.indexOf(b), 1); + this.insert(b, bury_buffer_position, new_buffer); + } + this.buffer_history.splice(this.buffer_history.indexOf(b), 1); + this.buffer_history.push(b); this.current = new_buffer; + if (bury_buffer_position != null) + move_buffer_hook.run(b); return true; }, @@ -639,6 +739,31 @@ minibuffer.prototype.read_buffer = function () { }; +function buffer_move_forward (window, count) { + var buffers = window.buffers; + var index = buffers.selected_index; + var buffer = buffers.current + var total = buffers.count; + if (total == 1) + return; + var new_index = (index + count) % total; + if (new_index == index) + return; + if (new_index < 0) + new_index += total; + buffers.buffer_list.splice(index, 1); + buffers.buffer_list.splice(new_index, 0, buffer); + move_buffer_hook.run(buffer); +} +interactive("buffer-move-forward", + "Move the current buffer forward in the buffer order.", + function (I) { buffer_move_forward(I.window, I.p); }); + +interactive("buffer-move-backward", + "Move the current buffer backward in the buffer order.", + function (I) { buffer_move_forward(I.window, -I.p); }); + + function buffer_next (window, count) { var index = window.buffers.selected_index; var total = window.buffers.count; @@ -668,7 +793,7 @@ interactive("switch-to-buffer", (yield I.minibuffer.read_buffer( $prompt = "Switch to buffer:", $default = (I.window.buffers.count > 1 ? - I.window.buffers.buffer_list[1] : + I.window.buffers.buffer_history[1] : I.buffer)))); }); diff --git a/modules/mode-line.js b/modules/mode-line.js index f3ddeff..a43a022 100644 --- a/modules/mode-line.js +++ b/modules/mode-line.js @@ -212,6 +212,7 @@ function buffer_count_widget (window) { this.add_hook("select_buffer_hook"); this.add_hook("create_buffer_hook"); this.add_hook("kill_buffer_hook"); + this.add_hook("move_buffer_hook"); } buffer_count_widget.prototype.__proto__ = text_widget.prototype; buffer_count_widget.prototype.update = function () { diff --git a/modules/new-tabs.js b/modules/new-tabs.js index 6093bef..42006a4 100644 --- a/modules/new-tabs.js +++ b/modules/new-tabs.js @@ -46,12 +46,13 @@ function tab_bar (window) { add_hook.call(window, "select_buffer_hook", tab_bar_select_buffer); add_hook.call(window, "create_buffer_early_hook", tab_bar_add_buffer); add_hook.call(window, "kill_buffer_hook", tab_bar_kill_buffer); + add_hook.call(window, "move_buffer_hook", tab_bar_move_buffer); add_hook.call(window, "create_buffer_hook", tab_bar_update_buffer_title); add_hook.call(window, "buffer_title_change_hook", tab_bar_update_buffer_title); add_hook.call(window, "buffer_description_change_hook", tab_bar_update_buffer_title); add_hook.call(window, "buffer_icon_change_hook", tab_bar_update_buffer_icon); - window.buffers.for_each(tab_bar_add_buffer); + window.buffers.for_each(function (b) tab_bar_add_buffer(b, true)); this.update_multiple_attribute(); if (window.buffers.current != null) tab_bar_select_buffer(window.buffers.current); @@ -65,6 +66,7 @@ tab_bar.prototype.destroy = function () { remove_hook.call(this.window, "select_buffer_hook", tab_bar_select_buffer); remove_hook.call(this.window, "create_buffer_early_hook", tab_bar_add_buffer); remove_hook.call(this.window, "kill_buffer_hook", tab_bar_kill_buffer); + remove_hook.call(this.window, "move_buffer_hook", tab_bar_move_buffer); remove_hook.call(this.window, "create_buffer_hook", tab_bar_update_buffer_title); remove_hook.call(this.window, "buffer_title_change_hook", tab_bar_update_buffer_title); remove_hook.call(this.window, "buffer_description_change_hook", tab_bar_update_buffer_title); @@ -77,6 +79,19 @@ tab_bar.prototype.destroy = function () { /** + * Updates the "index" node and "ordinal" attribute of all tabs. + */ +tab_bar.prototype.update_ordinals = function () { + var buffers = this.window.buffers; + for (var i = 0, n = this.element.childNodes.length; i < n; i++) { + var ordinal = buffers.index_of(this.element.childNodes[i].buffer) + 1; + this.element.childNodes[i].setAttribute("ordinal", ordinal); + this.element.childNodes[i].index.setAttribute("value", ordinal); + } +}; + + +/** * Updates the "multiple" attribute of the tab bar. */ tab_bar.prototype.update_multiple_attribute = function () { @@ -88,24 +103,33 @@ tab_bar.prototype.update_multiple_attribute = function () { /** - * Adds a tab for the given buffer. + * Adds a tab for the given buffer. When second argument 'noupdate' is + * true, a new tab in the middle of the buffer list will not cause the + * ordinals of other tabs to be updated. This is used during + * initialization of the tab bar. */ -function tab_bar_add_buffer (buffer) { +function tab_bar_add_buffer (buffer, noupdate) { // Get the tab bar var tabbar = buffer.window.tab_bar; tabbar.update_multiple_attribute(); + var ordinal = buffer.window.buffers.index_of(buffer) + 1; + if (ordinal < buffer.window.buffers.buffer_list.length && ! noupdate) + tabbar.update_ordinals(); + // Create a tab and add it to the tab bar var tab = create_XUL(buffer.window, "hbox"); + tab.buffer = buffer; tab.setAttribute("class", "tab2"); tab.addEventListener("click", function (event) { if (event.button == tab_bar_button_select) { - if (!buffer.dead) - buffer.window.buffers.current = buffer; + if (!tab.buffer.dead) + tab.buffer.window.buffers.current = tab.buffer; } }, false /* not capturing */); tab.setAttribute("selected", "false"); + tab.setAttribute("ordinal", ordinal); // Create the label to hold the buffer icon var image = create_XUL(buffer.window, "image"); @@ -116,6 +140,7 @@ function tab_bar_add_buffer (buffer) { // Create the label to hold the tab number var index = create_XUL(buffer.window, "label"); index.setAttribute("class", "tab2-index"); + index.setAttribute("value", ordinal); // Create the label to hold the tab title var label = create_XUL(buffer.window, "label"); @@ -125,7 +150,7 @@ function tab_bar_add_buffer (buffer) { // No close button, just use the designated mouse button. tab.addEventListener("click", function (event) { if (event.button == tab_bar_button_close) { - kill_buffer(buffer); + kill_buffer(tab.buffer); event.stopPropagation(); } }, false /* not capturing */); @@ -142,11 +167,6 @@ function tab_bar_add_buffer (buffer) { tabbar.element.appendChild(tab); buffer.tab = tab; tab_bar_update_buffer_title(buffer); - - // Set the tab number. Remember that at this point, the tab has already been - // added to the hbox. - var total = tabbar.element.getElementsByClassName("tab2").length; - index.value = total; } @@ -163,9 +183,17 @@ function tab_bar_kill_buffer (b) { delete b.tab; // Renumber the tabs. - for (var i = 0; i < t.element.childNodes.length; i++) { - t.element.childNodes[i].index.value = i + 1; - } + t.update_ordinals(); +} + + +/** + * Updates all tab indices and ensure that the current tab is still visible. + */ +function tab_bar_move_buffer (b) { + var t = b.window.tab_bar; + t.update_ordinals(); + t.element.ensureElementIsVisible(b.window.buffers.current.tab); } diff --git a/modules/session.js b/modules/session.js index dd756e0..45cce7b 100644 --- a/modules/session.js +++ b/modules/session.js @@ -130,7 +130,8 @@ in_module(null); } else { let c = buffer_creator(content_buffer, $load = session[s][i], - $opener = opener); + $opener = opener, + $position = buffer_position_end); create_buffer(window, c, OPEN_NEW_BUFFER_BACKGROUND); } } @@ -147,7 +148,8 @@ in_module(null); for (let i = 1; session[i] != undefined; ++i) { let c = buffer_creator(content_buffer, $load = session[i], - $opener = opener); + $opener = opener, + $position = buffer_position_end); create_buffer(window, c, OPEN_NEW_BUFFER_BACKGROUND); } } @@ -414,6 +416,7 @@ in_module(null); remove_hook("window_initialize_late_hook", _session_auto_save_mode_bootstrap); add_hook("create_buffer_hook", session_auto_save_save); add_hook("kill_buffer_hook", session_auto_save_save); + add_hook("move_buffer_hook", session_auto_save_save); add_hook("content_buffer_location_change_hook", session_auto_save_save); let user_gave_urls = false; for (let i = 0; i < command_line.length; ++i) { @@ -433,6 +436,7 @@ in_module(null); if (conkeror_started) { add_hook("create_buffer_hook", session_auto_save_save); add_hook("kill_buffer_hook", session_auto_save_save); + add_hook("move_buffer_hook", session_auto_save_save); add_hook("content_buffer_location_change_hook", session_auto_save_save); } else add_hook("window_initialize_late_hook", _session_auto_save_mode_bootstrap); @@ -441,6 +445,7 @@ in_module(null); let _session_auto_save_mode_disable = function () { remove_hook("create_buffer_hook", session_auto_save_save); remove_hook("kill_buffer_hook", session_auto_save_save); + remove_hook("move_buffer_hook", session_auto_save_save); remove_hook("content_buffer_location_change_hook", session_auto_save_save); // Just in case. remove_hook("window_initialize_late_hook", _session_auto_save_mode_bootstrap); diff --git a/modules/tab-bar.js b/modules/tab-bar.js index e04fc35..b0c5061 100644 --- a/modules/tab-bar.js +++ b/modules/tab-bar.js @@ -23,13 +23,15 @@ function tab_bar (window) { add_hook.call(window, "select_buffer_hook", tab_bar_select_buffer); add_hook.call(window, "create_buffer_early_hook", tab_bar_add_buffer); add_hook.call(window, "kill_buffer_hook", tab_bar_kill_buffer); + add_hook.call(window, "move_buffer_hook", tab_bar_move_buffer); add_hook.call(window, "create_buffer_hook", tab_bar_update_buffer_title); add_hook.call(window, "buffer_title_change_hook", tab_bar_update_buffer_title); add_hook.call(window, "buffer_description_change_hook", tab_bar_update_buffer_title); add_hook.call(window, "buffer_icon_change_hook", tab_bar_update_buffer_icon); - window.buffers.for_each(tab_bar_add_buffer); + window.buffers.for_each(function (b) tab_bar_add_buffer(b, true)); this.update_multiple_attribute(); + this.update_ordinals(); if (window.buffers.current != null) tab_bar_select_buffer(window.buffers.current); } @@ -37,6 +39,7 @@ tab_bar.prototype.destroy = function () { remove_hook.call(this.window, "select_buffer_hook", tab_bar_select_buffer); remove_hook.call(this.window, "create_buffer_early_hook", tab_bar_add_buffer); remove_hook.call(this.window, "kill_buffer_hook", tab_bar_kill_buffer); + remove_hook.call(this.window, "move_buffer_hook", tab_bar_move_buffer); remove_hook.call(this.window, "create_buffer_hook", tab_bar_update_buffer_title); remove_hook.call(this.window, "buffer_title_change_hook", tab_bar_update_buffer_title); remove_hook.call(this.window, "buffer_description_change_hook", tab_bar_update_buffer_title); @@ -46,6 +49,19 @@ tab_bar.prototype.destroy = function () { this.element.parentNode.removeChild(this.element); delete this.window.tab_bar; }; + + +/** + * Updates the "ordinal" attribute of all tabs. + */ +tab_bar.prototype.update_ordinals = function () { + var buffers = this.window.buffers; + for (var i = 0, n = this.element.childNodes.length; i < n; i++) { + var ordinal = buffers.index_of(this.element.childNodes[i].buffer) + 1; + this.element.childNodes[i].setAttribute("ordinal", ordinal); + } +}; + tab_bar.prototype.update_multiple_attribute = function () { if (this.window.buffers.count > 1) this.element.setAttribute("multiple", "true"); @@ -53,16 +69,27 @@ tab_bar.prototype.update_multiple_attribute = function () { this.element.setAttribute("multiple", "false"); }; -function tab_bar_add_buffer (b) { +/** + * Adds a tab for the given buffer. When second argument 'noupdate' is + * true, a new tab in the middle of the buffer list will not cause the + * ordinals of other tabs to be updated. This is used during + * initialization of the tab bar. + */ +function tab_bar_add_buffer (b, noupdate) { var t = b.window.tab_bar; t.update_multiple_attribute(); + var ordinal = b.window.buffers.index_of(b) + 1; + if (ordinal < b.window.buffers.buffer_list.length && ! noupdate) + t.update_ordinals(); var tab = create_XUL(b.window, "hbox"); + tab.buffer = b; tab.setAttribute("class", "tab"); tab.addEventListener("click", function () { - if (!b.dead) - b.window.buffers.current = b; + if (!tab.buffer.dead) + tab.buffer.window.buffers.current = tab.buffer; }, false /* not capturing */); tab.setAttribute("selected", "false"); + tab.setAttribute("ordinal", ordinal); var image = create_XUL(b.window, "image"); image.setAttribute("class", "tab-icon"); if (b.icon != null) @@ -73,7 +100,7 @@ function tab_bar_add_buffer (b) { var button = create_XUL(b.window, "toolbarbutton"); button.setAttribute("class", "tab-close-button"); button.addEventListener("click", function (event) { - kill_buffer(b); + kill_buffer(tab.buffer); event.stopPropagation(); }, false /* not capturing */); tab.appendChild(image); @@ -93,8 +120,20 @@ function tab_bar_kill_buffer (b) { t.selected_buffer = null; b.tab.parentNode.removeChild(b.tab); delete b.tab; + t.update_ordinals(); } + +/** + * Updates all tab indices and ensure that the current tab is still visible. + */ +function tab_bar_move_buffer (b) { + var t = b.window.tab_bar; + t.update_ordinals(); + t.element.ensureElementIsVisible(b.window.buffers.current.tab); +} + + function tab_bar_select_buffer (b) { var t = b.window.tab_bar; if (t.selected_buffer != null) -- 2.11.4.GIT