・サブツリーの開閉機能を実装

・サブツリーが折り畳まれたタブを閉じた時、子孫のタブをすべて閉じる機能を実装


git-svn-id: http://www.cozmixng.org/repos/piro/treestyletab/trunk@1226 599a83e7-65a4-db11-8015-0010dcdd6dc2
This commit is contained in:
piro 2007-10-18 17:11:03 +00:00
parent 8e684bc5eb
commit ee84bab985
6 changed files with 205 additions and 59 deletions

View File

@ -15,6 +15,9 @@
name="extensions.treestyletab.adoptChildrenToGrandParentOnRemoveTab" name="extensions.treestyletab.adoptChildrenToGrandParentOnRemoveTab"
type="bool" type="bool"
inverted="true"/> inverted="true"/>
<preference id="extensions.treestyletab.autoExpandSubTreeOnAppendChild"
name="extensions.treestyletab.autoExpandSubTreeOnAppendChild"
type="bool"/>
<preference id="browser.link.open_newwindow" <preference id="browser.link.open_newwindow"
name="browser.link.open_newwindow" name="browser.link.open_newwindow"
type="int"/> type="int"/>
@ -26,6 +29,9 @@
<checkbox id="extensions.treestyletab.adoptChildrenToGrandParentOnRemoveTab-check" <checkbox id="extensions.treestyletab.adoptChildrenToGrandParentOnRemoveTab-check"
preference="extensions.treestyletab.adoptChildrenToGrandParentOnRemoveTab" preference="extensions.treestyletab.adoptChildrenToGrandParentOnRemoveTab"
label="&config.adoptChildrenToGrandParentOnRemoveTab;"/> label="&config.adoptChildrenToGrandParentOnRemoveTab;"/>
<checkbox id="extensions.treestyletab.autoExpandSubTreeOnAppendChild-check"
preference="extensions.treestyletab.autoExpandSubTreeOnAppendChild"
label="&config.autoExpandSubTreeOnAppendChild;"/>
<groupbox> <groupbox>
<caption label="&config.open_newwindow.caption;"/> <caption label="&config.open_newwindow.caption;"/>
<vbox> <vbox>

View File

