]> git.wh0rd.org - tt-rss.git/blob - lib/dijit/tree/_dndSelector.js
upgrade Dojo to 1.6.1
[tt-rss.git] / lib / dijit / tree / _dndSelector.js
1 /*
2 Copyright (c) 2004-2011, The Dojo Foundation All Rights Reserved.
3 Available via Academic Free License >= 2.1 OR the modified BSD license.
4 see: http://dojotoolkit.org/license for details
5 */
6
7
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;
10 dojo.provide("dijit.tree._dndSelector");
11 dojo.require("dojo.dnd.common");
12 dojo.require("dijit.tree._dndContainer");
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
287 }