]>
Commit | Line | Data |
---|---|---|
2f01fe57 | 1 | /* |
81bea17a | 2 | Copyright (c) 2004-2011, The Dojo Foundation All Rights Reserved. |
2f01fe57 AD |
3 | Available via Academic Free License >= 2.1 OR the modified BSD license. |
4 | see: http://dojotoolkit.org/license for details | |
5 | */ | |
6 | ||
7 | ||
81bea17a AD |
8 | if(!dojo._hasResource["dijit.tree._dndSelector"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. |
9 | dojo._hasResource["dijit.tree._dndSelector"] = true; | |
2f01fe57 AD |
10 | dojo.provide("dijit.tree._dndSelector"); |
11 | dojo.require("dojo.dnd.common"); | |
12 | dojo.require("dijit.tree._dndContainer"); | |
81bea17a AD |
13 | |
14 | ||
15 | dojo.declare("dijit.tree._dndSelector", | |
16 | dijit.tree._dndContainer, | |
17 | { | |
18 | // summary: | |
19 | // This is a base class for `dijit.tree.dndSource` , and isn't meant to be used directly. | |
20 | // It's based on `dojo.dnd.Selector`. | |
21 | // tags: | |
22 | // protected | |
23 | ||
24 | /*===== | |
25 | // selection: Hash<String, DomNode> | |
26 | // (id, DomNode) map for every TreeNode that's currently selected. | |
27 | // The DOMNode is the TreeNode.rowNode. | |
28 | selection: {}, | |
29 | =====*/ | |
30 | ||
31 | constructor: function(tree, params){ | |
32 | // summary: | |
33 | // Initialization | |
34 | // tags: | |
35 | // private | |
36 | ||
37 | this.selection={}; | |
38 | this.anchor = null; | |
39 | ||
40 | dijit.setWaiState(this.tree.domNode, "multiselect", !this.singular); | |
41 | ||
42 | this.events.push( | |
43 | dojo.connect(this.tree.domNode, "onmousedown", this,"onMouseDown"), | |
44 | dojo.connect(this.tree.domNode, "onmouseup", this,"onMouseUp"), | |
45 | dojo.connect(this.tree.domNode, "onmousemove", this,"onMouseMove") | |
46 | ); | |
47 | }, | |
48 | ||
49 | // singular: Boolean | |
50 | // Allows selection of only one element, if true. | |
51 | // Tree hasn't been tested in singular=true mode, unclear if it works. | |
52 | singular: false, | |
53 | ||
54 | // methods | |
55 | getSelectedTreeNodes: function(){ | |
56 | // summary: | |
57 | // Returns a list of selected node(s). | |
58 | // Used by dndSource on the start of a drag. | |
59 | // tags: | |
60 | // protected | |
61 | var nodes=[], sel = this.selection; | |
62 | for(var i in sel){ | |
63 | nodes.push(sel[i]); | |
64 | } | |
65 | return nodes; | |
66 | }, | |
67 | ||
68 | selectNone: function(){ | |
69 | // summary: | |
70 | // Unselects all items | |
71 | // tags: | |
72 | // private | |
73 | ||
74 | this.setSelection([]); | |
75 | return this; // self | |
76 | }, | |
77 | ||
78 | destroy: function(){ | |
79 | // summary: | |
80 | // Prepares the object to be garbage-collected | |
81 | this.inherited(arguments); | |
82 | this.selection = this.anchor = null; | |
83 | }, | |
84 | addTreeNode: function(/*dijit._TreeNode*/node, /*Boolean?*/isAnchor){ | |
85 | // summary | |
86 | // add node to current selection | |
87 | // node: Node | |
88 | // node to add | |
89 | // isAnchor: Boolean | |
90 | // Whether the node should become anchor. | |
91 | ||
92 | this.setSelection(this.getSelectedTreeNodes().concat( [node] )); | |
93 | if(isAnchor){ this.anchor = node; } | |
94 | return node; | |
95 | }, | |
96 | removeTreeNode: function(/*dijit._TreeNode*/node){ | |
97 | // summary | |
98 | // remove node from current selection | |
99 | // node: Node | |
100 | // node to remove | |
101 | this.setSelection(this._setDifference(this.getSelectedTreeNodes(), [node])) | |
102 | return node; | |
103 | }, | |
104 | isTreeNodeSelected: function(/*dijit._TreeNode*/node){ | |
105 | // summary | |
106 | // return true if node is currently selected | |
107 | // node: Node | |
108 | // the node to check whether it's in the current selection | |
109 | ||
110 | return node.id && !!this.selection[node.id]; | |
111 | }, | |
112 | setSelection: function(/*dijit._treeNode[]*/ newSelection){ | |
113 | // summary | |
114 | // set the list of selected nodes to be exactly newSelection. All changes to the | |
115 | // selection should be passed through this function, which ensures that derived | |
116 | // attributes are kept up to date. Anchor will be deleted if it has been removed | |
117 | // from the selection, but no new anchor will be added by this function. | |
118 | // newSelection: Node[] | |
119 | // list of tree nodes to make selected | |
120 | var oldSelection = this.getSelectedTreeNodes(); | |
121 | dojo.forEach(this._setDifference(oldSelection, newSelection), dojo.hitch(this, function(node){ | |
122 | node.setSelected(false); | |
123 | if(this.anchor == node){ | |
124 | delete this.anchor; | |
125 | } | |
126 | delete this.selection[node.id]; | |
127 | })); | |
128 | dojo.forEach(this._setDifference(newSelection, oldSelection), dojo.hitch(this, function(node){ | |
129 | node.setSelected(true); | |
130 | this.selection[node.id] = node; | |
131 | })); | |
132 | this._updateSelectionProperties(); | |
133 | }, | |
134 | _setDifference: function(xs,ys){ | |
135 | // summary | |
136 | // Returns a copy of xs which lacks any objects | |
137 | // occurring in ys. Checks for membership by | |
138 | // modifying and then reading the object, so it will | |
139 | // not properly handle sets of numbers or strings. | |
140 | ||
141 | dojo.forEach(ys, function(y){ y.__exclude__ = true; }); | |
142 | var ret = dojo.filter(xs, function(x){ return !x.__exclude__; }); | |
143 | ||
144 | // clean up after ourselves. | |
145 | dojo.forEach(ys, function(y){ delete y['__exclude__'] }); | |
146 | return ret; | |
147 | }, | |
148 | _updateSelectionProperties: function() { | |
149 | // summary | |
150 | // Update the following tree properties from the current selection: | |
151 | // path[s], selectedItem[s], selectedNode[s] | |
152 | ||
153 | var selected = this.getSelectedTreeNodes(); | |
154 | var paths = [], nodes = []; | |
155 | dojo.forEach(selected, function(node) { | |
156 | nodes.push(node); | |
157 | paths.push(node.getTreePath()); | |
158 | }); | |
159 | var items = dojo.map(nodes,function(node) { return node.item; }); | |
160 | this.tree._set("paths", paths); | |
161 | this.tree._set("path", paths[0] || []); | |
162 | this.tree._set("selectedNodes", nodes); | |
163 | this.tree._set("selectedNode", nodes[0] || null); | |
164 | this.tree._set("selectedItems", items); | |
165 | this.tree._set("selectedItem", items[0] || null); | |
166 | }, | |
167 | // mouse events | |
168 | onMouseDown: function(e){ | |
169 | // summary: | |
170 | // Event processor for onmousedown | |
171 | // e: Event | |
172 | // mouse event | |
173 | // tags: | |
174 | // protected | |
175 | ||
176 | // ignore click on expando node | |
177 | if(!this.current || this.tree.isExpandoNode( e.target, this.current)){ return; } | |
178 | ||
179 | if(e.button == dojo.mouseButtons.RIGHT){ return; } // ignore right-click | |
180 | ||
181 | dojo.stopEvent(e); | |
182 | ||
183 | var treeNode = this.current, | |
184 | copy = dojo.isCopyKey(e), id = treeNode.id; | |
185 | ||
186 | // if shift key is not pressed, and the node is already in the selection, | |
187 | // delay deselection until onmouseup so in the case of DND, deselection | |
188 | // will be canceled by onmousemove. | |
189 | if(!this.singular && !e.shiftKey && this.selection[id]){ | |
190 | this._doDeselect = true; | |
191 | return; | |
192 | }else{ | |
193 | this._doDeselect = false; | |
194 | } | |
195 | this.userSelect(treeNode, copy, e.shiftKey); | |
196 | }, | |
197 | ||
198 | onMouseUp: function(e){ | |
199 | // summary: | |
200 | // Event processor for onmouseup | |
201 | // e: Event | |
202 | // mouse event | |
203 | // tags: | |
204 | // protected | |
205 | ||
206 | // _doDeselect is the flag to indicate that the user wants to either ctrl+click on | |
207 | // a already selected item (to deselect the item), or click on a not-yet selected item | |
208 | // (which should remove all current selection, and add the clicked item). This can not | |
209 | // be done in onMouseDown, because the user may start a drag after mousedown. By moving | |
210 | // the deselection logic here, the user can drags an already selected item. | |
211 | if(!this._doDeselect){ return; } | |
212 | this._doDeselect = false; | |
213 | this.userSelect(this.current, dojo.isCopyKey( e ), e.shiftKey); | |
214 | }, | |
215 | onMouseMove: function(e){ | |
216 | // summary | |
217 | // event processor for onmousemove | |
218 | // e: Event | |
219 | // mouse event | |
220 | this._doDeselect = false; | |
221 | }, | |
222 | ||
223 | userSelect: function(node, multi, range){ | |
224 | // summary: | |
225 | // Add or remove the given node from selection, responding | |
226 | // to a user action such as a click or keypress. | |
227 | // multi: Boolean | |
228 | // Indicates whether this is meant to be a multi-select action (e.g. ctrl-click) | |
229 | // range: Boolean | |
230 | // Indicates whether this is meant to be a ranged action (e.g. shift-click) | |
231 | // tags: | |
232 | // protected | |
233 | ||
234 | if(this.singular){ | |
235 | if(this.anchor == node && multi){ | |
236 | this.selectNone(); | |
237 | }else{ | |
238 | this.setSelection([node]); | |
239 | this.anchor = node; | |
240 | } | |
241 | }else{ | |
242 | if(range && this.anchor){ | |
243 | var cr = dijit.tree._compareNodes(this.anchor.rowNode, node.rowNode), | |
244 | begin, end, anchor = this.anchor; | |
245 | ||
246 | if(cr < 0){ //current is after anchor | |
247 | begin = anchor; | |
248 | end = node; | |
249 | }else{ //current is before anchor | |
250 | begin = node; | |
251 | end = anchor; | |
252 | } | |
253 | nodes = []; | |
254 | //add everything betweeen begin and end inclusively | |
255 | while(begin != end) { | |
256 | nodes.push(begin) | |
257 | begin = this.tree._getNextNode(begin); | |
258 | } | |
259 | nodes.push(end) | |
260 | ||
261 | this.setSelection(nodes); | |
262 | }else{ | |
263 | if( this.selection[ node.id ] && multi ) { | |
264 | this.removeTreeNode( node ); | |
265 | } else if(multi) { | |
266 | this.addTreeNode(node, true); | |
267 | } else { | |
268 | this.setSelection([node]); | |
269 | this.anchor = node; | |
270 | } | |
271 | } | |
272 | } | |
273 | }, | |
274 | ||
275 | forInSelectedItems: function(/*Function*/ f, /*Object?*/ o){ | |
276 | // summary: | |
277 | // Iterates over selected items; | |
278 | // see `dojo.dnd.Container.forInItems()` for details | |
279 | o = o || dojo.global; | |
280 | for(var id in this.selection){ | |
281 | // console.log("selected item id: " + id); | |
282 | f.call(o, this.getItem(id), id, this); | |
283 | } | |
284 | } | |
285 | }); | |
286 | ||
2f01fe57 | 287 | } |