@ -4,7 +4,11 @@ var TreeStyleTabService = {
kID : 'treestyletab-id', kID : 'treestyletab-id',
kCHILDREN : 'treestyletab-children', kCHILDREN : 'treestyletab-children',
kPARENT : 'treestyletab-parent', kPARENT : 'treestyletab-parent',
kINSERTBEFORE : 'treestyletab-insert-before', kINSERT_BEFORE : 'treestyletab-insert-before',
kSUBTREE_COLLAPSED : 'treestyletab-subtree-collapsed',
kCOLLAPSED : 'treestyletab-tab-collapsed',
kTWISTY : 'treestyletab-tab-tree-twisty',
levelMargin : 12, levelMargin : 12,
@ -52,6 +56,16 @@ var TreeStyleTabService = {
return false; return false;
}, },
isEventFiredOnTwisty : function(aEvent)
{
var node = aEvent.originalTarget;
while (node.getAttribute('class') != this.kTWISTY && node.localName != 'tabs')
{
node = node.parentNode;
}
return (node && node.getAttribute('class') == this.kTWISTY) ? true : false ;
},
get browser() get browser()
{ {
return 'SplitBrowser' ? SplitBrowser.activeBrowser : gBrowser ; return 'SplitBrowser' ? SplitBrowser.activeBrowser : gBrowser ;
@ -230,8 +244,24 @@ var TreeStyleTabService = {
aTabBrowser.mTabContainer.addEventListener('TabClose', this, true); aTabBrowser.mTabContainer.addEventListener('TabClose', this, true);
aTabBrowser.mTabContainer.addEventListener('TabMove', this, true); aTabBrowser.mTabContainer.addEventListener('TabMove', this, true);
aTabBrowser.mTabContainer.addEventListener('SSTabRestoring', this, true); aTabBrowser.mTabContainer.addEventListener('SSTabRestoring', this, true);
aTabBrowser.mTabContainer.addEventListener('click', this, true);
aTabBrowser.mTabContainer.addEventListener('mousedown', this, true);
aTabBrowser.mTabContainer.addEventListener('select', this, true);
aTabBrowser.mPanelContainer.addEventListener('click', this, true); aTabBrowser.mPanelContainer.addEventListener('click', this, true);
eval('aTabBrowser.mTabContainer.selectNewTab = '+
aTabBrowser.mTabContainer.selectNewTab.toSource().replace(
/\{/,
<><![CDATA[
{
if (aNewTab.__treestyletab__preventSelect) {
aNewTab.__treestyletab__preventSelect = false;
return;
}
]]></>
)
);
var addTabMethod = 'addTab'; var addTabMethod = 'addTab';
var removeTabMethod = 'removeTab'; var removeTabMethod = 'removeTab';
@ -289,11 +319,23 @@ var TreeStyleTabService = {
this.setTabValue(aTab, this.kID, id); this.setTabValue(aTab, this.kID, id);
aTab.__treestyletab__linkedTabBrowser = aTabBrowser; aTab.__treestyletab__linkedTabBrowser = aTabBrowser;
this.initTabTwisty(aTab);
var event = document.createEvent('Events'); var event = document.createEvent('Events');
event.initEvent('TreeStyleTab:TabOpen', true, false); event.initEvent('TreeStyleTab:TabOpen', true, false);
aTab.dispatchEvent(event); aTab.dispatchEvent(event);
}, },
initTabTwisty : function(aTab)
{
if (document.getAnonymousElementByAttribute(aTab, 'class', this.kTWISTY)) return;
var twisty = document.createElement('toolbarbutton');
twisty.setAttribute('class', this.kTWISTY);
var icon = document.getAnonymousElementByAttribute(aTab, 'class', 'tab-icon');
icon.appendChild(twisty);
},
destroy : function() destroy : function()
{ {
this.destroyTabBrowser(gBrowser); this.destroyTabBrowser(gBrowser);
@ -328,6 +370,9 @@ var TreeStyleTabService = {
aTabBrowser.mTabContainer.removeEventListener('TabClose', this, true); aTabBrowser.mTabContainer.removeEventListener('TabClose', this, true);
aTabBrowser.mTabContainer.removeEventListener('TabMove', this, true); aTabBrowser.mTabContainer.removeEventListener('TabMove', this, true);
aTabBrowser.mTabContainer.removeEventListener('SSTabRestoring', this, true); aTabBrowser.mTabContainer.removeEventListener('SSTabRestoring', this, true);
aTabBrowser.mTabContainer.removeEventListener('click', this, true);
aTabBrowser.mTabContainer.removeEventListener('mousedown', this, true);
aTabBrowser.mTabContainer.removeEventListener('select', this, true);
aTabBrowser.mPanelContainer.removeEventListener('click', this, true); aTabBrowser.mPanelContainer.removeEventListener('click', this, true);
// var tabContextMenu = document.getAnonymousElementByAttribute(aTabBrowser, 'anonid', 'tabContextMenu'); // var tabContextMenu = document.getAnonymousElementByAttribute(aTabBrowser, 'anonid', 'tabContextMenu');
@ -347,25 +392,30 @@ var TreeStyleTabService = {
{ {
case 'TreeStyleTab:TabOpen': case 'TreeStyleTab:TabOpen':
this.onTabAdded(aEvent); this.onTabAdded(aEvent);
break; return;
case 'TabClose': case 'TabClose':
this.onTabRemoved(aEvent); this.onTabRemoved(aEvent);
break; return;
case 'TabMove': case 'TabMove':
var tab = aEvent.originalTarget; var tab = aEvent.originalTarget;
this.initTabTwisty(tab); // twisty vanished after the tab is moved!!
var b = this.getTabBrowserFromChildren(tab); var b = this.getTabBrowserFromChildren(tab);
if (tab.getAttribute(this.kCHILDREN) && !b.__treestyletab__isSubTreeMoving) { if (tab.getAttribute(this.kCHILDREN) && !b.__treestyletab__isSubTreeMoving) {
this.moveTabSubTreeTo(tab, tab._tPos); this.moveTabSubTreeTo(tab, tab._tPos);
} }
break; return;
case 'SSTabRestoring': case 'SSTabRestoring':
this.onTabRestored(aEvent); this.onTabRestored(aEvent);
break; return;
case 'click': case 'click':
if (aEvent.target.ownerDocument == document) {
this.onTabClick(aEvent);
return;
}
var isMiddleClick = ( var isMiddleClick = (
aEvent.button == 1 || aEvent.button == 1 ||
aEvent.button == 0 && (aEvent.ctrlKey || aEvent.metaKey) aEvent.button == 0 && (aEvent.ctrlKey || aEvent.metaKey)
@ -380,27 +430,40 @@ var TreeStyleTabService = {
b.__treestyletab__readyToAdoptNewTab = true; b.__treestyletab__readyToAdoptNewTab = true;
b.__treestyletab__parentTab = b.selectedTab.getAttribute(this.kID); b.__treestyletab__parentTab = b.selectedTab.getAttribute(this.kID);
} }
break; return;
case 'mousedown':
this.onTabMouseDown(aEvent);
return;
case 'select':
var b = this.getTabBrowserFromChildren(aEvent.currentTarget);
var tab = b.selectedTab
var p;
if (tab.getAttribute(this.kCOLLAPSED) && (p = this.getParentTabOf(tab))) {
b.selectedTab = p;
}
return;
case 'load': case 'load':
this.init(); this.init();
break; return;
case 'unload': case 'unload':
this.destroy(); this.destroy();
break; return;
case 'popupshowing': case 'popupshowing':
// this.showHideMenuItems(aEvent.target); // this.showHideMenuItems(aEvent.target);
break; return;
case 'SubBrowserAdded': case 'SubBrowserAdded':
this.initTabBrowser(aEvent.originalTarget.browser); this.initTabBrowser(aEvent.originalTarget.browser);
break; return;
case 'SubBrowserRemoveRequest': case 'SubBrowserRemoveRequest':
this.destroyTabBrowser(aEvent.originalTarget.browser); this.destroyTabBrowser(aEvent.originalTarget.browser);
break; return;
} }
}, },
@ -423,6 +486,15 @@ var TreeStyleTabService = {
{ {
var tab = aEvent.originalTarget; var tab = aEvent.originalTarget;
var b = this.getTabBrowserFromChildren(tab); var b = this.getTabBrowserFromChildren(tab);
if (tab.getAttribute(this.kSUBTREE_COLLAPSED)) {
var descendant = this.getDescendantTabsOf(tab);
for (var i = descendant.length-1; i > -1; i--)
{
b.removeTab(descendant[i]);
}
}
var firstChild = this.getFirstChildTabOf(tab); var firstChild = this.getFirstChildTabOf(tab);
var parentTab = this.getParentTabOf(tab); var parentTab = this.getParentTabOf(tab);
var nextFocusedTab = null; var nextFocusedTab = null;
@ -431,7 +503,7 @@ var TreeStyleTabService = {
this.setTabValue(tab, this.kPARENT, parentTab.getAttribute(this.kID)); this.setTabValue(tab, this.kPARENT, parentTab.getAttribute(this.kID));
var next = this.getNextSiblingTabOf(tab); var next = this.getNextSiblingTabOf(tab);
if (next) if (next)
this.setTabValue(tab, this.kINSERTBEFORE, next.getAttribute(this.kID)); this.setTabValue(tab, this.kINSERT_BEFORE, next.getAttribute(this.kID));
} }
if (firstChild) { if (firstChild) {
@ -490,6 +562,8 @@ var TreeStyleTabService = {
var id = this.getTabValue(tab, this.kID); var id = this.getTabValue(tab, this.kID);
this.setTabValue(tab, this.kID, id); this.setTabValue(tab, this.kID, id);
var isSubTreeCollapsed = (this.getTabValue(tab, this.kSUBTREE_COLLAPSED) == 'true');
var children = this.getTabValue(tab, this.kCHILDREN); var children = this.getTabValue(tab, this.kCHILDREN);
if (children) { if (children) {
children = children.split('|'); children = children.split('|');
@ -504,7 +578,7 @@ var TreeStyleTabService = {
} }
var parent = this.getTabValue(tab, this.kPARENT); var parent = this.getTabValue(tab, this.kPARENT);
var before = this.getTabValue(tab, this.kINSERTBEFORE); var before = this.getTabValue(tab, this.kINSERT_BEFORE);
if (parent && (parent = this.getTabById(parent, b))) { if (parent && (parent = this.getTabById(parent, b))) {
this.adoptTabTo(tab, parent, (before ? this.getTabById(before, b) : null ), true); this.adoptTabTo(tab, parent, (before ? this.getTabById(before, b) : null ), true);
this.deleteTabValue(tab, this.kPARENT); this.deleteTabValue(tab, this.kPARENT);
@ -513,49 +587,34 @@ var TreeStyleTabService = {
else if (children) { else if (children) {
this.updateTabsIndent(tabs); this.updateTabsIndent(tabs);
} }
/*
if (isSubTreeCollapsed) {
this.collapseExpandTabSubTree(tab, isSubTreeCollapsed);
}
*/
},
onTabMouseDown : function(aEvent)
{
if (aEvent.button != 0 ||
!this.isEventFiredOnTwisty(aEvent))
return;
this.getTabFromEvent(aEvent).__treestyletab__preventSelect = true;
}, },
onTabClick : function(aEvent) onTabClick : function(aEvent)
{ {
if (aEvent.button != 0) return; if (aEvent.button != 0 ||
!this.isEventFiredOnTwisty(aEvent))
return;
var tab = this.getTabFromEvent(aEvent); var tab = this.getTabFromEvent(aEvent);
if (tab) { this.collapseExpandTabSubTree(tab, tab.getAttribute(this.kSUBTREE_COLLAPSED) != 'true');
var b = this.getTabBrowserFromChildren(tab);
if (aEvent.shiftKey) {
var tabs = b.mTabContainer.childNodes;
var inSelection = false;
for (var i = 0, maxi = tabs.length; i < maxi; i++)
{
if (tabs[i] == b.selectedTab ||
tabs[i] == tab) {
inSelection = !inSelection;
this.setSelection(tabs[i], true);
}
else {
this.setSelection(tabs[i], inSelection);
}
}
aEvent.preventDefault(); aEvent.preventDefault();
aEvent.stopPropagation(); aEvent.stopPropagation();
return;
}
else if (aEvent.ctrlKey || aEvent.metaKey) {
if (this.tabClickMode != this.TAB_CLICK_MODE_TOGGLE) return;
if (!this.selectionModified && !this.hasSelection())
this.setSelection(b.selectedTab, true);
this.toggleSelection(tab);
aEvent.preventDefault();
aEvent.stopPropagation();
return;
}
}
if (this.selectionModified && !this.hasSelection())
this.selectionModified = false;
this.clearSelection();
}, },
/* Tab Utilities */ /* Tab Utilities */
@ -786,6 +845,21 @@ var TreeStyleTabService = {
if (newIndex > aChild._tPos) newIndex--; if (newIndex > aChild._tPos) newIndex--;
this.moveTabSubTreeTo(aChild, newIndex); this.moveTabSubTreeTo(aChild, newIndex);
if (aParent.getAttribute(this.kSUBTREE_COLLAPSED) == 'true') {
if (this.getPref('extensions.treestyletab.autoExpandSubTreeOnAppendChild')) {
var p = aParent;
do {
this.collapseExpandTabSubTree(p, false);
}
while (p = this.getParentTabOf(p));
}
else
this.collapseExpandTab(aChild, true);
}
if (aParent.getAttribute(this.kCOLLAPSED) == 'true')
this.collapseExpandTab(aChild, true);
if (!aDontUpdateIndent) this.updateTabsIndent([aChild]); if (!aDontUpdateIndent) this.updateTabsIndent([aChild]);
}, },
@ -829,6 +903,8 @@ var TreeStyleTabService = {
moveTabSubTreeTo : function(aTab, aIndex) moveTabSubTreeTo : function(aTab, aIndex)
{ {
if (!aTab) return;
var b = this.getTabBrowserFromChildren(aTab); var b = this.getTabBrowserFromChildren(aTab);
b.__treestyletab__isSubTreeMoving = true; b.__treestyletab__isSubTreeMoving = true;
@ -843,6 +919,40 @@ var TreeStyleTabService = {
b.__treestyletab__isSubTreeMoving = false; b.__treestyletab__isSubTreeMoving = false;
}, },
collapseExpandTabSubTree : function(aTab, aCollapse)
{
if (!aTab) return;
if (aTab.getAttribute(this.kSUBTREE_COLLAPSED) == String(aCollapse)) return;
this.setTabValue(aTab, this.kSUBTREE_COLLAPSED, aCollapse);
var tabs = this.getChildTabsOf(aTab);
for (var i = 0, maxi = tabs.length; i < maxi; i++)
{
this.collapseExpandTab(tabs[i], aCollapse);
}
},
collapseExpandTab : function(aTab, aCollapse)
{
if (!aTab) return;
this.setTabValue(aTab, this.kCOLLAPSED, aCollapse);
var b = this.getTabBrowserFromChildren(aTab);
var p;
if (aTab == b.selectedTab && (p = this.getParentTabOf(aTab))) b.selectedTab = p;
var isSubTreeCollapsed = (aTab.getAttribute(this.kSUBTREE_COLLAPSED) == 'true');
var tabs = this.getChildTabsOf(aTab);
for (var i = 0, maxi = tabs.length; i < maxi; i++)
{
if (!isSubTreeCollapsed)
this.collapseExpandTab(tabs[i], aCollapse);
}
},
/* Pref Listener */ /* Pref Listener */
domain : 'extensions.treestyletab', domain : 'extensions.treestyletab',

View File

@ -1,4 +1,5 @@
pref("extensions.treestyletab.adoptChildrenToGrandParentOnRemoveTab", true); pref("extensions.treestyletab.adoptChildrenToGrandParentOnRemoveTab", true);
pref("extensions.treestyletab.autoExpandSubTreeOnAppendChild", true);
pref("browser.link.open_newwindow.restriction", 0); pref("browser.link.open_newwindow.restriction", 0);

View File

@ -5,6 +5,8 @@
<!ENTITY config.adoptChildrenToGrandParentOnRemoveTab "Liberate child tabs from the tree when the parent tab is closed"> <!ENTITY config.adoptChildrenToGrandParentOnRemoveTab "Liberate child tabs from the tree when the parent tab is closed">
<!ENTITY config.autoExpandSubTreeOnAppendChild "Expand subtree automatically, when tabs are inserted into the subtree">
<!ENTITY config.open_newwindow.caption "New window opened from links in webpages unexpectedly"> <!ENTITY config.open_newwindow.caption "New window opened from links in webpages unexpectedly">
<!ENTITY config.open_newwindow.window "Open as Window"> <!ENTITY config.open_newwindow.window "Open as Window">
<!ENTITY config.open_newwindow.tab "Open as Tab (default)"> <!ENTITY config.open_newwindow.tab "Open as Tab (default)">

View File

@ -5,6 +5,8 @@
<!ENTITY config.adoptChildrenToGrandParentOnRemoveTab "親のタブを閉じたら子孫のタブをツリーから解放する"> <!ENTITY config.adoptChildrenToGrandParentOnRemoveTab "親のタブを閉じたら子孫のタブをツリーから解放する">
<!ENTITY config.autoExpandSubTreeOnAppendChild "畳まれたサブツリー内に子孫のタブが追加された時、サブツリーを自動的に展開する">
<!ENTITY config.open_newwindow.caption "Webページのリンクから勝手に開かれたウィンドウの制御"> <!ENTITY config.open_newwindow.caption "Webページのリンクから勝手に開かれたウィンドウの制御">
<!ENTITY config.open_newwindow.window "ウィンドウで開く"> <!ENTITY config.open_newwindow.window "ウィンドウで開く">
<!ENTITY config.open_newwindow.tab "タブで開く(初期値)"> <!ENTITY config.open_newwindow.tab "タブで開く(初期値)">

View File

@ -1,2 +1,27 @@
@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"); @namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
.treestyletab-tab-tree-twisty {
-moz-appearance: none;
margin: -2px -3px -2px -4px;
padding: 1px;
width: auto;
height: auto;
border: none;
outline: none;
background: none;
list-style-image: url("tree-twisty-expanded.png");
}
tab[treestyletab-subtree-collapsed="true"] .treestyletab-tab-tree-twisty {
list-style-image: url("tree-twisty-collapsed.png");
}
tab:not([treestyletab-children]) .treestyletab-tab-tree-twisty {
display: none;
}
tab[treestyletab-tab-collapsed="true"] {
visibility: collapse;
}