diff options
Diffstat (limited to 'webapps/qooxdoo-0.6.5-sdk/frontend/framework/source/class/qx/ui/tree')
4 files changed, 1593 insertions, 0 deletions
diff --git a/webapps/qooxdoo-0.6.5-sdk/frontend/framework/source/class/qx/ui/tree/AbstractTreeElement.js b/webapps/qooxdoo-0.6.5-sdk/frontend/framework/source/class/qx/ui/tree/AbstractTreeElement.js new file mode 100644 index 0000000000..cbaf27f35c --- /dev/null +++ b/webapps/qooxdoo-0.6.5-sdk/frontend/framework/source/class/qx/ui/tree/AbstractTreeElement.js @@ -0,0 +1,505 @@ +/* ************************************************************************ + + qooxdoo - the new era of web development + + http://qooxdoo.org + + Copyright: + 2004-2007 1&1 Internet AG, Germany, http://www.1and1.org + + License: + LGPL: http://www.gnu.org/licenses/lgpl.html + EPL: http://www.eclipse.org/org/documents/epl-v10.php + See the LICENSE file in the project's top-level directory for details. + + Authors: + * Sebastian Werner (wpbasti) + * Andreas Ecker (ecker) + +************************************************************************ */ + +/* ************************************************************************ + +#module(ui_tree) +#embed(qx.widgettheme/tree/*) +#embed(qx.icontheme/16/actions/document-new.png) + +************************************************************************ */ + +qx.OO.defineClass("qx.ui.tree.AbstractTreeElement", qx.ui.layout.BoxLayout, +function(vLabel, vIcon, vIconSelected) +{ + if (this.classname == qx.ui.tree.AbstractTreeElement.ABSTRACT_CLASS) { + throw new Error("Please omit the usage of qx.ui.tree.AbstractTreeElement directly. Choose between qx.ui.tree.TreeFolder and qx.ui.tree.TreeFile instead!"); + } + + // Precreate subwidgets + this._indentObject = new qx.ui.embed.HtmlEmbed; + this._iconObject = new qx.ui.basic.Image; + this._labelObject = new qx.ui.basic.Label; + + // Make anonymous + this._indentObject.setAnonymous(true); + this._iconObject.setAnonymous(true); + this._labelObject.setAnonymous(true); + + // Behaviour and Hard Styling + this._labelObject.setSelectable(false); + this._labelObject.setStyleProperty("lineHeight", "100%"); + + qx.ui.layout.BoxLayout.call(this, "horizontal"); + + this.setLabel(vLabel); + + // Prohibit selection + this.setSelectable(false); + + // Base URL used for indent images + this.BASE_URI = qx.manager.object.AliasManager.getInstance().resolvePath("widget/tree/"); + + // Adding subwidgets + this.add(this._indentObject, this._iconObject, this._labelObject); + + // Set Icons + if (vIcon != null) { + this.setIcon(vIcon); + this.setIconSelected(vIcon); + } + + if (vIconSelected != null) { + this.setIconSelected(vIconSelected); + } + + // Setup initial icon + this._iconObject.setSource(this._evalCurrentIcon()); + + // Set Appearance + this._iconObject.setAppearance("tree-element-icon"); + this._labelObject.setAppearance("tree-element-label"); + + // Register event listeners + this.addEventListener("mousedown", this._onmousedown); + this.addEventListener("mouseup", this._onmouseup); +}); + +qx.ui.tree.AbstractTreeElement.ABSTRACT_CLASS = "qx.ui.tree.AbstractTreeElement"; + + + + +/* +--------------------------------------------------------------------------- + PROPERTIES +--------------------------------------------------------------------------- +*/ + +qx.OO.changeProperty({ name : "appearance", type : "string", defaultValue : "tree-element" }); + +/*! + The icons +*/ +qx.OO.addProperty({ name : "icon", type : "string" }); +qx.OO.addProperty({ name : "iconSelected", type : "string" }); + +/*! + The label/caption/text of the qx.ui.basic.Atom instance +*/ +qx.OO.addProperty({ name : "label" }); + +/*! + Selected property +*/ +qx.OO.addProperty({ name : "selected", type : "boolean", defaultValue : false }); + + + + + + +/* +--------------------------------------------------------------------------- + MODIFIER +--------------------------------------------------------------------------- +*/ + +qx.Proto._modifyLabel = function(propValue, propOldValue, propData) +{ + if (this._labelObject) { + this._labelObject.setHtml(propValue); + } + + return true; +} + +qx.Proto._modifySelected = function(propValue, propOldValue, propData) +{ + propValue ? this.addState("selected") : this.removeState("selected"); + propValue ? this._labelObject.addState("selected") : this._labelObject.removeState("selected"); + + var vTree = this.getTree(); + if (!vTree._fastUpdate || (propOldValue && vTree._oldItem == this)) + { + this._iconObject.setSource(this._evalCurrentIcon()); + + if (propValue) { + this._iconObject.addState("selected"); + } else { + this._iconObject.removeState("selected"); + } + } + + var vManager = this.getTree().getManager(); + + if (propOldValue && vManager.getSelectedItem() == this) + { + vManager.deselectAll(); + } + else if (propValue && vManager.getSelectedItem() != this) + { + vManager.setSelectedItem(this); + } + + return true; +} + +qx.Proto._evalCurrentIcon = function() +{ + if (this.getSelected() && this.getIconSelected()) { + return this.getIconSelected(); + } else { + return this.getIcon() || "icon/16/actions/document-new.png"; + } +} + + + + + +/* +--------------------------------------------------------------------------- + UTILITIES +--------------------------------------------------------------------------- +*/ + +qx.Proto.getParentFolder = function() +{ + try { + return this.getParent().getParent(); + } catch(ex) {} + + return null; +} + +qx.Proto.getLevel = function() +{ + var vParentFolder = this.getParentFolder(); + return vParentFolder ? vParentFolder.getLevel() + 1 : null; +} + +qx.Proto.getTree = function() +{ + var vParentFolder = this.getParentFolder(); + return vParentFolder ? vParentFolder.getTree() : null; +} + +qx.Proto.getIndentObject = function() { + return this._indentObject; +} + +qx.Proto.getIconObject = function() { + return this._iconObject; +} + +qx.Proto.getLabelObject = function() { + return this._labelObject; +} + +/** + * <p>deselects, disconnects, removes and disposes the + * current tree element and its content. + * </p> + * + * <p>destroys the current item (TreeFile or TreeFolder) + * and all its subitems. The destruction of the subitems + * is done by calling destroyContent. This is done if the + * subitem has the method destroyContent which is true if the + * subitem is a TreeFolder (or one of its subclasses). + * </p> + * + * <p>The method destroyContent is defined in the TreeFolder class. + * </p> + */ +qx.Proto.destroy = function() { + var manager = this.getTree() ? this.getTree().getManager() : null; + if(manager) { + + // if the current destroyed item is + // selectd deselect the item. If we are + // in single selection mode we have to + // call deselectAll because setItemSelected + // refuses to deselect in this case + if(manager.getItemSelected(this)) { + if(manager.getMultiSelection()) { + manager.setItemSelected(this,false); + } + else { + manager.deselectAll(); + } + } + + // set the leadItem to null if the current + // destroyed item is the leadItem + if(manager.getLeadItem() == this) { + manager.setLeadItem(null); + } + // set the anchorItem to null if the current + // destroyed item is the anchorItem + if(manager.getAnchorItem() == this) { + manager.setAnchorItem(null); + } + } + + // if the item has the method destroyContent defined + // then it is a TreeFolder (and it's subclasses) + // which potentially have content which also + // has to be destroyed + if(this.destroyContent) { + this.destroyContent(); + } + + // first disconnect the item so rendering + // of the tree lines can be done correctly + this.disconnect(); + + // remove the current item from + // the parent folder + var parentFolder = this.getParentFolder(); + if(parentFolder) { + parentFolder.remove(this); + } + + this.dispose(); +} + + + + + +/* +--------------------------------------------------------------------------- + QUEUE HANDLING +--------------------------------------------------------------------------- +*/ + +qx.Proto.addToTreeQueue = function() +{ + var vTree = this.getTree(); + if (vTree) { + vTree.addChildToTreeQueue(this); + } +} + +qx.Proto.removeFromTreeQueue = function() +{ + var vTree = this.getTree(); + if (vTree) { + vTree.removeChildFromTreeQueue(this); + } +} + +qx.Proto.addToCustomQueues = function(vHint) +{ + this.addToTreeQueue(); + + qx.ui.layout.BoxLayout.prototype.addToCustomQueues.call(this, vHint); +} + +qx.Proto.removeFromCustomQueues = function(vHint) +{ + this.removeFromTreeQueue(); + + qx.ui.layout.BoxLayout.prototype.removeFromCustomQueues.call(this, vHint); +} + + + + + + + + +/* +--------------------------------------------------------------------------- + DISPLAYBLE HANDLING +--------------------------------------------------------------------------- +*/ + +qx.Proto._modifyParent = function(propValue, propOldValue, propData) +{ + qx.ui.layout.BoxLayout.prototype._modifyParent.call(this, propValue, propOldValue, propData); + + // Be sure to update previous folder also if it is closed currently (plus/minus symbol) + if (propOldValue && !propOldValue.isDisplayable() && propOldValue.getParent() && propOldValue.getParent().isDisplayable()) { + propOldValue.getParent().addToTreeQueue(); + } + + // Be sure to update new folder also if it is closed currently (plus/minus symbol) + if (propValue && !propValue.isDisplayable() && propValue.getParent() && propValue.getParent().isDisplayable()) { + propValue.getParent().addToTreeQueue(); + } + + return true; +} + +qx.Proto._handleDisplayableCustom = function(vDisplayable, vParent, vHint) +{ + qx.ui.layout.BoxLayout.prototype._handleDisplayableCustom.call(this, vDisplayable, vParent, vHint); + + if (vHint) + { + var vParentFolder = this.getParentFolder(); + var vPreviousParentFolder = this._previousParentFolder; + + if (vPreviousParentFolder) + { + if (this._wasLastVisibleChild) + { + vPreviousParentFolder._updateIndent(); + } + else if (!vPreviousParentFolder.hasContent()) + { + vPreviousParentFolder.addToTreeQueue(); + } + } + + if (vParentFolder && vParentFolder.isDisplayable() && vParentFolder._initialLayoutDone) { + vParentFolder.addToTreeQueue(); + } + + if (this.isLastVisibleChild()) + { + var vPrev = this.getPreviousVisibleSibling(); + + if (vPrev && vPrev instanceof qx.ui.tree.AbstractTreeElement) { + vPrev._updateIndent(); + } + } + + if (vDisplayable) { + this._updateIndent(); + } + } +} + + + + + + + +/* +--------------------------------------------------------------------------- + EVENT LISTENERS +--------------------------------------------------------------------------- +*/ + +qx.Proto._onmousedown = function(e) +{ + this.getTree().getManager().handleMouseDown(this, e); + e.stopPropagation(); +} + +qx.Proto._onmouseup = qx.lang.Function.returnTrue; + + + + + +/* +--------------------------------------------------------------------------- + TREE FLUSH +--------------------------------------------------------------------------- +*/ + +qx.Proto.flushTree = function() +{ + // store informations for update process + this._previousParentFolder = this.getParentFolder(); + this._wasLastVisibleChild = this.isLastVisibleChild(); + + // generate html for indent area + var vLevel = this.getLevel(); + var vTree = this.getTree(); + var vImage; + var vHtml = []; + var vCurrentObject = this; + + for (var i=0; i<vLevel; i++) + { + vImage = vCurrentObject.getIndentSymbol(vTree.getUseTreeLines(), i==0); + + if (vImage) + { + vHtml.push("<img style=\"position:absolute;top:0px;left:"); + vHtml.push((vLevel-i-1) * 19); + vHtml.push("px\" src=\""); + vHtml.push(this.BASE_URI); + vHtml.push(vImage); + vHtml.push("."); + vHtml.push("gif"); + vHtml.push("\" />"); + } + + vCurrentObject = vCurrentObject.getParentFolder(); + } + + this._indentObject.setHtml(vHtml.join("")); + this._indentObject.setWidth(vLevel * 19); +} + + + + + + + + + + +/* +--------------------------------------------------------------------------- + DISPOSER +--------------------------------------------------------------------------- +*/ + +qx.Proto.dispose = function() +{ + if (this.getDisposed()) { + return true; + } + + if (this._indentObject) + { + this._indentObject.dispose(); + this._indentObject = null; + } + + if (this._iconObject) + { + this._iconObject.dispose(); + this._iconObject = null; + } + + if (this._labelObject) + { + this._labelObject.dispose(); + this._labelObject = null; + } + + this._previousParentFolder = null; + + this.removeEventListener("mousedown", this._onmousedown); + this.removeEventListener("mouseup", this._onmouseup); + + return qx.ui.layout.BoxLayout.prototype.dispose.call(this); +} diff --git a/webapps/qooxdoo-0.6.5-sdk/frontend/framework/source/class/qx/ui/tree/Tree.js b/webapps/qooxdoo-0.6.5-sdk/frontend/framework/source/class/qx/ui/tree/Tree.js new file mode 100644 index 0000000000..850891e058 --- /dev/null +++ b/webapps/qooxdoo-0.6.5-sdk/frontend/framework/source/class/qx/ui/tree/Tree.js @@ -0,0 +1,400 @@ +/* ************************************************************************ + + qooxdoo - the new era of web development + + http://qooxdoo.org + + Copyright: + 2004-2007 1&1 Internet AG, Germany, http://www.1and1.org + + License: + LGPL: http://www.gnu.org/licenses/lgpl.html + EPL: http://www.eclipse.org/org/documents/epl-v10.php + See the LICENSE file in the project's top-level directory for details. + + Authors: + * Sebastian Werner (wpbasti) + * Andreas Ecker (ecker) + +************************************************************************ */ + +/* ************************************************************************ + +#module(ui_tree) + +************************************************************************ */ + +qx.OO.defineClass("qx.ui.tree.Tree", qx.ui.tree.TreeFolder, +function(vLabel, vIcon, vIconSelected) +{ + qx.ui.tree.TreeFolder.call(this, vLabel, vIcon, vIconSelected); + + // ************************************************************************ + // INITILISIZE MANAGER + // ************************************************************************ + this._manager = new qx.manager.selection.TreeSelectionManager(this); + + + this._iconObject.setAppearance("tree-icon"); + this._labelObject.setAppearance("tree-label"); + + + // ************************************************************************ + // DEFAULT STATE + // ************************************************************************ + // The tree should be open by default + this.setOpen(true); + + // Fix vertical alignment of empty tree + this.addToFolder(); + + + // ************************************************************************ + // KEY EVENT LISTENER + // ************************************************************************ + this.addEventListener("keydown", this._onkeydown); + this.addEventListener("keypress", this._onkeypress); + this.addEventListener("keyup", this._onkeyup); +}); + + + + + +/* +--------------------------------------------------------------------------- + PROPERTIES +--------------------------------------------------------------------------- +*/ + +qx.OO.addProperty({ name : "useDoubleClick", type : "boolean", defaultValue : false, getAlias : "useDoubleClick" }); +qx.OO.addProperty({ name : "useTreeLines", type : "boolean", defaultValue : true, getAlias : "useTreeLines" }); + + + + + + +/* +--------------------------------------------------------------------------- + MANAGER BINDING +--------------------------------------------------------------------------- +*/ + +qx.Proto.getManager = function() { + return this._manager; +} + +qx.Proto.getSelectedElement = function() { + return this.getManager().getSelectedItem(); +} + + + + + + +/* +--------------------------------------------------------------------------- + QUEUE HANDLING +--------------------------------------------------------------------------- +*/ + +qx.Proto.addChildToTreeQueue = function(vChild) +{ + if (!vChild._isInTreeQueue && !vChild._isDisplayable) { + this.debug("Ignoring invisible child: " + vChild); + } + + if (!vChild._isInTreeQueue && vChild._isDisplayable) + { + qx.ui.core.Widget.addToGlobalWidgetQueue(this); + + if (!this._treeQueue) { + this._treeQueue = {}; + } + + this._treeQueue[vChild.toHashCode()] = vChild; + + vChild._isInTreeQueue = true; + } +} + +qx.Proto.removeChildFromTreeQueue = function(vChild) +{ + if (vChild._isInTreeQueue) + { + if (this._treeQueue) { + delete this._treeQueue[vChild.toHashCode()]; + } + + delete vChild._isInTreeQueue; + } +} + +qx.Proto.flushWidgetQueue = function() { + this.flushTreeQueue(); +} + +qx.Proto.flushTreeQueue = function() +{ + if (!qx.lang.Object.isEmpty(this._treeQueue)) + { + for (var vHashCode in this._treeQueue) + { + // this.debug("Flushing Tree Child: " + this._treeQueue[vHashCode]); + this._treeQueue[vHashCode].flushTree(); + delete this._treeQueue[vHashCode]._isInTreeQueue; + } + + delete this._treeQueue; + } +} + + + + + + + +/* +--------------------------------------------------------------------------- + MODIFIER +--------------------------------------------------------------------------- +*/ + +qx.Proto._modifyUseTreeLines = function(propValue, propOldValue, propData) +{ + if (this._initialLayoutDone) { + this._updateIndent(); + } + + return true; +} + + + + + + + +/* +--------------------------------------------------------------------------- + UTILITIES +--------------------------------------------------------------------------- +*/ + +qx.Proto.getTree = function() { + return this; +} + +qx.Proto.getParentFolder = function() { + return null; +} + +qx.Proto.getLevel = function() { + return 0; +} + + + + + + + + +/* +--------------------------------------------------------------------------- + COMMON CHECKERS +--------------------------------------------------------------------------- +*/ + +qx.ui.tree.Tree.isTreeFolder = function(vObject) { + return vObject && vObject instanceof qx.ui.tree.TreeFolder && !(vObject instanceof qx.ui.tree.Tree); +}; + +qx.ui.tree.Tree.isOpenTreeFolder = function(vObject) { + return vObject instanceof qx.ui.tree.TreeFolder && vObject.getOpen() && vObject.hasContent(); +}; + + + + + + + +/* +--------------------------------------------------------------------------- + EVENT HANDLER +--------------------------------------------------------------------------- +*/ + +qx.Proto._onkeydown = function(e) +{ + var vSelectedItem = this.getManager().getSelectedItem(); + + if (e.getKeyIdentifier() == "Enter") { + e.preventDefault(); + + if (qx.ui.tree.Tree.isTreeFolder(vSelectedItem)) { + return vSelectedItem.toggle(); + } + } +}; + + +qx.Proto._onkeypress = function(e) +{ + var vManager = this.getManager(); + var vSelectedItem = vManager.getSelectedItem(); + + switch(e.getKeyIdentifier()) + { + case "Left": + e.preventDefault(); + + if (qx.ui.tree.Tree.isTreeFolder(vSelectedItem)) + { + if (!vSelectedItem.getOpen()) + { + var vParent = vSelectedItem.getParentFolder(); + if (vParent instanceof qx.ui.tree.TreeFolder) { + if (!(vParent instanceof qx.ui.tree.Tree)) { + vParent.close(); + } + + this.setSelectedElement(vParent); + } + } + else + { + return vSelectedItem.close(); + } + } + else if (vSelectedItem instanceof qx.ui.tree.TreeFile) + { + var vParent = vSelectedItem.getParentFolder(); + if (vParent instanceof qx.ui.tree.TreeFolder) { + if (!(vParent instanceof qx.ui.tree.Tree)) { + vParent.close(); + } + + this.setSelectedElement(vParent); + } + } + + break; + + case "Right": + e.preventDefault(); + + if (qx.ui.tree.Tree.isTreeFolder(vSelectedItem)) + { + if (!vSelectedItem.getOpen()) + { + return vSelectedItem.open(); + } + else if (vSelectedItem.hasContent()) + { + var vFirst = vSelectedItem.getFirstVisibleChildOfFolder(); + this.setSelectedElement(vFirst); + + if (vFirst instanceof qx.ui.tree.TreeFolder) { + vFirst.open(); + } + + return; + } + } + + break; + + default: + if (!this._fastUpdate) + { + this._fastUpdate = true; + this._oldItem = vSelectedItem; + } + + vManager.handleKeyPress(e); + } +}; + + +qx.Proto._onkeyup = function(e) +{ + if (this._fastUpdate) + { + var vOldItem = this._oldItem; + var vNewItem = this.getManager().getSelectedItem(); + + vNewItem.getIconObject().addState("selected"); + + delete this._fastUpdate; + delete this._oldItem; + } +}; + + +qx.Proto.getLastTreeChild = function() +{ + var vLast = this; + + while (vLast instanceof qx.ui.tree.AbstractTreeElement) + { + if (!(vLast instanceof qx.ui.tree.TreeFolder) || !vLast.getOpen()) { + return vLast; + } + + vLast = vLast.getLastVisibleChildOfFolder(); + } + + return null; +}; + + +qx.Proto.getFirstTreeChild = function() { + return this; +}; + + +qx.Proto.setSelectedElement = function(vElement) +{ + var vManager = this.getManager(); + + vManager.setSelectedItem(vElement); + vManager.setLeadItem(vElement); +}; + + + + + + + +/* +--------------------------------------------------------------------------- + DISPOSER +--------------------------------------------------------------------------- +*/ + +qx.Proto.dispose = function() +{ + if (this.getDisposed()) { + return; + } + + this.removeEventListener("keydown", this._onkeydown); + this.removeEventListener("keypress", this._onkeypress); + this.removeEventListener("keyup", this._onkeyup); + + if (this._manager) + { + this._manager.dispose(); + this._manager = null; + } + + delete this._oldItem; + + return qx.ui.tree.TreeFolder.prototype.dispose.call(this); +} diff --git a/webapps/qooxdoo-0.6.5-sdk/frontend/framework/source/class/qx/ui/tree/TreeFile.js b/webapps/qooxdoo-0.6.5-sdk/frontend/framework/source/class/qx/ui/tree/TreeFile.js new file mode 100644 index 0000000000..8c74fa1bf5 --- /dev/null +++ b/webapps/qooxdoo-0.6.5-sdk/frontend/framework/source/class/qx/ui/tree/TreeFile.js @@ -0,0 +1,64 @@ +/* ************************************************************************ + + qooxdoo - the new era of web development + + http://qooxdoo.org + + Copyright: + 2004-2007 1&1 Internet AG, Germany, http://www.1and1.org + + License: + LGPL: http://www.gnu.org/licenses/lgpl.html + EPL: http://www.eclipse.org/org/documents/epl-v10.php + See the LICENSE file in the project's top-level directory for details. + + Authors: + * Sebastian Werner (wpbasti) + * Andreas Ecker (ecker) + +************************************************************************ */ + +/* ************************************************************************ + +#module(ui_tree) + +************************************************************************ */ + +qx.OO.defineClass("qx.ui.tree.TreeFile", qx.ui.tree.AbstractTreeElement, +function(vLabel, vIcon, vIconSelected) { + qx.ui.tree.AbstractTreeElement.call(this, vLabel, vIcon, vIconSelected); +}); + + + + +/* +--------------------------------------------------------------------------- + INDENT HELPER +--------------------------------------------------------------------------- +*/ + +qx.Proto.getIndentSymbol = function(vUseTreeLines, vIsLastColumn) +{ + if (vUseTreeLines) + { + if (vIsLastColumn) + { + return this.isLastChild() ? "end" : "cross"; + } + else + { + return "line"; + } + } + + return null; +} + +qx.Proto._updateIndent = function() { + this.addToTreeQueue(); +} + +qx.Proto.getItems = function() { + return [this]; +} diff --git a/webapps/qooxdoo-0.6.5-sdk/frontend/framework/source/class/qx/ui/tree/TreeFolder.js b/webapps/qooxdoo-0.6.5-sdk/frontend/framework/source/class/qx/ui/tree/TreeFolder.js new file mode 100644 index 0000000000..236a776de5 --- /dev/null +++ b/webapps/qooxdoo-0.6.5-sdk/frontend/framework/source/class/qx/ui/tree/TreeFolder.js @@ -0,0 +1,624 @@ +/* ************************************************************************ + + qooxdoo - the new era of web development + + http://qooxdoo.org + + Copyright: + 2004-2007 1&1 Internet AG, Germany, http://www.1and1.org + + License: + LGPL: http://www.gnu.org/licenses/lgpl.html + EPL: http://www.eclipse.org/org/documents/epl-v10.php + See the LICENSE file in the project's top-level directory for details. + + Authors: + * Sebastian Werner (wpbasti) + * Andreas Ecker (ecker) + +************************************************************************ */ + +/* ************************************************************************ + +#module(ui_tree) +#embed(qx.icontheme/16/status/folder-open.png) +#embed(qx.icontheme/16/places/folder.png) + +************************************************************************ */ + +qx.OO.defineClass("qx.ui.tree.TreeFolder", qx.ui.tree.AbstractTreeElement, +function(vLabel, vIcon, vIconSelected) +{ + qx.ui.tree.AbstractTreeElement.call(this, vLabel, vIcon, vIconSelected); + + this._iconObject.setAppearance("tree-folder-icon"); + this._labelObject.setAppearance("tree-folder-label"); + + this.addEventListener("dblclick", this._ondblclick); + + // Remapping of add/remove methods + this.add = this.addToFolder; + this.addBefore = this.addBeforeToFolder; + this.addAfter = this.addAfterToFolder; + this.addAt = this.addAtToFolder; + this.addAtBegin = this.addAtBeginToFolder; + this.addAtEnd = this.addAtEndToFolder; + this.remove = this.removeFromFolder; +}); + + + +/* +--------------------------------------------------------------------------- + PROPERTIES +--------------------------------------------------------------------------- +*/ + + +qx.OO.changeProperty({ name : "appearance", type : "string", defaultValue : "tree-folder" }); +qx.OO.changeProperty({ name : "icon", type : "string" }); +qx.OO.changeProperty({ name : "iconSelected", type : "string" }); + +qx.OO.addProperty({ name : "open", type : "boolean", defaultValue : false }); +qx.OO.addProperty({ name : "alwaysShowPlusMinusSymbol", type : "boolean", defaultValue : false }); + + + + +/* +--------------------------------------------------------------------------- + UTILITIES +--------------------------------------------------------------------------- +*/ + +qx.Proto.hasContent = function() { + return this._containerObject && this._containerObject.getChildrenLength() > 0; +} + +qx.Proto.open = function() +{ + if (this.getOpen()) { + return; + } + + if (this.hasContent() && this.isSeeable()) + { + this.getTopLevelWidget().setGlobalCursor("progress"); + qx.client.Timer.once(this._openCallback, this, 0); + } + else + { + this.setOpen(true); + } +} + +qx.Proto.close = function() { + this.setOpen(false); +} + +qx.Proto.toggle = function() { + this.getOpen() ? this.close() : this.open(); +} + +qx.Proto._openCallback = function() +{ + this.setOpen(true); + qx.ui.core.Widget.flushGlobalQueues(); + this.getTopLevelWidget().setGlobalCursor(null); +} + + + + + + + + +/* +--------------------------------------------------------------------------- + CHILDREN HANDLING +--------------------------------------------------------------------------- +*/ + +qx.Proto._createChildrenStructure = function() +{ + this.setAppearance(this instanceof qx.ui.tree.Tree ? "tree-container" : "tree-folder-container"); + + if (!this._horizontalLayout) + { + this.setOrientation("vertical"); + + this._horizontalLayout = new qx.ui.layout.HorizontalBoxLayout; + this._horizontalLayout.setWidth(null); + this._horizontalLayout.setParent(this); + this._horizontalLayout.setAnonymous(true); + this._horizontalLayout.setAppearance(this instanceof qx.ui.tree.Tree ? "tree" : "tree-folder"); + + this._indentObject.setParent(this._horizontalLayout); + this._iconObject.setParent(this._horizontalLayout); + this._labelObject.setParent(this._horizontalLayout); + } + + if (!this._containerObject) + { + this._containerObject = new qx.ui.layout.VerticalBoxLayout; + this._containerObject.setWidth(null); + this._containerObject.setAnonymous(true); + + // it should be faster to first handle display, + // because the default display value is true and if we first + // setup the parent the logic do all to make the + // widget first visible and then, if the folder is not + // opened again invisible. + this._containerObject.setDisplay(this.getOpen()); + this._containerObject.setParent(this); + + // remap remove* functions + this.remapChildrenHandlingTo(this._containerObject); + } +} + +qx.Proto._handleChildMove = function(vChild, vRelationIndex, vRelationChild) +{ + if (vChild.isDisplayable()) + { + var vChildren = this._containerObject.getChildren(); + var vOldChildIndex = vChildren.indexOf(vChild); + + if (vOldChildIndex != -1) + { + if (vRelationChild) { + vRelationIndex = vChildren.indexOf(vRelationChild); + } + + if (vRelationIndex == vChildren.length-1) + { + vChild._updateIndent(); + + // Update indent of previous last child + this._containerObject.getLastVisibleChild()._updateIndent(); + } + else if (vChild._wasLastVisibleChild) + { + vChild._updateIndent(); + + // Update indent for new last child + var vPreviousSibling = vChild.getPreviousVisibleSibling(); + if (vPreviousSibling) { + vPreviousSibling._updateIndent(); + } + } + } + } +} + +qx.Proto.addToFolder = function() +{ + this._createChildrenStructure(); + + if (this._containerObject) { + return this._containerObject.add.apply(this._containerObject, arguments); + } +} + +qx.Proto.addBeforeToFolder = function(vChild, vBefore) +{ + this._createChildrenStructure(); + + if (this._containerObject) + { + this._handleChildMove(vChild, null, vBefore); + return this._containerObject.addBefore.apply(this._containerObject, arguments); + } +} + +qx.Proto.addAfterToFolder = function(vChild, vAfter) +{ + this._createChildrenStructure(); + + if (this._containerObject) + { + this._handleChildMove(vChild, null, vAfter); + return this._containerObject.addAfter.apply(this._containerObject, arguments); + } +} + +qx.Proto.addAtToFolder = function(vChild, vIndex) +{ + this._createChildrenStructure(); + + if (this._containerObject) + { + this._handleChildMove(vChild, vIndex); + return this._containerObject.addAt.apply(this._containerObject, arguments); + } +} + +qx.Proto.addAtBeginToFolder = function(vChild) { + return this.addAtToFolder(vChild, 0); +} + +qx.Proto.addAtEndToFolder = function(vChild) +{ + this._createChildrenStructure(); + + if (this._containerObject) + { + var vLast = this._containerObject.getLastChild(); + + if (vLast) + { + this._handleChildMove(vChild, null, vLast); + return this._containerObject.addAfter.call(this._containerObject, vChild, vLast); + } + else + { + return this.addAtBeginToFolder(vChild); + } + } +} + +qx.Proto._remappingChildTable = [ "remove", "removeAt", "removeAll" ]; + + + + + + +/* +--------------------------------------------------------------------------- + CHILDREN UTILITIES +--------------------------------------------------------------------------- +*/ + +qx.Proto.getContainerObject = function() { + return this._containerObject; +} + +qx.Proto.getHorizontalLayout = function() { + return this._horizontalLayout; +} + +qx.Proto.getFirstVisibleChildOfFolder = function() +{ + if (this._containerObject) { + return this._containerObject.getFirstChild(); + } +} + +qx.Proto.getLastVisibleChildOfFolder = function() +{ + if (this._containerObject) { + return this._containerObject.getLastChild(); + } +} + +qx.Proto.getItems = function(recursive, invisible) +{ + var a = [this]; + + if (this._containerObject) + { + var ch = invisible == true ? this._containerObject.getChildren() : this._containerObject.getVisibleChildren(); + + if (recursive == false) + { + a = a.concat(ch); + } + else + { + for (var i=0, chl=ch.length; i<chl; i++) { + a = a.concat(ch[i].getItems(recursive, invisible)); + } + } + } + + return a; +} + +/** + * <p>deselects, disconnects, removes and disposes the + * content of the folder and its subfolders. + * </p> + * + * <p>the current items subitems (and the subitems of each + * subitem) are destroyed going top down the TreeFolder + * hierarchy. The current item is left as is. + * </p> + */ +qx.Proto.destroyContent = function() +{ + if(!this.hasContent()) { + return; + } + + var manager = this.getTree() ? this.getTree().getManager() : null; + + var leadItem; + var anchorItem; + if(manager) { + leadItem = manager.getLeadItem(); + anchorItem = manager.getAnchorItem(); + } + + // set the container objects display property + // to true so getChildren will retreive all + // children objects + this._containerObject.setDisplay(true); + var items = this._containerObject.getChildren(); + var item; + + for(var i=items.length-1;i>=0;--i) { + item = items[i]; + + // this.getItems seems to also contain "this". + // In order to avoid endless loops by calling + // recursively destroyContent we have to avoid + // destroying ourselves + if(item != this) { + if(manager) { + // set the leadItem to null if the current + // destroyed item is the leadItem + if(leadItem == item) { + manager.setLeadItem(null); + } + // set the anchorItem to null if the current + // destroyed item is the anchorItem + if(anchorItem == item) { + manager.setAnchorItem(null); + } + + // if the current destroyed item is + // selected, deselect the item. If we are + // in single selection mode we have to + // call deselectAll because setItemSelected + // refuses to deselect in this case + if(manager.getItemSelected(item)) { + if(manager.getMultiSelection()) { + manager.setItemSelected(item,false); + } + else { + manager.deselectAll(); + } + } + + // if the item has the method destroyContent defined + // then it is a TreeFolder (and it's subclasses) + // which potentially have content which also + // has to be destroyed + if (item.destroyContent) { + item.destroyContent(); + } + } + + // first disconnect the item so rendering + // of the tree lines can be done correctly + item.removeFromTreeQueue(); + item.disconnect(); + + // remove the item from the containerObject + this._containerObject.remove(item); + item.dispose(); + delete items[i]; + } + } +} + + + + +/* +--------------------------------------------------------------------------- + MODIFIER +--------------------------------------------------------------------------- +*/ + +qx.Proto._evalCurrentIcon = function() +{ + if (this.getSelected()) { + return this.getIconSelected() || "icon/16/status/folder-open.png"; + } else { + return this.getIcon() || "icon/16/places/folder.png"; + } +} + +qx.Proto._modifyOpen = function(propValue, propOldValue, propData) +{ + this._updateLastColumn(); + + if (this._containerObject) { + this._containerObject.setDisplay(propValue); + } + + return true; +} + +qx.Proto._modifyAlwaysShowPlusMinusSymbol = function(propValue, propOldValue, propData) +{ + this._updateLastColumn(); + + return true; +} + +qx.Proto._updateLastColumn = function() +{ + if (this._indentObject) + { + var vElement = this._indentObject.getElement(); + + if (vElement && vElement.firstChild) { + vElement.firstChild.src = this.BASE_URI + this.getIndentSymbol(this.getTree().getUseTreeLines(), true) + ".gif"; + } + } +} + + + + + + + +/* +--------------------------------------------------------------------------- + EVENT LISTENERS +--------------------------------------------------------------------------- +*/ + +qx.Proto._onmousedown = function(e) +{ + var vOriginalTarget = e.getOriginalTarget(); + + switch(vOriginalTarget) + { + case this._indentObject: + if (this._indentObject.getElement().firstChild == e.getDomTarget()) + { + this.toggle(); + + // Only if we just get closed and the current selection is inside of this node. + if (!this.getOpen()) + { + if(qx.lang.Array.contains(this.getItems(true, true), this.getTree().getSelectedElement())) { + this.getTree().getManager().handleMouseDown(this, e); + } + } + } + + break; + + case this._containerObject: + break; + + case this: + if (this._containerObject) { + break; + } + + // no break here + + default: + this.getTree().getManager().handleMouseDown(this, e); + } + + e.stopPropagation(); +} + +qx.Proto._onmouseup = function(e) +{ + var vOriginalTarget = e.getOriginalTarget(); + + switch(vOriginalTarget) + { + case this._indentObject: + case this._containerObject: + case this: + break; + + default: + if (!this.getTree().getUseDoubleClick()) { + this.open(); + } + } +} + +qx.Proto._ondblclick = function(e) +{ + if (!this.getTree().getUseDoubleClick()) { + return; + } + + this.toggle(); + e.stopPropagation(); +} + + + + + + + +/* +--------------------------------------------------------------------------- + INDENT HELPER +--------------------------------------------------------------------------- +*/ + +qx.Proto.getIndentSymbol = function(vUseTreeLines, vIsLastColumn) +{ + if (vIsLastColumn) + { + if (this.hasContent() || this.getAlwaysShowPlusMinusSymbol()) + { + if (!vUseTreeLines) + { + return this.getOpen() ? "minus" : "plus"; + } + else if (this.isLastChild()) + { + return this.getOpen() ? "end_minus" : "end_plus"; + } + else + { + return this.getOpen() ? "cross_minus" : "cross_plus"; + } + } + else if (vUseTreeLines) + { + return this.isLastChild() ? "end" : "cross"; + } + } + else + { + return vUseTreeLines && !this.isLastChild() ? "line" : null; + } +} + +qx.Proto._updateIndent = function() +{ + // Intentionally bypass superclass; the _updateIndent we want is in TreeFile + qx.ui.tree.TreeFile.prototype._updateIndent.call(this); + + if (!this._containerObject) { + return; + } + + var ch = this._containerObject.getVisibleChildren(); + for (var i=0, l=ch.length; i<l; i++) { + ch[i]._updateIndent(); + } +} + + + + + + + +/* +--------------------------------------------------------------------------- + DISPOSER +--------------------------------------------------------------------------- +*/ + +qx.Proto.dispose = function() +{ + if (this.getDisposed()) { + return; + } + + this.removeEventListener("dblclick", this._ondblclick); + + if (this._horizontalLayout) + { + this._horizontalLayout.dispose(); + this._horizontalLayout = null; + } + + if (this._containerObject) + { + this._containerObject.dispose(); + this._containerObject = null; + } + + return qx.ui.tree.AbstractTreeElement.prototype.dispose.call(this); +} |