Upgrade to ExtJS 3.0.0 - Released 07/06/2009
[extjs.git] / src / widgets / tree / TreeSelectionModel.js
1 /*!
2  * Ext JS Library 3.0.0
3  * Copyright(c) 2006-2009 Ext JS, LLC
4  * licensing@extjs.com
5  * http://www.extjs.com/license
6  */
7 /**\r
8  * @class Ext.tree.DefaultSelectionModel\r
9  * @extends Ext.util.Observable\r
10  * The default single selection for a TreePanel.\r
11  */\r
12 Ext.tree.DefaultSelectionModel = function(config){\r
13    this.selNode = null;\r
14    \r
15    this.addEvents(\r
16        /**\r
17         * @event selectionchange\r
18         * Fires when the selected node changes\r
19         * @param {DefaultSelectionModel} this\r
20         * @param {TreeNode} node the new selection\r
21         */\r
22        "selectionchange",\r
23 \r
24        /**\r
25         * @event beforeselect\r
26         * Fires before the selected node changes, return false to cancel the change\r
27         * @param {DefaultSelectionModel} this\r
28         * @param {TreeNode} node the new selection\r
29         * @param {TreeNode} node the old selection\r
30         */\r
31        "beforeselect"\r
32    );\r
33 \r
34     Ext.apply(this, config);\r
35     Ext.tree.DefaultSelectionModel.superclass.constructor.call(this);\r
36 };\r
37 \r
38 Ext.extend(Ext.tree.DefaultSelectionModel, Ext.util.Observable, {\r
39     init : function(tree){\r
40         this.tree = tree;\r
41         tree.getTreeEl().on("keydown", this.onKeyDown, this);\r
42         tree.on("click", this.onNodeClick, this);\r
43     },\r
44     \r
45     onNodeClick : function(node, e){\r
46         this.select(node);\r
47     },\r
48     \r
49     /**\r
50      * Select a node.\r
51      * @param {TreeNode} node The node to select\r
52      * @return {TreeNode} The selected node\r
53      */\r
54     select : function(node){\r
55         var last = this.selNode;\r
56         if(node == last){\r
57             node.ui.onSelectedChange(true);\r
58         }else if(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         if(e.ctrlKey && this.isSelected(node)){\r
230             this.unselect(node);\r
231         }else{\r
232             this.select(node, e, e.ctrlKey);\r
233         }\r
234     },\r
235     \r
236     /**\r
237      * Select a node.\r
238      * @param {TreeNode} node The node to select\r
239      * @param {EventObject} e (optional) An event associated with the selection\r
240      * @param {Boolean} keepExisting True to retain existing selections\r
241      * @return {TreeNode} The selected node\r
242      */\r
243     select : function(node, e, keepExisting){\r
244         if(keepExisting !== true){\r
245             this.clearSelections(true);\r
246         }\r
247         if(this.isSelected(node)){\r
248             this.lastSelNode = node;\r
249             return node;\r
250         }\r
251         this.selNodes.push(node);\r
252         this.selMap[node.id] = node;\r
253         this.lastSelNode = node;\r
254         node.ui.onSelectedChange(true);\r
255         this.fireEvent("selectionchange", this, this.selNodes);\r
256         return node;\r
257     },\r
258     \r
259     /**\r
260      * Deselect a node.\r
261      * @param {TreeNode} node The node to unselect\r
262      */\r
263     unselect : function(node){\r
264         if(this.selMap[node.id]){\r
265             node.ui.onSelectedChange(false);\r
266             var sn = this.selNodes;\r
267             var index = sn.indexOf(node);\r
268             if(index != -1){\r
269                 this.selNodes.splice(index, 1);\r
270             }\r
271             delete this.selMap[node.id];\r
272             this.fireEvent("selectionchange", this, this.selNodes);\r
273         }\r
274     },\r
275     \r
276     /**\r
277      * Clear all selections\r
278      */\r
279     clearSelections : function(suppressEvent){\r
280         var sn = this.selNodes;\r
281         if(sn.length > 0){\r
282             for(var i = 0, len = sn.length; i < len; i++){\r
283                 sn[i].ui.onSelectedChange(false);\r
284             }\r
285             this.selNodes = [];\r
286             this.selMap = {};\r
287             if(suppressEvent !== true){\r
288                 this.fireEvent("selectionchange", this, this.selNodes);\r
289             }\r
290         }\r
291     },\r
292     \r
293     /**\r
294      * Returns true if the node is selected\r
295      * @param {TreeNode} node The node to check\r
296      * @return {Boolean}\r
297      */\r
298     isSelected : function(node){\r
299         return this.selMap[node.id] ? true : false;  \r
300     },\r
301     \r
302     /**\r
303      * Returns an array of the selected nodes\r
304      * @return {Array}\r
305      */\r
306     getSelectedNodes : function(){\r
307         return this.selNodes;    \r
308     },\r
309 \r
310     onKeyDown : Ext.tree.DefaultSelectionModel.prototype.onKeyDown,\r
311 \r
312     selectNext : Ext.tree.DefaultSelectionModel.prototype.selectNext,\r
313 \r
314     selectPrevious : Ext.tree.DefaultSelectionModel.prototype.selectPrevious\r
315 });