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