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