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