X-Git-Url: http://git.ithinksw.org/extjs.git/blobdiff_plain/ee06f37b0f6f6d94cd05a6ffae556660f7c4a2bc..c930e9176a5a85509c5b0230e2bff5c22a591432:/src/widgets/tree/TreeSelectionModel.js diff --git a/src/widgets/tree/TreeSelectionModel.js b/src/widgets/tree/TreeSelectionModel.js new file mode 100644 index 00000000..736d5eb8 --- /dev/null +++ b/src/widgets/tree/TreeSelectionModel.js @@ -0,0 +1,315 @@ +/*! + * Ext JS Library 3.0.0 + * Copyright(c) 2006-2009 Ext JS, LLC + * licensing@extjs.com + * http://www.extjs.com/license + */ +/** + * @class Ext.tree.DefaultSelectionModel + * @extends Ext.util.Observable + * The default single selection for a TreePanel. + */ +Ext.tree.DefaultSelectionModel = function(config){ + this.selNode = null; + + this.addEvents( + /** + * @event selectionchange + * Fires when the selected node changes + * @param {DefaultSelectionModel} this + * @param {TreeNode} node the new selection + */ + "selectionchange", + + /** + * @event beforeselect + * Fires before the selected node changes, return false to cancel the change + * @param {DefaultSelectionModel} this + * @param {TreeNode} node the new selection + * @param {TreeNode} node the old selection + */ + "beforeselect" + ); + + Ext.apply(this, config); + Ext.tree.DefaultSelectionModel.superclass.constructor.call(this); +}; + +Ext.extend(Ext.tree.DefaultSelectionModel, Ext.util.Observable, { + init : function(tree){ + this.tree = tree; + tree.getTreeEl().on("keydown", this.onKeyDown, this); + tree.on("click", this.onNodeClick, this); + }, + + onNodeClick : function(node, e){ + this.select(node); + }, + + /** + * Select a node. + * @param {TreeNode} node The node to select + * @return {TreeNode} The selected node + */ + select : function(node){ + var last = this.selNode; + if(node == last){ + node.ui.onSelectedChange(true); + }else if(this.fireEvent('beforeselect', this, node, last) !== false){ + if(last){ + last.ui.onSelectedChange(false); + } + this.selNode = node; + node.ui.onSelectedChange(true); + this.fireEvent("selectionchange", this, node, last); + } + return node; + }, + + /** + * Deselect a node. + * @param {TreeNode} node The node to unselect + */ + unselect : function(node){ + if(this.selNode == node){ + this.clearSelections(); + } + }, + + /** + * Clear all selections + */ + clearSelections : function(){ + var n = this.selNode; + if(n){ + n.ui.onSelectedChange(false); + this.selNode = null; + this.fireEvent("selectionchange", this, null); + } + return n; + }, + + /** + * Get the selected node + * @return {TreeNode} The selected node + */ + getSelectedNode : function(){ + return this.selNode; + }, + + /** + * Returns true if the node is selected + * @param {TreeNode} node The node to check + * @return {Boolean} + */ + isSelected : function(node){ + return this.selNode == node; + }, + + /** + * Selects the node above the selected node in the tree, intelligently walking the nodes + * @return TreeNode The new selection + */ + selectPrevious : function(){ + var s = this.selNode || this.lastSelNode; + if(!s){ + return null; + } + var ps = s.previousSibling; + if(ps){ + if(!ps.isExpanded() || ps.childNodes.length < 1){ + return this.select(ps); + } else{ + var lc = ps.lastChild; + while(lc && lc.isExpanded() && lc.childNodes.length > 0){ + lc = lc.lastChild; + } + return this.select(lc); + } + } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){ + return this.select(s.parentNode); + } + return null; + }, + + /** + * Selects the node above the selected node in the tree, intelligently walking the nodes + * @return TreeNode The new selection + */ + selectNext : function(){ + var s = this.selNode || this.lastSelNode; + if(!s){ + return null; + } + if(s.firstChild && s.isExpanded()){ + return this.select(s.firstChild); + }else if(s.nextSibling){ + return this.select(s.nextSibling); + }else if(s.parentNode){ + var newS = null; + s.parentNode.bubble(function(){ + if(this.nextSibling){ + newS = this.getOwnerTree().selModel.select(this.nextSibling); + return false; + } + }); + return newS; + } + return null; + }, + + onKeyDown : function(e){ + var s = this.selNode || this.lastSelNode; + // undesirable, but required + var sm = this; + if(!s){ + return; + } + var k = e.getKey(); + switch(k){ + case e.DOWN: + e.stopEvent(); + this.selectNext(); + break; + case e.UP: + e.stopEvent(); + this.selectPrevious(); + break; + case e.RIGHT: + e.preventDefault(); + if(s.hasChildNodes()){ + if(!s.isExpanded()){ + s.expand(); + }else if(s.firstChild){ + this.select(s.firstChild, e); + } + } + break; + case e.LEFT: + e.preventDefault(); + if(s.hasChildNodes() && s.isExpanded()){ + s.collapse(); + }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){ + this.select(s.parentNode, e); + } + break; + }; + } +}); + +/** + * @class Ext.tree.MultiSelectionModel + * @extends Ext.util.Observable + * Multi selection for a TreePanel. + */ +Ext.tree.MultiSelectionModel = function(config){ + this.selNodes = []; + this.selMap = {}; + this.addEvents( + /** + * @event selectionchange + * Fires when the selected nodes change + * @param {MultiSelectionModel} this + * @param {Array} nodes Array of the selected nodes + */ + "selectionchange" + ); + Ext.apply(this, config); + Ext.tree.MultiSelectionModel.superclass.constructor.call(this); +}; + +Ext.extend(Ext.tree.MultiSelectionModel, Ext.util.Observable, { + init : function(tree){ + this.tree = tree; + tree.getTreeEl().on("keydown", this.onKeyDown, this); + tree.on("click", this.onNodeClick, this); + }, + + onNodeClick : function(node, e){ + if(e.ctrlKey && this.isSelected(node)){ + this.unselect(node); + }else{ + this.select(node, e, e.ctrlKey); + } + }, + + /** + * Select a node. + * @param {TreeNode} node The node to select + * @param {EventObject} e (optional) An event associated with the selection + * @param {Boolean} keepExisting True to retain existing selections + * @return {TreeNode} The selected node + */ + select : function(node, e, keepExisting){ + if(keepExisting !== true){ + this.clearSelections(true); + } + if(this.isSelected(node)){ + this.lastSelNode = node; + return node; + } + this.selNodes.push(node); + this.selMap[node.id] = node; + this.lastSelNode = node; + node.ui.onSelectedChange(true); + this.fireEvent("selectionchange", this, this.selNodes); + return node; + }, + + /** + * Deselect a node. + * @param {TreeNode} node The node to unselect + */ + unselect : function(node){ + if(this.selMap[node.id]){ + node.ui.onSelectedChange(false); + var sn = this.selNodes; + var index = sn.indexOf(node); + if(index != -1){ + this.selNodes.splice(index, 1); + } + delete this.selMap[node.id]; + this.fireEvent("selectionchange", this, this.selNodes); + } + }, + + /** + * Clear all selections + */ + clearSelections : function(suppressEvent){ + var sn = this.selNodes; + if(sn.length > 0){ + for(var i = 0, len = sn.length; i < len; i++){ + sn[i].ui.onSelectedChange(false); + } + this.selNodes = []; + this.selMap = {}; + if(suppressEvent !== true){ + this.fireEvent("selectionchange", this, this.selNodes); + } + } + }, + + /** + * Returns true if the node is selected + * @param {TreeNode} node The node to check + * @return {Boolean} + */ + isSelected : function(node){ + return this.selMap[node.id] ? true : false; + }, + + /** + * Returns an array of the selected nodes + * @return {Array} + */ + getSelectedNodes : function(){ + return this.selNodes; + }, + + onKeyDown : Ext.tree.DefaultSelectionModel.prototype.onKeyDown, + + selectNext : Ext.tree.DefaultSelectionModel.prototype.selectNext, + + selectPrevious : Ext.tree.DefaultSelectionModel.prototype.selectPrevious +}); \ No newline at end of file