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 };
227
228 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
229     init : function(tree){
230         this.tree = tree;
231         tree.getTreeEl().on("keydown", this.onKeyDown, this);
232         tree.on("click", this.onNodeClick, this);
233     },
234     
235     onNodeClick : function(node, e){
236         this.select(node, e, e.ctrlKey);
237     },
238     
239     /**
240      * Select a node.
241      * @param {TreeNode} node The node to select
242      * @param {EventObject} e (optional) An event associated with the selection
243      * @param {Boolean} keepExisting True to retain existing selections
244      * @return {TreeNode} The selected node
245      */
246     select : function(node, e, keepExisting){
247         if(keepExisting !== true){
248             this.clearSelections(true);
249         }
250         if(this.isSelected(node)){
251             this.lastSelNode = node;
252             return node;
253         }
254         this.selNodes.push(node);
255         this.selMap[node.id] = node;
256         this.lastSelNode = node;
257         node.ui.onSelectedChange(true);
258         this.fireEvent("selectionchange", this, this.selNodes);
259         return node;
260     },
261     
262     /**
263      * Deselect a node.
264      * @param {TreeNode} node The node to unselect
265      */
266     unselect : function(node){
267         if(this.selMap[node.id]){
268             node.ui.onSelectedChange(false);
269             var sn = this.selNodes;
270             var index = -1;
271             if(sn.indexOf){
272                 index = sn.indexOf(node);
273             }else{
274                 for(var i = 0, len = sn.length; i < len; i++){
275                     if(sn[i] == node){
276                         index = i;
277                         break;
278                     }
279                 }
280             }
281             if(index != -1){
282                 this.selNodes.splice(index, 1);
283             }
284             delete this.selMap[node.id];
285             this.fireEvent("selectionchange", this, this.selNodes);
286         }
287     },
288     
289     /**
290      * Clear all selections
291      */
292     clearSelections : function(suppressEvent){
293         var sn = this.selNodes;
294         if(sn.length > 0){
295             for(var i = 0, len = sn.length; i < len; i++){
296                 sn[i].ui.onSelectedChange(false);
297             }
298             this.selNodes = [];
299             this.selMap = {};
300             if(suppressEvent !== true){
301                 this.fireEvent("selectionchange", this, this.selNodes);
302             }
303         }
304     },
305     
306     /**
307      * Returns true if the node is selected
308      * @param {TreeNode} node The node to check
309      * @return {Boolean}
310      */
311     isSelected : function(node){
312         return this.selMap[node.id] ? true : false;  
313     },
314     
315     /**
316      * Returns an array of the selected nodes
317      * @return {Array}
318      */
319     getSelectedNodes : function(){
320         return this.selNodes;    
321     },
322
323     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
324
325     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
326
327     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
328 });