Mouse.js 14 KB

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