From 78dc5ef23ce50cdb5244f8d7e7a7117bb7bf89f4 Mon Sep 17 00:00:00 2001 From: Piro / SHIMODA Hiroshi Date: Thu, 2 Dec 2010 09:00:39 +0900 Subject: [PATCH] refactor codes for drag and drop --- content/treestyletab/hacks.js | 8 +- content/treestyletab/treestyletab.js | 4 +- content/treestyletab/treestyletabbrowser.js | 495 +---------------- .../treestyletabbrowser_tabbarDNDObserver.js | 511 +++++++++++++++++- 4 files changed, 515 insertions(+), 503 deletions(-) diff --git a/content/treestyletab/hacks.js b/content/treestyletab/hacks.js index d8dd443f..3dc39bfe 100644 --- a/content/treestyletab/hacks.js +++ b/content/treestyletab/hacks.js @@ -421,7 +421,7 @@ TreeStyleTabService.overrideExtensionsOnInitAfter = function TSTService_override eval('TabDNDObserver.clearDragmark = '+ TabDNDObserver.clearDragmark.toSource().replace( /(\})(\))?$/, - 'gBrowser.treeStyleTab.clearDropPosition(); $1$2' + 'gBrowser.treeStyleTab.tabbarDNDObserver.clearDropPosition(); $1$2' ) ); if (TabDNDObserver.canDrop) { @@ -475,13 +475,13 @@ TreeStyleTabService.overrideExtensionsOnInitAfter = function TSTService_override ).replace( /(var newIndex =)/, ).replace( /(aTab = gBrowser.addTab\(url\));/, ).replace( @@ -493,7 +493,7 @@ TreeStyleTabService.overrideExtensionsOnInitAfter = function TSTService_override dropActionInfo.position != TreeStyleTabService.kDROP_ON || (TreeStyleTabService.dropLinksOnTabBehavior() & TreeStyleTabService.kDROPLINK_NEWTAB) ) { - TSTTabBrowser.treeStyleTab.performDrop(dropActionInfo, TSTTabBrowser.loadOneTab(url, null, null, null, bgLoad, false)); + TSTTabBrowser.treeStyleTab.tabbarDNDObserver.performDrop(dropActionInfo, TSTTabBrowser.loadOneTab(url, null, null, null, bgLoad, false)); return; } ]]> diff --git a/content/treestyletab/treestyletab.js b/content/treestyletab/treestyletab.js index 729f559c..d8c6743c 100644 --- a/content/treestyletab/treestyletab.js +++ b/content/treestyletab/treestyletab.js @@ -1581,7 +1581,7 @@ var TreeStyleTabService = { if (remoteService.hasChildTabs(remoteTab) || (remoteMultipleTabService && remoteMultipleTabService.isSelected(remoteTab))) { var remoteBrowser = remoteService.getTabBrowserFromChild(remoteTab); - if (remoteBrowser.treeStyleTab.isDraggingAllTabs(remoteTab)) { + if (remoteBrowser.treeStyleTab.tabbarDNDObserver.isDraggingAllTabs(remoteTab)) { window.close(); } else { @@ -1590,7 +1590,7 @@ var TreeStyleTabService = { }; window.setTimeout(function() { var blankTab = gBrowser.selectedTab; - gBrowser.treeStyleTab.performDrop(actionInfo, remoteTab); + gBrowser.treeStyleTab.tabbarDNDObserver.performDrop(actionInfo, remoteTab); window.setTimeout(function() { gBrowser.removeTab(blankTab); diff --git a/content/treestyletab/treestyletabbrowser.js b/content/treestyletab/treestyletabbrowser.js index 74acf466..453ea425 100644 --- a/content/treestyletab/treestyletabbrowser.js +++ b/content/treestyletab/treestyletabbrowser.js @@ -85,6 +85,16 @@ TreeStyleTabBrowser.prototype = { return (this._tabStripPlaceHolder = value); }, + get tabbarDNDObserver() + { + return this._tabbarDNDObserver || (this._tabbarDNDObserver = new TreeStyleTabBrowserTabbarDNDObserver(this)); + }, + + get panelDNDObserver() + { + return this._panelDNDObserver || (this._panelDNDObserver = new TreeStyleTabBrowserTabpanelDNDObserver(this)); + }, + /* utils */ /* get tab contents */ @@ -2024,7 +2034,7 @@ TreeStyleTabBrowser.prototype = { aEvent.preventDefault(); return; - // cancel tab dragging by Multiple Tab + // cancel tab dragging by Multiple Tab case 'MultipleTabHandler:TabsDragStart': return aEvent.preventDefault(); } @@ -3296,487 +3306,6 @@ TreeStyleTabBrowser.prototype = { this.removeTabbrowserAttribute(this.kPRINT_PREVIEW); }, -/* drag and drop */ - - get tabbarDNDObserver() - { - return this._tabbarDNDObserver || (this._tabbarDNDObserver = new TreeStyleTabBrowserTabbarDNDObserver(this)); - }, - - get panelDNDObserver() - { - return this._panelDNDObserver || (this._panelDNDObserver = new TreeStyleTabBrowserTabpanelDNDObserver(this)); - }, - - getCurrentDragSession : function TSTBrowser_getCurrentDragSession() - { - return Components - .classes['@mozilla.org/widget/dragservice;1'] - .getService(Components.interfaces.nsIDragService) - .getCurrentSession(); - }, - - getDropAction : function TSTBrowser_getDropAction(aEvent, aDragSession) - { - if (!aDragSession) - aDragSession = this.getCurrentDragSession(); - - var tab = aDragSession ? this.getTabFromChild(aDragSession.sourceNode) : null ; - this.ensureTabInitialized(tab); - - var info = this.getDropActionInternal(aEvent, tab); - info.canDrop = true; - info.source = tab; - if (tab) { - var isCopy = this.isCopyAction(aEvent); - if (isCopy && 'duplicateTab' in this.mTabBrowser) { - info.action |= this.kACTION_DUPLICATE; - } - if ( - !isCopy && - this.getTabBrowserFromChild(tab) != this.mTabBrowser && - ( - ('duplicateTab' in this.mTabBrowser) || - ('swapBrowsersAndCloseOther' in this.mTabBrowser) - ) - ) { - info.action |= this.kACTION_IMPORT; - } - - if (info.action & this.kACTIONS_FOR_DESTINATION) { - if (info.action & this.kACTION_MOVE) info.action ^= this.kACTION_MOVE; - if (info.action & this.kACTION_STAY) info.action ^= this.kACTION_STAY; - } - - if (info.action & this.kACTION_ATTACH) { - if (info.parent == tab) { - info.canDrop = false; - } - else { - var orig = tab; - tab = info.target; - while (tab = this.getParentTab(tab)) - { - if (tab != orig) continue; - info.canDrop = false; - break; - } - } - } - } - return info; - }, - - getDropActionInternal : function TSTBrowser_getDropActionInternal(aEvent, aSourceTab) - { - var tab = aEvent.target; - var b = this.mTabBrowser; - var tabs = this.getTabsArray(b); - var firstTab = this.getFirstNormalTab(b); - var lastTabIndex = tabs.length -1; - var isInverted = this.isVertical ? false : window.getComputedStyle(b.parentNode, null).direction == 'rtl'; - var info = { - target : null, - position : null, - action : null, - parent : null, - insertBefore : null, - event : aEvent - }; - - var isTabMoveFromOtherWindow = aSourceTab && aSourceTab.ownerDocument != document; - var isNewTabAction = !aSourceTab || aSourceTab.ownerDocument != document; - - if (tab.localName != 'tab') { - var action = isTabMoveFromOtherWindow ? this.kACTION_STAY : (this.kACTION_MOVE | this.kACTION_PART) ; - if (isNewTabAction) action |= this.kACTION_NEWTAB; - if (aEvent[this.positionProp] < firstTab.boxObject[this.positionProp]) { - info.target = info.parent = info.insertBefore = firstTab; - info.position = isInverted ? this.kDROP_AFTER : this.kDROP_BEFORE ; - info.action = action; - return info; - } - else if (aEvent[this.positionProp] > tabs[lastTabIndex].boxObject[this.positionProp] + tabs[lastTabIndex].boxObject[this.sizeProp]) { - info.target = info.parent = tabs[lastTabIndex]; - info.position = isInverted ? this.kDROP_BEFORE : this.kDROP_AFTER ; - info.action = action; - return info; - } - else { - let index = b.getNewIndex ? - b.getNewIndex(aEvent) : - b.tabContainer._getDropIndex(aEvent) ; - info.target = tabs[Math.min(index, lastTabIndex)]; - } - } - else { - this.ensureTabInitialized(tab); - info.target = tab; - } - - var positionProp = this.isVertical && tab.getAttribute('pinned') == 'true' ? this.invertedPositionProp : this.positionProp ; - var sizeProp = this.isVertical && tab.getAttribute('pinned') == 'true' ? this.invertedSizeProp : this.sizeProp ; - var boxPos = tab.boxObject[positionProp]; - var boxUnit = Math.round(tab.boxObject[sizeProp] / 3); - if (aEvent[positionProp] < boxPos + boxUnit) { - info.position = isInverted ? this.kDROP_AFTER : this.kDROP_BEFORE ; - } - else if (aEvent[positionProp] > boxPos + boxUnit + boxUnit) { - info.position = isInverted ? this.kDROP_BEFORE : this.kDROP_AFTER ; - } - else { - info.position = this.kDROP_ON; - } - - switch (info.position) - { - case this.kDROP_ON: - info.action = this.kACTION_STAY | this.kACTION_ATTACH; - info.parent = tab; - var visible = this.getNextVisibleTab(tab); - info.insertBefore = this.getTreePref('insertNewChildAt') == this.kINSERT_FISRT ? - (this.getFirstChildTab(tab) || visible) : - (this.getNextSiblingTab(tab) || this.getNextTab(this.getLastDescendantTab(tab)) || visible); - break; - - case this.kDROP_BEFORE: -/* - [TARGET ] Şpart from parent, and move - - [ ] - [TARGET ] Şattach to the parent of the target, and move - - [ ] - [TARGET ] Şattach to the parent of the target, and move - - [ ] - [TARGET] Şattach to the parent of the target (previous tab), and move -*/ - var prevTab = this.getPreviousVisibleTab(tab); - if (!prevTab) { - info.action = this.kACTION_MOVE | this.kACTION_PART; - info.insertBefore = firstTab; - } - else { - var prevLevel = Number(prevTab.getAttribute(this.kNEST)); - var targetNest = Number(tab.getAttribute(this.kNEST)); - info.parent = (prevLevel < targetNest) ? prevTab : this.getParentTab(tab) ; - info.action = this.kACTION_MOVE | (info.parent ? this.kACTION_ATTACH : this.kACTION_PART ); - info.insertBefore = tab; - } - break; - - case this.kDROP_AFTER: -/* - [TARGET ] Ğif the target has a parent, attach to it and and move - - [TARGET] Ğattach to the parent of the target, and move - [ ] - - [TARGET ] Ğattach to the parent of the target, and move - [ ] - - [TARGET ] Ğattach to the target, and move - [ ] -*/ - var nextTab = this.getNextVisibleTab(tab); - if (!nextTab) { - info.action = this.kACTION_MOVE | this.kACTION_ATTACH; - info.parent = this.getParentTab(tab); - } - else { - var targetNest = Number(tab.getAttribute(this.kNEST)); - var nextLevel = Number(nextTab.getAttribute(this.kNEST)); - info.parent = (targetNest < nextLevel) ? tab : this.getParentTab(tab) ; - info.action = this.kACTION_MOVE | (info.parent ? this.kACTION_ATTACH : this.kACTION_PART ); - info.insertBefore = nextTab; -/* - [TARGET ] Ğattach dragged tab to the parent of the target as its next sibling - [DRAGGED] -*/ - if (aSourceTab == nextTab && this.getDescendantTabs(info.parent).length == 1) { - info.action = this.kACTION_MOVE | this.kACTION_ATTACH; - info.parent = this.getParentTab(tab); - info.insertBefore = this.getNextTab(nextTab); - } - } - break; - } - - if (isNewTabAction) action |= this.kACTION_NEWTAB; - - return info; - }, - - performDrop : function TSTBrowser_performDrop(aInfo, aDraggedTab) - { - var tabsInfo = this.getDraggedTabsInfoFromOneTab(aInfo, aDraggedTab); - if (!tabsInfo.draggedTab) return false; - - aDraggedTab = tabsInfo.draggedTab; - var draggedTabs = tabsInfo.draggedTabs; - var draggedRoots = tabsInfo.draggedRoots; - - - var targetBrowser = this.mTabBrowser; - var tabs = this.getTabsArray(targetBrowser); - - var sourceWindow = aDraggedTab.ownerDocument.defaultView; - var sourceBrowser = this.getTabBrowserFromChild(aDraggedTab); - - var draggedWholeTree = [].concat(draggedRoots); - draggedRoots.forEach(function(aRoot) { - draggedWholeTree = draggedWholeTree.concat(this.getDescendantTabs(aRoot)); - }, this); - while (aInfo.insertBefore && draggedWholeTree.indexOf(aInfo.insertBefore) > -1) - { - aInfo.insertBefore = this.getNextTab(aInfo.insertBefore); - } - - if (aInfo.action & this.kACTIONS_FOR_SOURCE) { - if (aInfo.action & this.kACTION_PART) { - this.partTabsOnDrop(draggedRoots); - } - else if (aInfo.action & this.kACTION_ATTACH) { - this.attachTabsOnDrop(draggedRoots, aInfo.parent); - } - else { - return false; - } - - if ( // if this move will cause no change... - sourceBrowser == targetBrowser && - sourceBrowser.treeStyleTab.getNextVisibleTab(draggedTabs[draggedTabs.length-1]) == aInfo.insertBefore - ) { - // then, do nothing - return true; - } - } - - - // prevent Multiple Tab Handler feature - targetBrowser.duplicatingSelectedTabs = true; - targetBrowser.movingSelectedTabs = true; - - - var newRoots = []; - var shouldClose = ( - aInfo.action & this.kACTION_IMPORT && - this.getAllTabsArray(sourceBrowser).length == draggedTabs.length - ); - var oldTabs = []; - var newTabs = []; - var treeStructure = draggedTabs.map(function(aTab) { - var parent = sourceBrowser.treeStyleTab.getParentTab(aTab); - return parent ? draggedTabs.indexOf(parent) : -1 ; - }); - - var parentTabsArray = draggedTabs.map(function(aTab) { - return (aInfo.action & this.kACTIONS_FOR_DESTINATION) ? - sourceBrowser.treeStyleTab.getParentTab(aTab) : null ; - }, this); - - // Firefox fails to "move" collapsed tabs. So, expand them first - // and collapse them after they are moved. - var collapseExpandState = []; - if (aInfo.action & this.kACTION_IMPORT && - 'swapBrowsersAndCloseOther' in targetBrowser) { - draggedWholeTree.forEach(function(aTab) { - collapseExpandState.push(this.getTabValue(aTab, this.kSUBTREE_COLLAPSED) == 'true'); - this.collapseExpandSubtree(aTab, false, true); - this.collapseExpandTab(aTab, false, true); - }, this); - } - - var lastTabIndex = tabs.length -1; - draggedTabs.forEach(function(aTab, aIndex) { - var tab = aTab; - if (aInfo.action & this.kACTIONS_FOR_DESTINATION) { - var parent = parentTabsArray[aIndex]; - if (tabsInfo.isMultipleMove && 'MultipleTabService' in sourceWindow) - sourceWindow.MultipleTabService.setSelection(aTab, false); - if (aInfo.action & this.kACTION_IMPORT && - 'swapBrowsersAndCloseOther' in targetBrowser) { - tab = targetBrowser.addTab(); - tab.linkedBrowser.stop(); - tab.linkedBrowser.docShell; - targetBrowser.swapBrowsersAndCloseOther(tab, aTab); - targetBrowser.setTabTitle(tab); - } - else { - tab = targetBrowser.duplicateTab(aTab); - this.deleteTabValue(tab, this.kCHILDREN); - this.deleteTabValue(tab, this.kPARENT); - if (aInfo.action & this.kACTION_IMPORT) - oldTabs.push(aTab); - } - newTabs.push(tab); - if (tabsInfo.isMultipleMove && 'MultipleTabService' in window) - MultipleTabService.setSelection(tab, true); - if (!parent || draggedTabs.indexOf(parent) < 0) - newRoots.push(tab); - lastTabIndex++; - } - - var newIndex = aInfo.insertBefore ? aInfo.insertBefore._tPos : lastTabIndex ; - if (aInfo.insertBefore && newIndex > tab._tPos) newIndex--; - - this.internallyTabMovingCount++; - targetBrowser.moveTabTo(tab, newIndex); - this.collapseExpandTab(tab, false, true); - this.internallyTabMovingCount--; - - }, this); - - // close imported tabs from the source browser - oldTabs.forEach(function(aTab) { - sourceBrowser.removeTab(aTab, { animate : true }); - }); - if (shouldClose) this.closeOwner(sourceBrowser); - - // restore tree structure for newly opened tabs - newTabs.forEach(function(aTab, aIndex) { - var index = treeStructure[aIndex]; - if (index < 0) return; - targetBrowser.treeStyleTab.attachTabTo(aTab, newTabs[index]); - }); - newTabs.reverse(); - collapseExpandState.reverse(); - collapseExpandState.forEach(function(aCollapsed, aIndex) { - this.collapseExpandSubtree(newTabs[aIndex], aCollapsed, true); - }, this); - - if (aInfo.action & this.kACTIONS_FOR_DESTINATION && - aInfo.action & this.kACTION_ATTACH) - this.attachTabsOnDrop(newRoots, aInfo.parent); - - // Multiple Tab Handler - targetBrowser.movingSelectedTabs = false; - targetBrowser.duplicatingSelectedTabs = false; - - return true; - }, - - getDraggedTabsInfoFromOneTab : function TSTBrowser_getDraggedTabsInfoFromOneTab(aInfo, aTab) - { - aTab = this.getTabFromChild(aTab); - if (!aTab) - return { - draggedTab : null, - draggedTabs : [], - draggedRoots : [], - isMultipleMove : false - }; - - var targetBrowser = this.mTabBrowser; - var tabs = this.getTabsArray(targetBrowser); - - var sourceWindow = aTab.ownerDocument.defaultView; - var sourceBrowser = this.getTabBrowserFromChild(aTab); - - var draggedTabs = window['piro.sakura.ne.jp'].tabsDragUtils.getSelectedTabs(aInfo.event || sourceBrowser); - var draggedRoots = [aTab]; - var isMultipleMove = false; - - if (draggedTabs.length > 1) { - isMultipleMove = true; - if (!(aInfo.action & this.kACTIONS_FOR_DESTINATION)) { - draggedRoots = []; - draggedTabs.forEach(function(aTab) { - var parent = aTab, - current; - do { - current = parent; - parent = sourceBrowser.treeStyleTab.getParentTab(parent) - if (parent && draggedTabs.indexOf(parent) > -1) continue; - draggedRoots.push(current); - return; - } - while (parent); - }, this); - } - } - else if (aInfo.action & this.kACTIONS_FOR_DESTINATION) { - draggedTabs = [aTab].concat(sourceBrowser.treeStyleTab.getDescendantTabs(aTab)); - } - - return { - draggedTab : aTab, - draggedTabs : draggedTabs, - draggedRoots : draggedRoots, - isMultipleMove : isMultipleMove - }; - }, - - attachTabsOnDrop : function TSTBrowser_attachTabsOnDrop(aTabs, aParent) - { - this.mTabBrowser.movingSelectedTabs = true; // Multiple Tab Handler - aTabs.forEach(function(aTab) { - if (!aTab.parentNode) return; // ignore removed tabs - if (aParent) - this.attachTabTo(aTab, aParent); - else - this.partTab(aTab); - this.collapseExpandTab(aTab, false); - }, this); - this.mTabBrowser.movingSelectedTabs = false; // Multiple Tab Handler - }, - - partTabsOnDrop : function TSTBrowser_partTabsOnDrop(aTabs) - { - this.mTabBrowser.movingSelectedTabs = true; // Multiple Tab Handler - aTabs.forEach(function(aTab) { - if (!aTab.parentNode) return; // ignore removed tabs - this.partTab(aTab); - this.collapseExpandTab(aTab, false); - }, this); - this.mTabBrowser.movingSelectedTabs = false; // Multiple Tab Handler - }, - - closeOwner : function TSTBrowser_closeOwner(aTabOwner) - { - var w = aTabOwner.ownerDocument.defaultView; - if (!w) return; - if ('SplitBrowser' in w) { - if ('getSubBrowserFromChild' in w.SplitBrowser) { - var subbrowser = w.SplitBrowser.getSubBrowserFromChild(aTabOwner); - if (subbrowser) { - subbrowser.close(); - return; - } - } - if (w.SplitBrowser.browsers.length) return; - } - w.close(); - }, - - clearDropPosition : function TSTBrowser_clearDropPosition() - { - var b = this.mTabBrowser; - var xpathResult = this.evaluateXPath( - 'child::xul:tab[@'+this.kDROP_POSITION+']', - b.mTabContainer - ); - for (var i = 0, maxi = xpathResult.snapshotLength; i < maxi; i++) - { - xpathResult.snapshotItem(i).removeAttribute(this.kDROP_POSITION); - } - }, - - isDraggingAllTabs : function TSTBrowser_isDraggingAllTabs(aTab, aTabs) - { - var actionInfo = { - action : this.kACTIONS_FOR_DESTINATION | this.kACTION_IMPORT - }; - var tabsInfo = this.getDraggedTabsInfoFromOneTab(actionInfo, aTab); - return tabsInfo.draggedTabs.length == (aTabs || this.getAllTabsArray(this.mTabBrowser)).length; - }, - - isDraggingAllCurrentTabs : function TSTBrowser_isDraggingAllCurrentTabs(aTab) - { - return this.isDraggingAllTabs(aTab, this.getTabsArray(this.mTabBrowser)); - }, - /* commands */ /* reset */ @@ -4334,7 +3863,7 @@ TreeStyleTabBrowser.prototype = { this.updateTabsCount(parent); }, - promoteTooDeepLevelTabs : function TSTBrowser_promoteTooDeepLevelTabs(aParent) + promoteTooDeepLevelTabs : function TSTBrowser_promoteTooDeepLevelTabs(aParent) { if (this.maxTreeLevel < 0 || !this.maxTreeLevelPhisical) return; diff --git a/content/treestyletab/treestyletabbrowser_tabbarDNDObserver.js b/content/treestyletab/treestyletabbrowser_tabbarDNDObserver.js index 3fa86893..3bcaf6ec 100644 --- a/content/treestyletab/treestyletabbrowser_tabbarDNDObserver.js +++ b/content/treestyletab/treestyletabbrowser_tabbarDNDObserver.js @@ -95,10 +95,10 @@ TreeStyleTabBrowserTabbarDNDObserver.prototype = { tooltip.popupBoxObject.popupState != 'closed') tooltip.hidePopup(); - var dropAction = sv.getDropAction(aEvent); + var dropAction = this.getDropAction(aEvent); if ('dataTransfer' in aEvent) { var dt = aEvent.dataTransfer; - if (dropAction.action & this.kACTION_NEWTAB) { + if (dropAction.action & sv.kACTION_NEWTAB) { dt.effectAllowed = dt.dropEffect = ( !dropAction.source ? 'link' : sv.isCopyAction(aEvent) ? 'copy' : @@ -127,7 +127,7 @@ try{ if (sv.isCollapsed(tab)) return false; - var info = sv.getDropAction(aEvent, session); + var info = this.getDropAction(aEvent, session); return info.canDrop; } catch(e) { @@ -136,6 +136,489 @@ catch(e) { } }, + getDropAction : function TSTTabbarDND_getDropAction(aEvent, aDragSession) + { + var sv = this.mOwner; + var b = sv.mTabBrowser; + + if (!aDragSession) + aDragSession = sv.getCurrentDragSession(); + + var tab = aDragSession ? sv.getTabFromChild(aDragSession.sourceNode) : null ; + sv.ensureTabInitialized(tab); + + var info = this.getDropActionInternal(aEvent, tab); + info.canDrop = true; + info.source = tab; + if (tab) { + var isCopy = sv.isCopyAction(aEvent); + if (isCopy && 'duplicateTab' in b) { + info.action |= sv.kACTION_DUPLICATE; + } + if ( + !isCopy && + sv.getTabBrowserFromChild(tab) != b && + ( + ('duplicateTab' in b) || + ('swapBrowsersAndCloseOther' in b) + ) + ) { + info.action |= sv.kACTION_IMPORT; + } + + if (info.action & sv.kACTIONS_FOR_DESTINATION) { + if (info.action & sv.kACTION_MOVE) info.action ^= sv.kACTION_MOVE; + if (info.action & sv.kACTION_STAY) info.action ^= sv.kACTION_STAY; + } + + if (info.action & sv.kACTION_ATTACH) { + if (info.parent == tab) { + info.canDrop = false; + } + else { + var orig = tab; + tab = info.target; + while (tab = sv.getParentTab(tab)) + { + if (tab != orig) continue; + info.canDrop = false; + break; + } + } + } + } + return info; + }, + + getDropActionInternal : function TSTTabbarDND_getDropActionInternal(aEvent, aSourceTab) + { + var sv = this.mOwner; + var b = sv.mTabBrowser; + + var tab = aEvent.target; + var tabs = sv.getTabsArray(b); + var firstTab = sv.getFirstNormalTab(b); + var lastTabIndex = tabs.length -1; + var isInverted = sv.isVertical ? false : window.getComputedStyle(b.parentNode, null).direction == 'rtl'; + var info = { + target : null, + position : null, + action : null, + parent : null, + insertBefore : null, + event : aEvent + }; + + var isTabMoveFromOtherWindow = aSourceTab && aSourceTab.ownerDocument != document; + var isNewTabAction = !aSourceTab || aSourceTab.ownerDocument != document; + + if (tab.localName != 'tab') { + var action = isTabMoveFromOtherWindow ? sv.kACTION_STAY : (sv.kACTION_MOVE | sv.kACTION_PART) ; + if (isNewTabAction) action |= sv.kACTION_NEWTAB; + if (aEvent[sv.positionProp] < firstTab.boxObject[sv.positionProp]) { + info.target = info.parent = info.insertBefore = firstTab; + info.position = isInverted ? sv.kDROP_AFTER : sv.kDROP_BEFORE ; + info.action = action; + return info; + } + else if (aEvent[sv.positionProp] > tabs[lastTabIndex].boxObject[sv.positionProp] + tabs[lastTabIndex].boxObject[sv.sizeProp]) { + info.target = info.parent = tabs[lastTabIndex]; + info.position = isInverted ? sv.kDROP_BEFORE : sv.kDROP_AFTER ; + info.action = action; + return info; + } + else { + let index = b.getNewIndex ? + b.getNewIndex(aEvent) : + b.tabContainer._getDropIndex(aEvent) ; + info.target = tabs[Math.min(index, lastTabIndex)]; + } + } + else { + sv.ensureTabInitialized(tab); + info.target = tab; + } + + var positionProp = sv.isVertical && tab.getAttribute('pinned') == 'true' ? sv.invertedPositionProp : sv.positionProp ; + var sizeProp = sv.isVertical && tab.getAttribute('pinned') == 'true' ? sv.invertedSizeProp : sv.sizeProp ; + var boxPos = tab.boxObject[positionProp]; + var boxUnit = Math.round(tab.boxObject[sizeProp] / 3); + if (aEvent[positionProp] < boxPos + boxUnit) { + info.position = isInverted ? sv.kDROP_AFTER : sv.kDROP_BEFORE ; + } + else if (aEvent[positionProp] > boxPos + boxUnit + boxUnit) { + info.position = isInverted ? sv.kDROP_BEFORE : sv.kDROP_AFTER ; + } + else { + info.position = sv.kDROP_ON; + } + + switch (info.position) + { + case sv.kDROP_ON: + info.action = sv.kACTION_STAY | sv.kACTION_ATTACH; + info.parent = tab; + var visible = sv.getNextVisibleTab(tab); + info.insertBefore = sv.getTreePref('insertNewChildAt') == sv.kINSERT_FISRT ? + (sv.getFirstChildTab(tab) || visible) : + (sv.getNextSiblingTab(tab) || sv.getNextTab(sv.getLastDescendantTab(tab)) || visible); + break; + + case sv.kDROP_BEFORE: +/* + [TARGET ] Şpart from parent, and move + + [ ] + [TARGET ] Şattach to the parent of the target, and move + + [ ] + [TARGET ] Şattach to the parent of the target, and move + + [ ] + [TARGET] Şattach to the parent of the target (previous tab), and move +*/ + var prevTab = sv.getPreviousVisibleTab(tab); + if (!prevTab) { + info.action = sv.kACTION_MOVE | sv.kACTION_PART; + info.insertBefore = firstTab; + } + else { + var prevLevel = Number(prevTab.getAttribute(sv.kNEST)); + var targetNest = Number(tab.getAttribute(sv.kNEST)); + info.parent = (prevLevel < targetNest) ? prevTab : sv.getParentTab(tab) ; + info.action = sv.kACTION_MOVE | (info.parent ? sv.kACTION_ATTACH : sv.kACTION_PART ); + info.insertBefore = tab; + } + break; + + case sv.kDROP_AFTER: +/* + [TARGET ] Ğif the target has a parent, attach to it and and move + + [TARGET] Ğattach to the parent of the target, and move + [ ] + + [TARGET ] Ğattach to the parent of the target, and move + [ ] + + [TARGET ] Ğattach to the target, and move + [ ] +*/ + var nextTab = sv.getNextVisibleTab(tab); + if (!nextTab) { + info.action = sv.kACTION_MOVE | sv.kACTION_ATTACH; + info.parent = sv.getParentTab(tab); + } + else { + var targetNest = Number(tab.getAttribute(sv.kNEST)); + var nextLevel = Number(nextTab.getAttribute(sv.kNEST)); + info.parent = (targetNest < nextLevel) ? tab : sv.getParentTab(tab) ; + info.action = sv.kACTION_MOVE | (info.parent ? sv.kACTION_ATTACH : sv.kACTION_PART ); + info.insertBefore = nextTab; +/* + [TARGET ] Ğattach dragged tab to the parent of the target as its next sibling + [DRAGGED] +*/ + if (aSourceTab == nextTab && sv.getDescendantTabs(info.parent).length == 1) { + info.action = sv.kACTION_MOVE | sv.kACTION_ATTACH; + info.parent = sv.getParentTab(tab); + info.insertBefore = sv.getNextTab(nextTab); + } + } + break; + } + + if (isNewTabAction) action |= sv.kACTION_NEWTAB; + + return info; + }, + + performDrop : function TSTTabbarDND_performDrop(aInfo, aDraggedTab) + { + var sv = this.mOwner; + var b = sv.mTabBrowser; + + var tabsInfo = this.getDraggedTabsInfoFromOneTab(aInfo, aDraggedTab); + if (!tabsInfo.draggedTab) return false; + + aDraggedTab = tabsInfo.draggedTab; + var draggedTabs = tabsInfo.draggedTabs; + var draggedRoots = tabsInfo.draggedRoots; + + + var targetBrowser = b; + var tabs = sv.getTabsArray(targetBrowser); + + var sourceWindow = aDraggedTab.ownerDocument.defaultView; + var sourceBrowser = sv.getTabBrowserFromChild(aDraggedTab); + + var draggedWholeTree = [].concat(draggedRoots); + draggedRoots.forEach(function(aRoot) { + draggedWholeTree = draggedWholeTree.concat(sv.getDescendantTabs(aRoot)); + }, this); + while (aInfo.insertBefore && draggedWholeTree.indexOf(aInfo.insertBefore) > -1) + { + aInfo.insertBefore = sv.getNextTab(aInfo.insertBefore); + } + + if (aInfo.action & sv.kACTIONS_FOR_SOURCE) { + if (aInfo.action & sv.kACTION_PART) { + this.partTabsOnDrop(draggedRoots); + } + else if (aInfo.action & sv.kACTION_ATTACH) { + this.attachTabsOnDrop(draggedRoots, aInfo.parent); + } + else { + return false; + } + + if ( // if this move will cause no change... + sourceBrowser == targetBrowser && + sourceBrowser.treeStyleTab.getNextVisibleTab(draggedTabs[draggedTabs.length-1]) == aInfo.insertBefore + ) { + // then, do nothing + return true; + } + } + + + // prevent Multiple Tab Handler feature + targetBrowser.duplicatingSelectedTabs = true; + targetBrowser.movingSelectedTabs = true; + + + var newRoots = []; + var shouldClose = ( + aInfo.action & sv.kACTION_IMPORT && + sv.getAllTabsArray(sourceBrowser).length == draggedTabs.length + ); + var oldTabs = []; + var newTabs = []; + var treeStructure = draggedTabs.map(function(aTab) { + var parent = sourceBrowser.treeStyleTab.getParentTab(aTab); + return parent ? draggedTabs.indexOf(parent) : -1 ; + }); + + var parentTabsArray = draggedTabs.map(function(aTab) { + return (aInfo.action & sv.kACTIONS_FOR_DESTINATION) ? + sourceBrowser.treeStyleTab.getParentTab(aTab) : null ; + }, this); + + // Firefox fails to "move" collapsed tabs. So, expand them first + // and collapse them after they are moved. + var collapseExpandState = []; + if (aInfo.action & sv.kACTION_IMPORT && + 'swapBrowsersAndCloseOther' in targetBrowser) { + draggedWholeTree.forEach(function(aTab) { + collapseExpandState.push(sv.getTabValue(aTab, sv.kSUBTREE_COLLAPSED) == 'true'); + sv.collapseExpandSubtree(aTab, false, true); + sv.collapseExpandTab(aTab, false, true); + }, sv); + } + + var lastTabIndex = tabs.length -1; + draggedTabs.forEach(function(aTab, aIndex) { + var tab = aTab; + if (aInfo.action & sv.kACTIONS_FOR_DESTINATION) { + var parent = parentTabsArray[aIndex]; + if (tabsInfo.isMultipleMove && 'MultipleTabService' in sourceWindow) + sourceWindow.MultipleTabService.setSelection(aTab, false); + if (aInfo.action & sv.kACTION_IMPORT && + 'swapBrowsersAndCloseOther' in targetBrowser) { + tab = targetBrowser.addTab(); + tab.linkedBrowser.stop(); + tab.linkedBrowser.docShell; + targetBrowser.swapBrowsersAndCloseOther(tab, aTab); + targetBrowser.setTabTitle(tab); + } + else { + tab = targetBrowser.duplicateTab(aTab); + sv.deleteTabValue(tab, sv.kCHILDREN); + sv.deleteTabValue(tab, sv.kPARENT); + if (aInfo.action & sv.kACTION_IMPORT) + oldTabs.push(aTab); + } + newTabs.push(tab); + if (tabsInfo.isMultipleMove && 'MultipleTabService' in window) + MultipleTabService.setSelection(tab, true); + if (!parent || draggedTabs.indexOf(parent) < 0) + newRoots.push(tab); + lastTabIndex++; + } + + var newIndex = aInfo.insertBefore ? aInfo.insertBefore._tPos : lastTabIndex ; + if (aInfo.insertBefore && newIndex > tab._tPos) newIndex--; + + sv.internallyTabMovingCount++; + targetBrowser.moveTabTo(tab, newIndex); + sv.collapseExpandTab(tab, false, true); + sv.internallyTabMovingCount--; + + }, this); + + // close imported tabs from the source browser + oldTabs.forEach(function(aTab) { + sourceBrowser.removeTab(aTab, { animate : true }); + }); + if (shouldClose) + this.closeOwner(sourceBrowser); + + // restore tree structure for newly opened tabs + newTabs.forEach(function(aTab, aIndex) { + var index = treeStructure[aIndex]; + if (index < 0) return; + sv.attachTabTo(aTab, newTabs[index]); + }, sv); + newTabs.reverse(); + collapseExpandState.reverse(); + collapseExpandState.forEach(function(aCollapsed, aIndex) { + sv.collapseExpandSubtree(newTabs[aIndex], aCollapsed, true); + }, sv); + + if (aInfo.action & sv.kACTIONS_FOR_DESTINATION && + aInfo.action & sv.kACTION_ATTACH) + this.attachTabsOnDrop(newRoots, aInfo.parent); + + // Multiple Tab Handler + targetBrowser.movingSelectedTabs = false; + targetBrowser.duplicatingSelectedTabs = false; + + return true; + }, + + getDraggedTabsInfoFromOneTab : function TSTTabbarDND_getDraggedTabsInfoFromOneTab(aInfo, aTab) + { + var sv = this.mOwner; + var b = sv.mTabBrowser; + + aTab = sv.getTabFromChild(aTab); + if (!aTab) + return { + draggedTab : null, + draggedTabs : [], + draggedRoots : [], + isMultipleMove : false + }; + + var targetBrowser = b; + var tabs = sv.getTabsArray(targetBrowser); + + var sourceWindow = aTab.ownerDocument.defaultView; + var sourceBrowser = sv.getTabBrowserFromChild(aTab); + + var draggedTabs = window['piro.sakura.ne.jp'].tabsDragUtils.getSelectedTabs(aInfo.event || sourceBrowser); + var draggedRoots = [aTab]; + var isMultipleMove = false; + + if (draggedTabs.length > 1) { + isMultipleMove = true; + if (!(aInfo.action & sv.kACTIONS_FOR_DESTINATION)) { + draggedRoots = []; + draggedTabs.forEach(function(aTab) { + var parent = aTab, + current; + do { + current = parent; + parent = sourceBrowser.treeStyleTab.getParentTab(parent) + if (parent && draggedTabs.indexOf(parent) > -1) continue; + draggedRoots.push(current); + return; + } + while (parent); + }, this); + } + } + else if (aInfo.action & sv.kACTIONS_FOR_DESTINATION) { + draggedTabs = [aTab].concat(sourceBrowser.treeStyleTab.getDescendantTabs(aTab)); + } + + return { + draggedTab : aTab, + draggedTabs : draggedTabs, + draggedRoots : draggedRoots, + isMultipleMove : isMultipleMove + }; + }, + + attachTabsOnDrop : function TSTTabbarDND_attachTabsOnDrop(aTabs, aParent) + { + var sv = this.mOwner; + var b = sv.mTabBrowser; + + b.movingSelectedTabs = true; // Multiple Tab Handler + aTabs.forEach(function(aTab) { + if (!aTab.parentNode) return; // ignore removed tabs + if (aParent) + sv.attachTabTo(aTab, aParent); + else + sv.partTab(aTab); + sv.collapseExpandTab(aTab, false); + }, sv); + b.movingSelectedTabs = false; // Multiple Tab Handler + }, + + partTabsOnDrop : function TSTTabbarDND_partTabsOnDrop(aTabs) + { + var sv = this.mOwner; + var b = sv.mTabBrowser; + + b.movingSelectedTabs = true; // Multiple Tab Handler + aTabs.forEach(function(aTab) { + if (!aTab.parentNode) return; // ignore removed tabs + sv.partTab(aTab); + sv.collapseExpandTab(aTab, false); + }, sv); + b.movingSelectedTabs = false; // Multiple Tab Handler + }, + + closeOwner : function TSTTabbarDND_closeOwner(aTabOwner) + { + var w = aTabOwner.ownerDocument.defaultView; + if (!w) return; + if ('SplitBrowser' in w) { + if ('getSubBrowserFromChild' in w.SplitBrowser) { + var subbrowser = w.SplitBrowser.getSubBrowserFromChild(aTabOwner); + if (subbrowser) { + subbrowser.close(); + return; + } + } + if (w.SplitBrowser.browsers.length) return; + } + w.close(); + }, + + clearDropPosition : function TSTTabbarDND_clearDropPosition() + { + var sv = this.mOwner; + var b = sv.mTabBrowser; + var xpathResult = sv.evaluateXPath( + 'child::xul:tab[@'+sv.kDROP_POSITION+']', + b.mTabContainer + ); + for (var i = 0, maxi = xpathResult.snapshotLength; i < maxi; i++) + { + xpathResult.snapshotItem(i).removeAttribute(sv.kDROP_POSITION); + } + }, + + isDraggingAllTabs : function TSTTabbarDND_isDraggingAllTabs(aTab, aTabs) + { + var sv = this.mOwner; + var b = sv.mTabBrowser; + + var actionInfo = { + action : sv.kACTIONS_FOR_DESTINATION | sv.kACTION_IMPORT + }; + var tabsInfo = this.getDraggedTabsInfoFromOneTab(actionInfo, aTab); + return tabsInfo.draggedTabs.length == (aTabs || sv.getAllTabsArray(b)).length; + }, + + isDraggingAllCurrentTabs : function TSTTabbarDND_isDraggingAllCurrentTabs(aTab) + { + return this.isDraggingAllTabs(aTab, this.getTabsArray(this.mOwner.mTabBrowser)); + }, + handleEvent : function TSTTabbarDND_handleEvent(aEvent) { switch (aEvent.type) @@ -166,7 +649,7 @@ catch(e) { action : sv.kACTIONS_FOR_DESTINATION | sv.kACTION_MOVE, event : aEvent }; - var tabsInfo = sv.getDraggedTabsInfoFromOneTab(actionInfo, aTab); + var tabsInfo = this.getDraggedTabsInfoFromOneTab(actionInfo, aTab); if (tabsInfo.draggedTabs.length > 1) window['piro.sakura.ne.jp'].tabsDragUtils.startTabsDrag(aEvent, tabsInfo.draggedTabs); }, @@ -246,7 +729,7 @@ catch(e) { var tabbarFromEvent = sv.getTabbarFromChild(aEvent.relatedTarget); if (!tabbarFromEvent) - sv.clearDropPosition(); + this.clearDropPosition(); window.clearTimeout(this.mAutoExpandTimer); this.mAutoExpandTimer = null; @@ -271,7 +754,7 @@ catch(e) { var strip = sv.tabStrip; var dt = aEvent.dataTransfer; - sv.clearDropPosition(); + this.clearDropPosition(); if (dt.mozUserCancelled || dt.dropEffect != 'none') return; @@ -302,7 +785,7 @@ catch(e) { return; var draggedTab = dt.mozGetDataAt(TAB_DROP_TYPE, 0); - if (sv.isDraggingAllCurrentTabs(draggedTab)) + if (this.isDraggingAllCurrentTabs(draggedTab)) return; b.replaceTabWithWindow(draggedTab); @@ -341,7 +824,7 @@ try{ sv.autoScroll.processAutoScroll(aEvent); - var info = sv.getDropAction(aEvent, session); + var info = this.getDropAction(aEvent, session); var observer = b; if (b.tabContainer && b.tabContainer._setEffectAllowedForDataTransfer) // for Firefox 4.0 @@ -379,7 +862,7 @@ try{ b.mTabContainer, XPathResult.FIRST_ORDERED_NODE_TYPE ).singleNodeValue) - sv.clearDropPosition(); + this.clearDropPosition(); if ( !info.canDrop || @@ -427,13 +910,13 @@ catch(e) { var tabbar = b.mTabContainer; var dt = aEvent.dataTransfer; - sv.clearDropPosition(); + this.clearDropPosition(); if (tabbar._tabDropIndicator) // for Firefox 4 or later tabbar._tabDropIndicator.collapsed = true; var session = sv.getCurrentDragSession(); - var dropActionInfo = sv.getDropAction(aEvent, session); + var dropActionInfo = this.getDropAction(aEvent, session); var draggedTab; if (dt.dropEffect != 'link') { @@ -444,7 +927,7 @@ catch(e) { } } - if (draggedTab && sv.performDrop(dropActionInfo, draggedTab)) { + if (draggedTab && this.performDrop(dropActionInfo, draggedTab)) { aEvent.stopPropagation(); return; } @@ -500,7 +983,7 @@ catch(e) { let tab = sv.getTabFromEvent(aEvent); if (!tab || dt.dropEffect == 'copy') { - sv.performDrop(dropActionInfo, b.loadOneTab(getShortcutOrURI(url), { inBackground: bgLoad })); + this.performDrop(dropActionInfo, b.loadOneTab(getShortcutOrURI(url), { inBackground: bgLoad })); } else { let locked = ( @@ -514,7 +997,7 @@ catch(e) { try { if (loadDroppedLinkToNewChildTab || locked) { - sv.performDrop(dropActionInfo, b.loadOneTab(getShortcutOrURI(url), { inBackground: bgLoad })); + this.performDrop(dropActionInfo, b.loadOneTab(getShortcutOrURI(url), { inBackground: bgLoad })); } else { tab.linkedBrowser.loadURI(getShortcutOrURI(url));