Anchors.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475
  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.Anchors"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  7. dojo._hasResource["dojox.drawing.manager.Anchors"] = true;
  8. dojo.provide("dojox.drawing.manager.Anchors");
  9. dojox.drawing.manager.Anchors = dojox.drawing.util.oo.declare(
  10. // summary:
  11. // Creates and manages the anchor points that are attached to
  12. // (usually) the corners of a Stencil.
  13. // description:
  14. // Used internally, but there are some things that should be known:
  15. // Anchors attach to a Stencil's 'points' (See stencil.points)
  16. // To not display an anchor on a certain point, add noAnchor:true
  17. // to the point.
  18. function(/* dojox.__stencilArgs */options){
  19. // arguments: See stencil._Base
  20. this.mouse = options.mouse;
  21. this.undo = options.undo;
  22. this.util = options.util;
  23. this.drawing = options.drawing;
  24. this.items = {};
  25. },
  26. {
  27. onAddAnchor: function(/*Anchor*/anchor){
  28. // summary:
  29. // Event fires when anchor is created
  30. },
  31. onReset: function(/*Stencil*/stencil){
  32. // summary:
  33. // Event fires when an anchor's reset method is called
  34. //
  35. // a desperate hack in order to get the anchor point to reset.
  36. // FIXME: Is this still used? I think its item.deselect();item.select();
  37. var st = this.util.byId("drawing").stencils;
  38. st.onDeselect(stencil);
  39. st.onSelect(stencil);
  40. },
  41. onRenderStencil: function(){
  42. // summary:
  43. // Event fires when an anchor calls a Stencil's render method
  44. //
  45. for(var nm in this.items){
  46. dojo.forEach(this.items[nm].anchors, function(a){
  47. a.shape.moveToFront();
  48. });
  49. }
  50. },
  51. onTransformPoint: function(/*Anchor*/anchor){
  52. // summary:
  53. // Event fired on anchor drag
  54. //
  55. // If anchors are a "group", it's corresponding anchor
  56. // is set. All anchors then moved to front.
  57. var anchors = this.items[anchor.stencil.id].anchors;
  58. var item = this.items[anchor.stencil.id].item;
  59. var pts = [];
  60. dojo.forEach(anchors, function(a, i){
  61. if(anchor.id == a.id || anchor.stencil.anchorType!="group"){
  62. // nothing
  63. }else{
  64. if(anchor.org.y == a.org.y){
  65. a.setPoint({
  66. dx: 0,
  67. dy: anchor.shape.getTransform().dy - a.shape.getTransform().dy
  68. });
  69. }else if(anchor.org.x == a.org.x){
  70. a.setPoint({
  71. dx: anchor.shape.getTransform().dx - a.shape.getTransform().dx,
  72. dy: 0
  73. });
  74. }
  75. a.shape.moveToFront();
  76. }
  77. var mx = a.shape.getTransform();
  78. pts.push({x:mx.dx + a.org.x, y:mx.dy+ a.org.y});
  79. if(a.point.t){
  80. pts[pts.length-1].t = a.point.t;
  81. }
  82. }, this);
  83. item.setPoints(pts);
  84. item.onTransform(anchor);
  85. this.onRenderStencil();
  86. },
  87. onAnchorUp: function(/*Anchor*/anchor){
  88. // summary:
  89. // Event fired on anchor mouseup
  90. },
  91. onAnchorDown: function(/*Anchor*/anchor){
  92. // summary:
  93. // Event fired on anchor mousedown
  94. },
  95. onAnchorDrag: function(/*Anchor*/anchor){
  96. // summary:
  97. // Event fired when anchor is moved
  98. },
  99. onChangeStyle: function(/*Object*/stencil){
  100. // summary:
  101. // if the Stencil changes color while were's selected
  102. // this moves the anchors to the back. Fix it.
  103. for(var nm in this.items){
  104. dojo.forEach(this.items[nm].anchors, function(a){
  105. a.shape.moveToFront();
  106. });
  107. }
  108. },
  109. add: function(/*Stencil*/item){
  110. // summary:
  111. // Creates anchor points on a Stencil, based on the
  112. // Stencil's points.
  113. //
  114. this.items[item.id] = {
  115. item:item,
  116. anchors:[]
  117. };
  118. if(item.anchorType=="none"){ return; }
  119. var pts = item.points;
  120. dojo.forEach(pts, function(p, i){
  121. if(p.noAnchor){ return; }
  122. if(i==0 || i == item.points.length-1){
  123. console.log("ITEM TYPE:", item.type, item.shortType);
  124. }
  125. var a = new dojox.drawing.manager.Anchor({stencil:item, point:p, pointIdx:i, mouse:this.mouse, util:this.util});
  126. this.items[item.id]._cons = [
  127. dojo.connect(a, "onRenderStencil", this, "onRenderStencil"),
  128. dojo.connect(a, "reset", this, "onReset"),
  129. dojo.connect(a, "onAnchorUp", this, "onAnchorUp"),
  130. dojo.connect(a, "onAnchorDown", this, "onAnchorDown"),
  131. dojo.connect(a, "onAnchorDrag", this, "onAnchorDrag"),
  132. dojo.connect(a, "onTransformPoint", this, "onTransformPoint"),
  133. // FIXME: this will fire for each anchor. yech.
  134. dojo.connect(item, "onChangeStyle", this, "onChangeStyle")
  135. ];
  136. this.items[item.id].anchors.push(a);
  137. this.onAddAnchor(a);
  138. }, this);
  139. if(item.shortType=="path"){
  140. // check if we have a double-point of a closed-curve-path
  141. var f = pts[0], l = pts[pts.length-1], a = this.items[item.id].anchors;
  142. if(f.x ==l.x && f.y==l.y){
  143. console.warn("LINK ANVHROS", a[0], a[a.length-1]);
  144. a[0].linkedAnchor = a[a.length-1];
  145. a[a.length-1].linkedAnchor = a[0];
  146. }
  147. }
  148. if(item.anchorType=="group"){
  149. dojo.forEach(this.items[item.id].anchors, function(anchor){
  150. dojo.forEach(this.items[item.id].anchors, function(a){
  151. if(anchor.id != a.id){
  152. if(anchor.org.y == a.org.y){
  153. anchor.x_anchor = a;
  154. }else if(anchor.org.x == a.org.x){
  155. anchor.y_anchor = a;
  156. }
  157. }
  158. },this);
  159. },this);
  160. }
  161. },
  162. remove: function(/*Stencil*/item){
  163. // summary:
  164. // Destroys the anchor points for a Stencil.
  165. //
  166. if(!this.items[item.id]){
  167. return;
  168. }
  169. dojo.forEach(this.items[item.id].anchors, function(a){
  170. a.destroy();
  171. });
  172. dojo.forEach(this.items[item.id]._cons, dojo.disconnect, dojo);
  173. this.items[item.id].anchors = null;
  174. delete this.items[item.id];
  175. }
  176. }
  177. );
  178. dojox.drawing.manager.Anchor = dojox.drawing.util.oo.declare(
  179. // summary:
  180. // An anchor point that is attached to (usually) one of the
  181. // corners of a Stencil.
  182. // Used internally.
  183. function(/* Object */options){
  184. // summary:
  185. // constructor.
  186. // arguments:
  187. // dojox.__stencilArgs plus some additional
  188. // data, like which point this is (pointIdx)
  189. //
  190. this.defaults = dojox.drawing.defaults.copy();
  191. this.mouse = options.mouse;
  192. this.point = options.point;
  193. this.pointIdx = options.pointIdx;
  194. this.util = options.util;
  195. this.id = options.id || this.util.uid("anchor");
  196. this.org = dojo.mixin({}, this.point);
  197. this.stencil = options.stencil;
  198. if(this.stencil.anchorPositionCheck){
  199. this.anchorPositionCheck = dojo.hitch(this.stencil, this.stencil.anchorPositionCheck);
  200. }
  201. if(this.stencil.anchorConstrain){
  202. this.anchorConstrain = dojo.hitch(this.stencil, this.stencil.anchorConstrain);
  203. }
  204. this._zCon = dojo.connect(this.mouse, "setZoom", this, "render");
  205. this.render();
  206. this.connectMouse();
  207. },
  208. {
  209. y_anchor:null,
  210. x_anchor:null,
  211. render: function(){
  212. // summary:
  213. // Creates the anchor point. Unlike most render methods
  214. // in Drawing, this is only called once.
  215. //
  216. this.shape && this.shape.removeShape();
  217. var d = this.defaults.anchors,
  218. z = this.mouse.zoom,
  219. b = d.width * z,
  220. s = d.size * z,
  221. p = s/2,
  222. line = {
  223. width:b,
  224. style:d.style,
  225. color:d.color,
  226. cap:d.cap
  227. };
  228. var _r = {
  229. x: this.point.x-p,
  230. y: this.point.y-p,
  231. width: s,
  232. height: s
  233. };
  234. this.shape = this.stencil.container.createRect(_r)
  235. .setStroke(line)
  236. .setFill(d.fill);
  237. this.shape.setTransform({dx:0, dy:0});
  238. this.util.attr(this, "drawingType", "anchor");
  239. this.util.attr(this, "id", this.id);
  240. },
  241. onRenderStencil: function(/*Anchor*/anchor){
  242. // summary:
  243. // Event fires when an anchor calls a Stencil's render method
  244. },
  245. onTransformPoint: function(/*Anchor*/anchor){
  246. // summary:
  247. // Event fires when an anchor changes the points of a Stencil
  248. },
  249. onAnchorDown: function(/*Mouse.EventObject*/obj){
  250. // summary:
  251. // Event fires for mousedown on anchor
  252. this.selected = obj.id == this.id;
  253. },
  254. onAnchorUp: function(/*Mouse.EventObject*/obj){
  255. // summary:
  256. // Event fires for mouseup on anchor
  257. this.selected = false;
  258. this.stencil.onTransformEnd(this);
  259. },
  260. onAnchorDrag: function(/*Mouse.EventObject*/obj){
  261. // summary:
  262. // Event fires for on dragging of an anchor
  263. if(this.selected){
  264. // mx is the original transform from when the anchor
  265. // was created. It does not change
  266. var mx = this.shape.getTransform();
  267. var pmx = this.shape.getParent().getParent().getTransform();
  268. var marginZero = this.defaults.anchors.marginZero;
  269. var orgx = pmx.dx + this.org.x,
  270. orgy = pmx.dy + this.org.y,
  271. x = obj.x - orgx,
  272. y = obj.y - orgy,
  273. s = this.defaults.anchors.minSize;
  274. var conL, conR, conT, conB;
  275. var chk = this.anchorPositionCheck(x, y, this);
  276. if(chk.x<0){
  277. console.warn("X<0 Shift");
  278. while(this.anchorPositionCheck(x, y, this).x<0){
  279. this.shape.getParent().getParent().applyTransform({dx:2, dy:0});
  280. }
  281. }
  282. if(chk.y<0){
  283. console.warn("Y<0 Shift");
  284. while(this.anchorPositionCheck(x, y, this).y<0){
  285. this.shape.getParent().getParent().applyTransform({dx:0, dy:2});
  286. }
  287. }
  288. if(this.y_anchor){
  289. // prevent y overlap of opposite anchor
  290. if(this.org.y > this.y_anchor.org.y){
  291. // bottom anchor
  292. conT = this.y_anchor.point.y + s - this.org.y;
  293. conB = Infinity;
  294. if(y < conT){
  295. // overlapping other anchor
  296. y = conT;
  297. }
  298. }else{
  299. // top anchor
  300. conT = -orgy + marginZero;
  301. conB = this.y_anchor.point.y - s - this.org.y;
  302. if(y < conT){
  303. // less than zero
  304. y = conT;
  305. }else if(y > conB){
  306. // overlapping other anchor
  307. y = conB;
  308. }
  309. }
  310. }else{
  311. // Lines - check for zero
  312. conT = -orgy + marginZero;
  313. if(y < conT){
  314. // less than zero
  315. y = conT;
  316. }
  317. }
  318. if(this.x_anchor){
  319. // prevent x overlap of opposite anchor
  320. if(this.org.x>this.x_anchor.org.x){
  321. // right anchor
  322. conL = this.x_anchor.point.x + s - this.org.x;
  323. conR = Infinity;
  324. if(x < conL){
  325. // overlapping other anchor
  326. x = conL;
  327. }
  328. }else{
  329. // left anchor
  330. conL = -orgx + marginZero;
  331. conR = this.x_anchor.point.x - s - this.org.x;
  332. if(x < conL){
  333. x = conL;
  334. }else if(x > conR){
  335. // overlapping other anchor
  336. x = conR;
  337. }
  338. }
  339. }else{
  340. // Lines check for zero
  341. conL = -orgx + marginZero;
  342. if(x < conL){
  343. x = conL;
  344. }
  345. }
  346. //Constrains anchor point, returns null if not overwritten by stencil
  347. var constrained = this.anchorConstrain(x, y);
  348. if(constrained != null){
  349. x=constrained.x;
  350. y=constrained.y;
  351. }
  352. this.shape.setTransform({
  353. dx:x,
  354. dy:y
  355. });
  356. if(this.linkedAnchor){
  357. // first and last points of a closed-curve-path
  358. this.linkedAnchor.shape.setTransform({
  359. dx:x,
  360. dy:y
  361. });
  362. }
  363. this.onTransformPoint(this);
  364. }
  365. },
  366. anchorConstrain: function(/* Number */x,/* Number */ y){
  367. // summary:
  368. // To be over written by tool!
  369. // Add an anchorConstrain method to the tool
  370. // and it will automatically overwrite this stub.
  371. // Should return a constrained x & y value.
  372. return null;
  373. },
  374. anchorPositionCheck: function(/* Number */x,/* Number */ y, /* Anchor */anchor){
  375. // summary:
  376. // To be over written by tool!
  377. // Add a anchorPositionCheck method to the tool
  378. // and it will automatically overwrite this stub.
  379. // Should return x and y coords. Success is both
  380. // being greater than zero, fail is if one or both
  381. // are less than zero.
  382. return {x:1, y:1};
  383. },
  384. setPoint: function(mx){
  385. // summary:
  386. // Internal. Sets the Stencil's point
  387. this.shape.applyTransform(mx);
  388. },
  389. connectMouse: function(){
  390. // summary:
  391. // Internal. Connects anchor to manager.mouse
  392. this._mouseHandle = this.mouse.register(this);
  393. },
  394. disconnectMouse: function(){
  395. // summary:
  396. // Internal. Disconnects anchor to manager.mouse
  397. this.mouse.unregister(this._mouseHandle);
  398. },
  399. reset: function(stencil){
  400. // summary:
  401. // Called (usually) from a Stencil when that Stencil
  402. // needed to make modifications to the position of the
  403. // point. Basically used when teh anchor causes a
  404. // less than zero condition.
  405. },
  406. destroy: function(){
  407. // summary:
  408. // Destroys anchor.
  409. dojo.disconnect(this._zCon);
  410. this.disconnectMouse();
  411. this.shape.removeShape();
  412. }
  413. }
  414. );
  415. }