Roo/tree/TreeSelectionModel.js
[roojs1] / Roo / tree / TreeSelectionModel.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12
13 /**
14  * @class Roo.tree.DefaultSelectionModel
15  * @extends Roo.util.Observable
16  * The default single selection for a TreePanel.
17  */
18 Roo.tree.DefaultSelectionModel = function(cfg){
19    this.selNode = null;
20    
21    
22    
23    this.addEvents({
24        /**
25         * @event selectionchange
26         * Fires when the selected node changes
27         * @param {DefaultSelectionModel} this
28         * @param {TreeNode} node the new selection
29         */
30        "selectionchange" : true,
31
32        /**
33         * @event beforeselect
34         * Fires before the selected node changes, return false to cancel the change
35         * @param {DefaultSelectionModel} this
36         * @param {TreeNode} node the new selection
37         * @param {TreeNode} node the old selection
38         */
39        "beforeselect" : true
40    });
41    
42     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
43 };
44
45 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
46     init : function(tree){
47         this.tree = tree;
48         tree.getTreeEl().on("keydown", this.onKeyDown, this);
49         tree.on("click", this.onNodeClick, this);
50     },
51     
52     onNodeClick : function(node, e){
53         if (e.ctrlKey && this.selNode == node)  {
54             this.unselect(node);
55             return;
56         }
57         this.select(node);
58     },
59     
60     /**
61      * Select a node.
62      * @param {TreeNode} node The node to select
63      * @return {TreeNode} The selected node
64      */
65     select : function(node){
66         var last = this.selNode;
67         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
68             if(last){
69                 last.ui.onSelectedChange(false);
70             }
71             this.selNode = node;
72             node.ui.onSelectedChange(true);
73             this.fireEvent("selectionchange", this, node, last);
74         }
75         return node;
76     },
77     
78     /**
79      * Deselect a node.
80      * @param {TreeNode} node The node to unselect
81      */
82     unselect : function(node){
83         if(this.selNode == node){
84             this.clearSelections();
85         }    
86     },
87     
88     /**
89      * Clear all selections
90      */
91     clearSelections : function(){
92         var n = this.selNode;
93         if(n){
94             n.ui.onSelectedChange(false);
95             this.selNode = null;
96             this.fireEvent("selectionchange", this, null);
97         }
98         return n;
99     },
100     
101     /**
102      * Get the selected node
103      * @return {TreeNode} The selected node
104      */
105     getSelectedNode : function(){
106         return this.selNode;    
107     },
108     
109     /**
110      * Returns true if the node is selected
111      * @param {TreeNode} node The node to check
112      * @return {Boolean}
113      */
114     isSelected : function(node){
115         return this.selNode == node;  
116     },
117
118     /**
119      * Selects the node above the selected node in the tree, intelligently walking the nodes
120      * @return TreeNode The new selection
121      */
122     selectPrevious : function(){
123         var s = this.selNode || this.lastSelNode;
124         if(!s){
125             return null;
126         }
127         var ps = s.previousSibling;
128         if(ps){
129             if(!ps.isExpanded() || ps.childNodes.length < 1){
130                 return this.select(ps);
131             } else{
132                 var lc = ps.lastChild;
133                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
134                     lc = lc.lastChild;
135                 }
136                 return this.select(lc);
137             }
138         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
139             return this.select(s.parentNode);
140         }
141         return null;
142     },
143
144     /**
145      * Selects the node above the selected node in the tree, intelligently walking the nodes
146      * @return TreeNode The new selection
147      */
148     selectNext : function(){
149         var s = this.selNode || this.lastSelNode;
150         if(!s){
151             return null;
152         }
153         if(s.firstChild && s.isExpanded()){
154              return this.select(s.firstChild);
155          }else if(s.nextSibling){
156              return this.select(s.nextSibling);
157          }else if(s.parentNode){
158             var newS = null;
159             s.parentNode.bubble(function(){
160                 if(this.nextSibling){
161                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
162                     return false;
163                 }
164             });
165             return newS;
166          }
167         return null;
168     },
169
170     onKeyDown : function(e){
171         var s = this.selNode || this.lastSelNode;
172         // undesirable, but required
173         var sm = this;
174         if(!s){
175             return;
176         }
177         var k = e.getKey();
178         switch(k){
179              case e.DOWN:
180                  e.stopEvent();
181                  this.selectNext();
182              break;
183              case e.UP:
184                  e.stopEvent();
185                  this.selectPrevious();
186              break;
187              case e.RIGHT:
188                  e.preventDefault();
189                  if(s.hasChildNodes()){
190                      if(!s.isExpanded()){
191                          s.expand();
192                      }else if(s.firstChild){
193                          this.select(s.firstChild, e);
194                      }
195                  }
196              break;
197              case e.LEFT:
198                  e.preventDefault();
199                  if(s.hasChildNodes() && s.isExpanded()){
200                      s.collapse();
201                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
202                      this.select(s.parentNode, e);
203                  }
204              break;
205         };
206     }
207 });
208
209 /**
210  * @class Roo.tree.MultiSelectionModel
211  * @extends Roo.util.Observable
212  * Multi selection for a TreePanel.
213  */
214 Roo.tree.MultiSelectionModel = function(){
215    this.selNodes = [];
216    this.selMap = {};
217    this.addEvents({
218        /**
219         * @event selectionchange
220         * Fires when the selected nodes change
221         * @param {MultiSelectionModel} this
222         * @param {Array} nodes Array of the selected nodes
223         */
224        "selectionchange" : true
225    });
226    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
227    
228 };
229
230 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
231     init : function(tree){
232         this.tree = tree;
233         tree.getTreeEl().on("keydown", this.onKeyDown, this);
234         tree.on("click", this.onNodeClick, this);
235     },
236     
237     onNodeClick : function(node, e){
238         this.select(node, e, e.ctrlKey);
239     },
240     
241     /**
242      * Select a node.
243      * @param {TreeNode} node The node to select
244      * @param {EventObject} e (optional) An event associated with the selection
245      * @param {Boolean} keepExisting True to retain existing selections
246      * @return {TreeNode} The selected node
247      */
248     select : function(node, e, keepExisting){
249         if(keepExisting !== true){
250             this.clearSelections(true);
251         }
252         if(this.isSelected(node)){
253             this.lastSelNode = node;
254             return node;
255         }
256         this.selNodes.push(node);
257         this.selMap[node.id] = node;
258         this.lastSelNode = node;
259         node.ui.onSelectedChange(true);
260         this.fireEvent("selectionchange", this, this.selNodes);
261         return node;
262     },
263     
264     /**
265      * Deselect a node.
266      * @param {TreeNode} node The node to unselect
267      */
268     unselect : function(node){
269         if(this.selMap[node.id]){
270             node.ui.onSelectedChange(false);
271             var sn = this.selNodes;
272             var index = -1;
273             if(sn.indexOf){
274                 index = sn.indexOf(node);
275             }else{
276                 for(var i = 0, len = sn.length; i < len; i++){
277                     if(sn[i] == node){
278                         index = i;
279                         break;
280                     }
281                 }
282             }
283             if(index != -1){
284                 this.selNodes.splice(index, 1);
285             }
286             delete this.selMap[node.id];
287             this.fireEvent("selectionchange", this, this.selNodes);
288         }
289     },
290     
291     /**
292      * Clear all selections
293      */
294     clearSelections : function(suppressEvent){
295         var sn = this.selNodes;
296         if(sn.length > 0){
297             for(var i = 0, len = sn.length; i < len; i++){
298                 sn[i].ui.onSelectedChange(false);
299             }
300             this.selNodes = [];
301             this.selMap = {};
302             if(suppressEvent !== true){
303                 this.fireEvent("selectionchange", this, this.selNodes);
304             }
305         }
306     },
307     
308     /**
309      * Returns true if the node is selected
310      * @param {TreeNode} node The node to check
311      * @return {Boolean}
312      */
313     isSelected : function(node){
314         return this.selMap[node.id] ? true : false;  
315     },
316     
317     /**
318      * Returns an array of the selected nodes
319      * @return {Array}
320      */
321     getSelectedNodes : function(){
322         return this.selNodes;    
323     },
324
325     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
326
327     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
328
329     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
330 });