1 | dojo.provide("dojox.drawing.manager.Mouse"); |
---|
2 | |
---|
3 | dojox.drawing.manager.Mouse = dojox.drawing.util.oo.declare( |
---|
4 | // summary: |
---|
5 | // Master object (instance) that tracks mouse |
---|
6 | // events. A new instance is created for each |
---|
7 | // Drawing object. |
---|
8 | // description: |
---|
9 | // You could connect to any method or event in this |
---|
10 | // class, but it is designed to have the object |
---|
11 | // 'registered'. All objects with the current event |
---|
12 | // will be called directly. |
---|
13 | // |
---|
14 | // Custom events are used often. In addition to |
---|
15 | // standard events onDown, onUp, onDrag, etc, if |
---|
16 | // a certain object is clicked upon (or dragged, etc), |
---|
17 | // that object's drawingType will create the custom event, |
---|
18 | // such as onAnchorDown, or onStencilDown. |
---|
19 | // |
---|
20 | function(/* Object */options){ |
---|
21 | this.util = options.util; |
---|
22 | this.keys = options.keys; |
---|
23 | this.id = options.id || this.util.uid("mouse"); |
---|
24 | this.currentNodeId = ""; |
---|
25 | this.registered = {}; |
---|
26 | }, |
---|
27 | |
---|
28 | { |
---|
29 | // doublClickSpeed: Number |
---|
30 | // Milliseconds between clicks to |
---|
31 | // register as for onDoubleClick |
---|
32 | doublClickSpeed:400, |
---|
33 | |
---|
34 | // private properties |
---|
35 | |
---|
36 | _lastx:0, |
---|
37 | _lasty:0, |
---|
38 | __reg:0, |
---|
39 | _downOnCanvas:false, |
---|
40 | |
---|
41 | /*===== |
---|
42 | CustomEventMethod: function(){ |
---|
43 | // summary: |
---|
44 | // The custom event method that an Object that has |
---|
45 | // registered with manager.Mouse can receive. |
---|
46 | // Can contain any or all of the following methods |
---|
47 | // and they will be called as mouse events. All events |
---|
48 | // will be sent a EventObject event object. |
---|
49 | // NOTE: |
---|
50 | // Events happen anywhere in the document unless |
---|
51 | // otherwise noted. |
---|
52 | // |
---|
53 | // onMove |
---|
54 | // Fires on mousemove when mouse is up |
---|
55 | // onDown |
---|
56 | // Fires on mousedown *on the canvas* |
---|
57 | // onDrag |
---|
58 | // Fires on mousemove when mouse is down |
---|
59 | // onUp |
---|
60 | // Fires on mouseup, anywhere in the document |
---|
61 | // onStencilDown |
---|
62 | // Fired on mousedown on a Stencil |
---|
63 | // onStencilDrag |
---|
64 | // Fired when mouse moves and mose is down on a Stencil |
---|
65 | // onStencilUp |
---|
66 | // Fired on mouseup off of a Stencil |
---|
67 | // on[Custom]Up|Down|Move |
---|
68 | // Custom events can bet set and fired by setting a |
---|
69 | // different drawingType on a Stencil, or by calling |
---|
70 | // setEventMode(customEventName) |
---|
71 | }, |
---|
72 | EventObject: function(){ |
---|
73 | // summary: |
---|
74 | // The custom event object that is sent to registered objects |
---|
75 | // and their respective methods. |
---|
76 | // NOTE: Most event objects are the same with the exception |
---|
77 | // of the onDown events, which have fewer. |
---|
78 | // |
---|
79 | // All event properties included onDown: |
---|
80 | // |
---|
81 | // id: String |
---|
82 | // Id of the focused object |
---|
83 | // pageX: Number |
---|
84 | // The X coordinate of the mouse from the left side of |
---|
85 | // the document. |
---|
86 | // pageY: Number |
---|
87 | // The Y coordinate of the mouse from the top of |
---|
88 | // the document. |
---|
89 | // x: Number |
---|
90 | // The X coordinate of the mouse from the left side |
---|
91 | // of the canvas |
---|
92 | // y: Number |
---|
93 | // The Y coordinate of the mouse from the top |
---|
94 | // of the canvas |
---|
95 | // |
---|
96 | // These event properties are *not* in onDown: |
---|
97 | // |
---|
98 | // last: Object |
---|
99 | // The x and y coordinates of the last mousemove |
---|
100 | // relative to the canvas |
---|
101 | // move: Object |
---|
102 | // The x and y amounts the mouse moved since the last event |
---|
103 | // orgX: Number |
---|
104 | // The left side of the canvas from the side of the document |
---|
105 | // orgY: Number |
---|
106 | // The top of the canvas from the top of the document |
---|
107 | // scroll: Object |
---|
108 | // The 'top' and 'left' scroll amounts of the canvas. |
---|
109 | // start: Object |
---|
110 | // The x and y coordinates of the mousedown event |
---|
111 | // withinCanvas: Boolean |
---|
112 | // Whether the event happened within the Canvas or not |
---|
113 | |
---|
114 | }, |
---|
115 | =====*/ |
---|
116 | |
---|
117 | init: function(/* HTMLNode*/node){ |
---|
118 | // summary: |
---|
119 | // Internal. Initializes mouse. |
---|
120 | // |
---|
121 | this.container = node; |
---|
122 | this.setCanvas(); |
---|
123 | var c; |
---|
124 | var _isDown = false; |
---|
125 | dojo.connect(this.container, "rightclick", this, function(evt){ |
---|
126 | console.warn("RIGHTCLICK") |
---|
127 | }); |
---|
128 | |
---|
129 | dojo.connect(document.body, "mousedown", this, function(evt){ |
---|
130 | //evt.preventDefault(); |
---|
131 | //dojo.stopEvent(evt); |
---|
132 | }); |
---|
133 | |
---|
134 | dojo.connect(this.container, "mousedown", this, function(evt){ |
---|
135 | this.down(evt); |
---|
136 | // Right click shouldn't trigger drag |
---|
137 | if(evt.button != dojo.mouseButtons.RIGHT){ |
---|
138 | _isDown = true; |
---|
139 | c = dojo.connect(document, "mousemove", this, "drag"); |
---|
140 | } |
---|
141 | }); |
---|
142 | dojo.connect(document, "mouseup", this, function(evt){ |
---|
143 | dojo.disconnect(c); |
---|
144 | _isDown = false; |
---|
145 | this.up(evt); |
---|
146 | }); |
---|
147 | dojo.connect(document, "mousemove", this, function(evt){ |
---|
148 | if(!_isDown){ |
---|
149 | this.move(evt); |
---|
150 | } |
---|
151 | }); |
---|
152 | dojo.connect(this.keys, "onEsc", this, function(evt){ |
---|
153 | this._dragged = false; |
---|
154 | }); |
---|
155 | }, |
---|
156 | |
---|
157 | setCanvas: function(){ |
---|
158 | // summary: |
---|
159 | // Internal. Sets canvas position |
---|
160 | var pos = dojo.coords(this.container.parentNode); |
---|
161 | this.origin = dojo.clone(pos); |
---|
162 | }, |
---|
163 | |
---|
164 | scrollOffset: function(){ |
---|
165 | // summary: |
---|
166 | // Gets scroll offset of canvas |
---|
167 | return { |
---|
168 | top:this.container.parentNode.scrollTop, |
---|
169 | left:this.container.parentNode.scrollLeft |
---|
170 | }; // Object |
---|
171 | }, |
---|
172 | |
---|
173 | resize: function(width,height){ |
---|
174 | if(this.origin){ |
---|
175 | this.origin.w=width; |
---|
176 | this.origin.h=height; |
---|
177 | } |
---|
178 | }, |
---|
179 | |
---|
180 | register: function(/* Object*/scope){ |
---|
181 | // summary: |
---|
182 | // All objects (Stencils) should register here if they |
---|
183 | // use mouse events. When registering, the object will |
---|
184 | // be called if it has that method. |
---|
185 | // argument: |
---|
186 | // The object to be called |
---|
187 | // Returns: handle |
---|
188 | // Keep the handle to be used for disconnection. |
---|
189 | // See: CustomEventMethod and EventObject |
---|
190 | // |
---|
191 | var handle = scope.id || "reg_"+(this.__reg++); |
---|
192 | if(!this.registered[handle]){ this.registered[handle] = scope; } |
---|
193 | return handle; // String |
---|
194 | }, |
---|
195 | unregister: function(handle){ |
---|
196 | // summary: |
---|
197 | // Disconnects object. Mouse events are no longer |
---|
198 | // called for it. |
---|
199 | if(!this.registered[handle]){ return; } |
---|
200 | delete this.registered[handle]; |
---|
201 | }, |
---|
202 | |
---|
203 | _broadcastEvent:function(strEvt, obj){ |
---|
204 | // summary: |
---|
205 | // Fire events to all registered objects. |
---|
206 | // |
---|
207 | //console.log("mouse.broadcast:", strEvt, obj) |
---|
208 | for(var nm in this.registered){ |
---|
209 | if(this.registered[nm][strEvt]) this.registered[nm][strEvt](obj); |
---|
210 | } |
---|
211 | }, |
---|
212 | |
---|
213 | onDown: function(obj){ |
---|
214 | // summary: |
---|
215 | // Create on[xx]Down event and send to broadcaster. |
---|
216 | // Could be connected to. |
---|
217 | //console.info("onDown:", this.eventName("down")) |
---|
218 | this._broadcastEvent(this.eventName("down"), obj); |
---|
219 | }, |
---|
220 | |
---|
221 | onDrag: function(obj){ |
---|
222 | // summary: |
---|
223 | // Create on[xx]Drag event and send to broadcaster. |
---|
224 | // Could be connected to. |
---|
225 | // |
---|
226 | var nm = this.eventName("drag"); |
---|
227 | if(this._selected && nm == "onDrag"){ |
---|
228 | nm = "onStencilDrag" |
---|
229 | } |
---|
230 | this._broadcastEvent(nm, obj); |
---|
231 | }, |
---|
232 | |
---|
233 | onMove: function(obj){ |
---|
234 | // summary: |
---|
235 | // Create onMove event and send to broadcaster. |
---|
236 | // Could be connected to. |
---|
237 | // Note: onMove never uses a custom event |
---|
238 | // Note: onMove is currently not enabled in the app. |
---|
239 | // |
---|
240 | this._broadcastEvent("onMove", obj); |
---|
241 | }, |
---|
242 | |
---|
243 | overName: function(obj,evt){ |
---|
244 | var nm = obj.id.split("."); |
---|
245 | evt = evt.charAt(0).toUpperCase() + evt.substring(1); |
---|
246 | if(nm[0] == "dojox" && (dojox.drawing.defaults.clickable || !dojox.drawing.defaults.clickMode)){ |
---|
247 | return "onStencil"+evt; |
---|
248 | }else{ |
---|
249 | return "on"+evt; |
---|
250 | } |
---|
251 | |
---|
252 | }, |
---|
253 | |
---|
254 | onOver: function(obj){ |
---|
255 | // summary: |
---|
256 | // |
---|
257 | this._broadcastEvent(this.overName(obj,"over"), obj); |
---|
258 | }, |
---|
259 | |
---|
260 | onOut: function(obj){ |
---|
261 | // summary: |
---|
262 | // |
---|
263 | this._broadcastEvent(this.overName(obj,"out"), obj); |
---|
264 | }, |
---|
265 | |
---|
266 | onUp: function(obj){ |
---|
267 | // summary: |
---|
268 | // Create on[xx]Up event and send to broadcaster. |
---|
269 | // Could be connected to. |
---|
270 | // |
---|
271 | // blocking first click-off (deselect), largely for TextBlock |
---|
272 | // TODO: should have param to make this optional? |
---|
273 | var nm = this.eventName("up"); |
---|
274 | |
---|
275 | if(nm == "onStencilUp"){ |
---|
276 | this._selected = true; |
---|
277 | }else if(this._selected && nm == "onUp"){ ////////////////////////////////////////// |
---|
278 | nm = "onStencilUp"; |
---|
279 | this._selected = false; |
---|
280 | } |
---|
281 | |
---|
282 | console.info("Up Event:", this.id, nm, "id:", obj.id); |
---|
283 | this._broadcastEvent(nm, obj); |
---|
284 | |
---|
285 | // Silverlight double-click handled in Silverlight class |
---|
286 | if(dojox.gfx.renderer == "silverlight"){ return; } |
---|
287 | |
---|
288 | // Check Double Click |
---|
289 | // If a double click is detected, the onDoubleClick event fires, |
---|
290 | // but does not replace the normal event. They both fire. |
---|
291 | this._clickTime = new Date().getTime(); |
---|
292 | if(this._lastClickTime){ |
---|
293 | if(this._clickTime-this._lastClickTime<this.doublClickSpeed){ |
---|
294 | var dnm = this.eventName("doubleClick"); |
---|
295 | console.warn("DOUBLE CLICK", dnm, obj); |
---|
296 | this._broadcastEvent(dnm, obj); |
---|
297 | }else{ |
---|
298 | //console.log(" slow:", this._clickTime-this._lastClickTime) |
---|
299 | } |
---|
300 | } |
---|
301 | this._lastClickTime = this._clickTime; |
---|
302 | |
---|
303 | }, |
---|
304 | |
---|
305 | zoom: 1, |
---|
306 | setZoom: function(zoom){ |
---|
307 | // summary: |
---|
308 | // Internal. Sets the mouse zoom percentage to |
---|
309 | // that of the canvas |
---|
310 | this.zoom = 1/zoom; |
---|
311 | }, |
---|
312 | |
---|
313 | setEventMode: function(mode){ |
---|
314 | // summary: |
---|
315 | // Sets the mouse mode s that custom events can be called. |
---|
316 | // Also can 'disable' events by using a bogus mode: |
---|
317 | // | mouse.setEventMode("DISABLED") |
---|
318 | // (unless any object subscribes to this event, |
---|
319 | // it is effectively disabled) |
---|
320 | // |
---|
321 | this.mode = mode ? "on" + mode.charAt(0).toUpperCase() + mode.substring(1) : ""; |
---|
322 | }, |
---|
323 | |
---|
324 | eventName: function(name){ |
---|
325 | // summary: |
---|
326 | // Internal. Determine the event name |
---|
327 | // |
---|
328 | name = name.charAt(0).toUpperCase() + name.substring(1); |
---|
329 | if(this.mode){ |
---|
330 | if(this.mode == "onPathEdit"){ |
---|
331 | return "on"+name; |
---|
332 | } |
---|
333 | if(this.mode == "onUI"){ |
---|
334 | //return "on"+name; |
---|
335 | } |
---|
336 | return this.mode + name; |
---|
337 | }else{ |
---|
338 | //Allow a mode where stencils aren't clickable |
---|
339 | if(!dojox.drawing.defaults.clickable && dojox.drawing.defaults.clickMode){return "on"+name;} |
---|
340 | var dt = !this.drawingType || this.drawingType=="surface" || this.drawingType=="canvas" ? "" : this.drawingType; |
---|
341 | var t = !dt ? "" : dt.charAt(0).toUpperCase() + dt.substring(1); |
---|
342 | return "on"+t+name; |
---|
343 | } |
---|
344 | }, |
---|
345 | |
---|
346 | up: function(evt){ |
---|
347 | // summary: |
---|
348 | // Internal. Create onUp event |
---|
349 | // |
---|
350 | this.onUp(this.create(evt)); |
---|
351 | }, |
---|
352 | |
---|
353 | down: function(evt){ |
---|
354 | // summary: |
---|
355 | // Internal. Create onDown event |
---|
356 | // |
---|
357 | this._downOnCanvas = true; |
---|
358 | var sc = this.scrollOffset(); |
---|
359 | var dim = this._getXY(evt); |
---|
360 | this._lastpagex = dim.x; |
---|
361 | this._lastpagey = dim.y; |
---|
362 | var o = this.origin; |
---|
363 | var x = dim.x - o.x + sc.left; |
---|
364 | var y = dim.y - o.y + sc.top; |
---|
365 | |
---|
366 | var withinCanvas = x>=0 && y>=0 && x<=o.w && y<=o.h; |
---|
367 | x*= this.zoom; |
---|
368 | y*= this.zoom; |
---|
369 | |
---|
370 | o.startx = x; |
---|
371 | o.starty = y; |
---|
372 | this._lastx = x; |
---|
373 | this._lasty = y; |
---|
374 | |
---|
375 | this.drawingType = this.util.attr(evt, "drawingType") || ""; |
---|
376 | var id = this._getId(evt); |
---|
377 | //console.log("DOWN:", this.id, id, withinCanvas); |
---|
378 | //console.log("this.drawingType:", this.drawingType); |
---|
379 | |
---|
380 | if(evt.button == dojo.mouseButtons.RIGHT && this.id == "mse"){ |
---|
381 | //Allow right click events to bubble for context menus |
---|
382 | }else{ |
---|
383 | evt.preventDefault(); |
---|
384 | dojo.stopEvent(evt); |
---|
385 | } |
---|
386 | this.onDown({ |
---|
387 | mid:this.id, |
---|
388 | x:x, |
---|
389 | y:y, |
---|
390 | pageX:dim.x, |
---|
391 | pageY:dim.y, |
---|
392 | withinCanvas:withinCanvas, |
---|
393 | id:id |
---|
394 | }); |
---|
395 | |
---|
396 | }, |
---|
397 | over: function(obj){ |
---|
398 | // summary: |
---|
399 | // Internal. |
---|
400 | // |
---|
401 | this.onOver(obj); |
---|
402 | }, |
---|
403 | out: function(obj){ |
---|
404 | // summary: |
---|
405 | // Internal. |
---|
406 | // |
---|
407 | this.onOut(obj); |
---|
408 | }, |
---|
409 | move: function(evt){ |
---|
410 | // summary: |
---|
411 | // Internal. |
---|
412 | // |
---|
413 | var obj = this.create(evt); |
---|
414 | if(this.id=="MUI"){ |
---|
415 | //console.log("obj.id:", obj.id, "was:", this.currentNodeId) |
---|
416 | } |
---|
417 | if(obj.id != this.currentNodeId){ |
---|
418 | // TODO: I wonder if an ID is good enough |
---|
419 | // that would avoid the mixin |
---|
420 | var outObj = {}; |
---|
421 | for(var nm in obj){ |
---|
422 | outObj[nm] = obj[nm]; |
---|
423 | } |
---|
424 | outObj.id = this.currentNodeId; |
---|
425 | this.currentNodeId && this.out(outObj); |
---|
426 | obj.id && this.over(obj); |
---|
427 | this.currentNodeId = obj.id; |
---|
428 | } |
---|
429 | this.onMove(obj); |
---|
430 | }, |
---|
431 | drag: function(evt){ |
---|
432 | // summary: |
---|
433 | // Internal. Create onDrag event |
---|
434 | this.onDrag(this.create(evt, true)); |
---|
435 | }, |
---|
436 | create: function(evt, squelchErrors){ |
---|
437 | // summary: |
---|
438 | // Internal. Create EventObject |
---|
439 | // |
---|
440 | var sc = this.scrollOffset(); |
---|
441 | var dim = this._getXY(evt); |
---|
442 | |
---|
443 | var pagex = dim.x; |
---|
444 | var pagey = dim.y; |
---|
445 | |
---|
446 | var o = this.origin; |
---|
447 | var x = dim.x - o.x + sc.left; |
---|
448 | var y = dim.y - o.y + sc.top; |
---|
449 | |
---|
450 | var withinCanvas = x>=0 && y>=0 && x<=o.w && y<=o.h; |
---|
451 | x*= this.zoom; |
---|
452 | y*= this.zoom; |
---|
453 | |
---|
454 | var id = withinCanvas ? this._getId(evt, squelchErrors) : ""; |
---|
455 | var ret = { |
---|
456 | mid:this.id, |
---|
457 | x:x, |
---|
458 | y:y, |
---|
459 | pageX:dim.x, |
---|
460 | pageY:dim.y, |
---|
461 | page:{ |
---|
462 | x:dim.x, |
---|
463 | y:dim.y |
---|
464 | }, |
---|
465 | orgX:o.x, |
---|
466 | orgY:o.y, |
---|
467 | last:{ |
---|
468 | x: this._lastx, |
---|
469 | y: this._lasty |
---|
470 | }, |
---|
471 | start:{ |
---|
472 | x: this.origin.startx, //+ sc.left, |
---|
473 | y: this.origin.starty //+ sc.top |
---|
474 | }, |
---|
475 | move:{ |
---|
476 | x:pagex - this._lastpagex, |
---|
477 | y:pagey - this._lastpagey |
---|
478 | }, |
---|
479 | scroll:sc, |
---|
480 | id:id, |
---|
481 | withinCanvas:withinCanvas |
---|
482 | }; |
---|
483 | |
---|
484 | //console.warn("MSE LAST:", x-this._lastx, y-this._lasty) |
---|
485 | this._lastx = x; |
---|
486 | this._lasty = y; |
---|
487 | this._lastpagex = pagex; |
---|
488 | this._lastpagey = pagey; |
---|
489 | dojo.stopEvent(evt); |
---|
490 | return ret; //Object |
---|
491 | }, |
---|
492 | _getId: function(evt, squelchErrors){ |
---|
493 | // summary: |
---|
494 | // Internal. Gets ID of focused node. |
---|
495 | return this.util.attr(evt, "id", null, squelchErrors); // String |
---|
496 | }, |
---|
497 | _getXY: function(evt){ |
---|
498 | // summary: |
---|
499 | // Internal. Gets mouse coords to page. |
---|
500 | return {x:evt.pageX, y:evt.pageY}; // Object |
---|
501 | }, |
---|
502 | |
---|
503 | setCursor: function(cursor,/* HTMLNode*/node){ |
---|
504 | // summary: |
---|
505 | // Sets the cursor for a given node. If no |
---|
506 | // node is specified the containing node is used. |
---|
507 | if(!node){ |
---|
508 | dojo.style(this.container, "cursor", cursor); |
---|
509 | }else{ |
---|
510 | dojo.style(node, "cursor", cursor); |
---|
511 | } |
---|
512 | } |
---|
513 | } |
---|
514 | ); |
---|