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