1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.vectomatic.svg.edit.client.command.add;
19
20 import java.util.ArrayList;
21 import java.util.List;
22
23 import org.vectomatic.dom.svg.OMSVGElement;
24 import org.vectomatic.dom.svg.OMSVGGElement;
25 import org.vectomatic.dom.svg.OMSVGPathElement;
26 import org.vectomatic.dom.svg.OMSVGPathSeg;
27 import org.vectomatic.dom.svg.OMSVGPathSegList;
28 import org.vectomatic.dom.svg.OMSVGPoint;
29 import org.vectomatic.dom.svg.OMSVGSVGElement;
30 import org.vectomatic.dom.svg.impl.SVGElement;
31 import org.vectomatic.dom.svg.utils.SVGConstants;
32 import org.vectomatic.svg.edit.client.AppBundle;
33 import org.vectomatic.svg.edit.client.AppConstants;
34 import org.vectomatic.svg.edit.client.SvgrealApp;
35 import org.vectomatic.svg.edit.client.command.FactoryInstantiatorBase;
36 import org.vectomatic.svg.edit.client.command.IFactoryInstantiator;
37 import org.vectomatic.svg.edit.client.command.path.IPathRepOwner;
38 import org.vectomatic.svg.edit.client.command.path.SVGCloseSegRep;
39 import org.vectomatic.svg.edit.client.command.path.SVGCubicSegRep;
40 import org.vectomatic.svg.edit.client.command.path.SVGLineSegRep;
41 import org.vectomatic.svg.edit.client.command.path.SVGMoveSegRep;
42 import org.vectomatic.svg.edit.client.command.path.SVGQuadraticSegRep;
43 import org.vectomatic.svg.edit.client.command.path.SVGSegRep;
44 import org.vectomatic.svg.edit.client.engine.SVGModel;
45 import org.vectomatic.svg.edit.client.event.MouseDownProcessor;
46 import org.vectomatic.svg.edit.client.event.MouseMoveProcessor;
47 import org.vectomatic.svg.edit.client.event.MouseUpProcessor;
48 import org.vectomatic.svg.edit.client.event.ScalingEvent;
49 import org.vectomatic.svg.edit.client.event.ScalingHandler;
50 import org.vectomatic.svg.edit.client.model.ModelConstants;
51 import org.vectomatic.svg.edit.client.model.svg.SVGPathSegType;
52
53 import com.extjs.gxt.ui.client.Style.IconAlign;
54 import com.extjs.gxt.ui.client.event.ButtonEvent;
55 import com.extjs.gxt.ui.client.event.SelectionListener;
56 import com.extjs.gxt.ui.client.util.Margins;
57 import com.extjs.gxt.ui.client.util.Padding;
58 import com.extjs.gxt.ui.client.util.Rectangle;
59 import com.extjs.gxt.ui.client.widget.LayoutContainer;
60 import com.extjs.gxt.ui.client.widget.Window;
61 import com.extjs.gxt.ui.client.widget.button.Button;
62 import com.extjs.gxt.ui.client.widget.button.ToggleButton;
63 import com.extjs.gxt.ui.client.widget.layout.FitLayout;
64 import com.extjs.gxt.ui.client.widget.layout.HBoxLayout;
65 import com.extjs.gxt.ui.client.widget.layout.HBoxLayout.HBoxLayoutAlign;
66 import com.extjs.gxt.ui.client.widget.layout.HBoxLayoutData;
67 import com.extjs.gxt.ui.client.widget.layout.VBoxLayout;
68 import com.extjs.gxt.ui.client.widget.layout.VBoxLayout.VBoxLayoutAlign;
69 import com.extjs.gxt.ui.client.widget.layout.VBoxLayoutData;
70 import com.google.gwt.core.client.GWT;
71 import com.google.gwt.event.dom.client.MouseDownEvent;
72 import com.google.gwt.event.dom.client.MouseMoveEvent;
73 import com.google.gwt.event.dom.client.MouseUpEvent;
74 import com.google.gwt.event.shared.HandlerRegistration;
75 import com.google.gwt.user.client.ui.AbstractImagePrototype;
76
77
78
79
80
81 public class AddPathCommandFactory extends AddCommandFactoryBase implements IPathRepOwner, MouseDownProcessor, MouseMoveProcessor, MouseUpProcessor, ScalingHandler {
82 @SuppressWarnings("serial")
83 public static final IFactoryInstantiator<AddPathCommandFactory> INSTANTIATOR = new FactoryInstantiatorBase<AddPathCommandFactory>(ModelConstants.INSTANCE.addPathCmdFactory(), ModelConstants.INSTANCE.addPathCmdFactoryDesc()) {
84 @Override
85 public AddPathCommandFactory create() {
86 return new AddPathCommandFactory();
87 }
88 };
89
90 @Override
91 public IFactoryInstantiator<?> getInstantiator() {
92 return INSTANTIATOR;
93 }
94
95 class AddPathToolBar extends Window {
96 private ToggleButton[] pathButtons;
97 private Button undoButton, redoButton, commitButton;
98 private SelectionListener<ButtonEvent> segTypeListener = new SelectionListener<ButtonEvent>() {
99 @Override
100 public void componentSelected(ButtonEvent ce) {
101 ToggleButton button = (ToggleButton)ce.getButton();
102 if (button.isPressed()) {
103 setSegmentType(button.getToolTip().getToolTipConfig().getText());
104 }
105 }
106 };
107 public AddPathToolBar() {
108 ModelConstants constants = ModelConstants.INSTANCE;
109 AbstractImagePrototype icons[] = {
110 AbstractImagePrototype.create(AppBundle.INSTANCE.pathMove()),
111 AbstractImagePrototype.create(AppBundle.INSTANCE.pathLine()),
112 AbstractImagePrototype.create(AppBundle.INSTANCE.pathQuadratic()),
113 AbstractImagePrototype.create(AppBundle.INSTANCE.pathCubic())
114 };
115 String[] toolTips = {
116 constants.segMoveTo(),
117 constants.segLineTo(),
118 constants.segQuadraticTo(),
119 constants.segCubicTo()
120 };
121 LayoutContainer pathContainer = new LayoutContainer();
122 HBoxLayout hboxLayout = new HBoxLayout();
123 hboxLayout.setHBoxLayoutAlign(HBoxLayoutAlign.MIDDLE);
124 pathContainer.setLayout(hboxLayout);
125 HBoxLayoutData flex = new HBoxLayoutData(new Margins(0, 5, 0, 0));
126 flex.setFlex(1);
127 pathButtons = new ToggleButton[icons.length];
128 for (int i = 0; i < icons.length; i++) {
129 pathButtons[i] = new ToggleButton();
130 pathButtons[i].setIcon(icons[i]);
131 pathButtons[i].setIconAlign(IconAlign.TOP);
132 pathButtons[i].setToolTip(toolTips[i]);
133 pathButtons[i].addSelectionListener(segTypeListener);
134 pathButtons[i].setToggleGroup("path");
135 pathButtons[i].setSize(20, 24);
136 pathContainer.add(pathButtons[i], flex);
137 }
138 Button closePathButton = new Button();
139 closePathButton.setIcon(AbstractImagePrototype.create(AppBundle.INSTANCE.pathClose()));
140 closePathButton.setIconAlign(IconAlign.TOP);
141 closePathButton.setToolTip(constants.segClose());
142 closePathButton.addSelectionListener(new SelectionListener<ButtonEvent>() {
143 @Override
144 public void componentSelected(ButtonEvent ce) {
145 removeDanglingSegment();
146 appendSegment(new SVGCloseSegRep(AddPathCommandFactory.this, path.createSVGPathSegClosePath()));
147 validateSegment(SVGModel.getVertexSize(path));
148 setSegmentType(segType);
149 }
150 });
151 closePathButton.setSize(20, 24);
152 HBoxLayoutData flex2 = new HBoxLayoutData(new Margins(0));
153 flex2.setFlex(1);
154 pathContainer.add(closePathButton, flex2);
155
156
157 undoButton = new Button();
158 undoButton.setIcon(AbstractImagePrototype.create(AppBundle.INSTANCE.undo()));
159 undoButton.setIconAlign(IconAlign.TOP);
160 undoButton.addSelectionListener(new SelectionListener<ButtonEvent>() {
161 @Override
162 public void componentSelected(ButtonEvent ce) {
163 undoSegment();
164 }
165 });
166 redoButton = new Button();
167 redoButton.setIcon(AbstractImagePrototype.create(AppBundle.INSTANCE.redo()));
168 redoButton.setIconAlign(IconAlign.TOP);
169 redoButton.addSelectionListener(new SelectionListener<ButtonEvent>() {
170 @Override
171 public void componentSelected(ButtonEvent ce) {
172 redoSegment();
173 }
174 });
175 LayoutContainer urContainer = new LayoutContainer();
176 HBoxLayout urHboxLayout = new HBoxLayout();
177 urHboxLayout.setHBoxLayoutAlign(HBoxLayoutAlign.MIDDLE);
178 urContainer.setLayout(urHboxLayout);
179 urContainer.add(undoButton, flex);
180 urContainer.add(redoButton, flex2);
181
182 commitButton = new Button();
183 commitButton.setText(AppConstants.INSTANCE.commitButton());
184 commitButton.addSelectionListener(new SelectionListener<ButtonEvent>() {
185 @Override
186 public void componentSelected(ButtonEvent ce) {
187 commit();
188 }
189 });
190
191 LayoutContainer container = new LayoutContainer();
192 VBoxLayout layout = new VBoxLayout();
193 layout.setPadding(new Padding(5));
194 layout.setVBoxLayoutAlign(VBoxLayoutAlign.STRETCH);
195 VBoxLayoutData vbl1 = new VBoxLayoutData(new Margins(0, 0, 5, 0));
196 VBoxLayoutData vbl2 = new VBoxLayoutData(new Margins(0));
197 container.setLayout(layout);
198 container.add(pathContainer, vbl1);
199 container.add(urContainer, vbl1);
200 container.add(commitButton, vbl2);
201
202 int w = 175, h = 120;
203 setMinWidth(w);
204 setMinHeight(h);
205 setWidth(w);
206 setHeight(h);
207 setResizable(false);
208 setLayout(new FitLayout());
209 add(container);
210 setClosable(false);
211 setBorders(true);
212
213 Rectangle rect = SvgrealApp.getApp().getRectangle();
214 setPagePosition(rect.x + rect.width - w -5, rect.y + rect.height - h - 5);
215 }
216 public ToggleButton getPathButton(String segType) {
217 for (ToggleButton button : pathButtons) {
218 if (segType.equals(button.getToolTip().getToolTipConfig().getText())) {
219 return button;
220 }
221 }
222 return null;
223 }
224 }
225
226 enum State {
227 MOVE {
228 boolean processMouseDown(AddPathCommandFactory factory, OMSVGPoint p, float hs) {
229 factory.appendSegment(new SVGMoveSegRep(factory, factory.path.createSVGPathSegMovetoAbs(p.getX(), p.getY())));
230 factory.validateSegment(hs);
231 return true;
232 }
233 },
234 LINE {
235 boolean processMouseDown(AddPathCommandFactory factory, OMSVGPoint p, float hs) {
236 factory.updateStatus(ModelConstants.INSTANCE.addPathCmdFactoryLineP2());
237 if (factory.getSegCount() == 0) {
238 factory.appendSegment(new SVGMoveSegRep(factory, factory.path.createSVGPathSegMovetoAbs(p.getX(), p.getY())));
239 factory.validateSegment(hs);
240 }
241 if (factory.danglingSegment() != null) {
242 factory.validateSegment(hs);
243 }
244 factory.appendSegment(new SVGLineSegRep(factory, factory.path.createSVGPathSegLinetoAbs(p.getX(), p.getY())));
245 return true;
246 }
247 boolean processMouseMove(AddPathCommandFactory factory, OMSVGPoint delta, float hs) {
248 SVGSegRep segRep = factory.danglingSegment();
249 if (segRep != null) {
250
251 segRep.updateEnd(delta, hs);
252 }
253 return true;
254 }
255 },
256 QUADRATIC {
257 @Override
258 boolean processMouseDown(AddPathCommandFactory factory, OMSVGPoint p, float hs) {
259
260 if (factory.getSegCount() == 0) {
261 factory.appendSegment(new SVGMoveSegRep(factory, factory.path.createSVGPathSegMovetoAbs(p.getX(), p.getY())));
262 factory.validateSegment(hs);
263 }
264 if (factory.danglingSegment() != null) {
265 factory.validateSegment(hs);
266 }
267 factory.updateStatus(ModelConstants.INSTANCE.addPathCmdFactoryQuadraticCp1b());
268 SVGSegRep lastSegRep = factory.lastSegment();
269 SVGSegRep segRep = factory.appendSegment(new SVGQuadraticSegRep(factory, factory.path.createSVGPathSegCurvetoQuadraticAbs(lastSegRep.getX(), lastSegRep.getY(), p.getX(), p.getY())));
270 segRep.setCp1(p, hs);
271 if (factory.ctrlPressed) {
272 SVGSegRep prevSegRep = factory.previousCurve();
273 if (prevSegRep != null) {
274 prevSegRep.setCp2(factory.owner.getSvgElement().createSVGPoint(2 * segRep.getX() - p.getX(), 2 * segRep.getY() -p.getY()), hs);
275 }
276 }
277 return true;
278 }
279
280 @Override
281 boolean processMouseMove(AddPathCommandFactory factory, OMSVGPoint delta, float hs) {
282 SVGSegRep segRep = factory.danglingSegment();
283 if (segRep != null) {
284 if (factory.mousePressed) {
285
286 segRep.processMouseMove(delta, segRep.getCp1(), hs, false);
287 if (factory.ctrlPressed) {
288 SVGSegRep prevSegRep = factory.previousCurve();
289 if (prevSegRep != null) {
290 prevSegRep.processMouseMove(factory.owner.getSvgElement().createSVGPoint(-delta.getX(), -delta.getY()), prevSegRep.getCp2(), hs, false);
291 }
292 }
293 } else {
294
295 segRep.setX(segRep.getX() + delta.getX());
296 segRep.setY(segRep.getY() + delta.getY());
297 segRep.update(hs);
298 }
299 }
300 return true;
301 }
302
303 @Override
304 boolean processMouseUp(AddPathCommandFactory factory, OMSVGPoint p, float hs) {
305 factory.updateStatus(ModelConstants.INSTANCE.addPathCmdFactoryQuadraticP2());
306 SVGSegRep segRep = factory.danglingSegment();
307 factory.p0.setX(segRep.getX());
308 factory.p0.setY(segRep.getY());
309 return true;
310 }
311 },
312 CUBIC1 {
313 @Override
314 boolean processMouseDown(AddPathCommandFactory factory, OMSVGPoint p, float hs) {
315 factory.updateStatus(ModelConstants.INSTANCE.addPathCmdFactoryCubicCp1b());
316
317 if (factory.getSegCount() == 0) {
318 factory.appendSegment(new SVGMoveSegRep(factory, factory.path.createSVGPathSegMovetoAbs(p.getX(), p.getY())));
319 factory.validateSegment(hs);
320 }
321 SVGSegRep segRep = factory.danglingSegment();
322 if (segRep == null) {
323 SVGSegRep lastSegRep = factory.lastSegment();
324 segRep = factory.appendSegment(new SVGCubicSegRep(factory, factory.path.createSVGPathSegCurvetoCubicAbs(lastSegRep.getX(), lastSegRep.getY(), lastSegRep.getX(), lastSegRep.getY(), lastSegRep.getX(), lastSegRep.getY())));
325 }
326 segRep.setCp1(p, hs);
327 if (factory.ctrlPressed) {
328 SVGSegRep prevSegRep = factory.previousCurve();
329 if (prevSegRep != null) {
330 prevSegRep.setCp2(factory.owner.getSvgElement().createSVGPoint(2 * segRep.getX() - p.getX(), 2 * segRep.getY() - p.getY()), hs);
331 }
332 }
333 return true;
334 }
335
336 @Override
337 boolean processMouseMove(AddPathCommandFactory factory, OMSVGPoint delta, float hs) {
338 if (factory.mousePressed) {
339
340 SVGSegRep segRep = factory.danglingSegment();
341 segRep.processMouseMove(delta, segRep.getCp1(), hs, false);
342 if (factory.ctrlPressed) {
343 SVGSegRep prevSegRep = factory.previousCurve();
344 if (prevSegRep != null) {
345 prevSegRep.processMouseMove(factory.owner.getSvgElement().createSVGPoint(-delta.getX(), -delta.getY()), prevSegRep.getCp2(), hs, false);
346 }
347 }
348 }
349 return true;
350 }
351
352 @Override
353 boolean processMouseUp(AddPathCommandFactory factory, OMSVGPoint p, float hs) {
354 factory.updateStatus(ModelConstants.INSTANCE.addPathCmdFactoryCubicCp2a());
355 factory.setState(State.CUBIC2);
356 SVGSegRep segRep = factory.danglingSegment();
357 factory.p0.setX(segRep.getX());
358 factory.p0.setY(segRep.getY());
359 return true;
360 }
361 },
362 CUBIC2 {
363 @Override
364 boolean processMouseDown(AddPathCommandFactory factory, OMSVGPoint p, float hs) {
365 factory.updateStatus(ModelConstants.INSTANCE.addPathCmdFactoryCubicCp2b());
366 if (factory.ctrlPressed) {
367
368 SVGSegRep segRep = factory.danglingSegment();
369 factory.validateSegment(hs);
370 factory.appendSegment(new SVGCubicSegRep(factory, factory.path.createSVGPathSegCurvetoCubicAbs(segRep.getX(), segRep.getY(), segRep.getX(), segRep.getY(), segRep.getX(), segRep.getY())));
371 }
372 return true;
373 }
374
375 @Override
376 boolean processMouseMove(AddPathCommandFactory factory, OMSVGPoint delta, float hs) {
377 SVGSegRep segRep = factory.danglingSegment();
378 if (factory.ctrlPressed) {
379
380 segRep.processMouseMove(delta, segRep.getCp1(), hs, false);
381 SVGSegRep prevSegRep = factory.previousCurve();
382 if (prevSegRep != null) {
383 prevSegRep.processMouseMove(factory.owner.getSvgElement().createSVGPoint(-delta.getX(), -delta.getY()), prevSegRep.getCp2(), hs, false);
384 }
385 } else if (factory.mousePressed) {
386
387 segRep.processMouseMove(delta, segRep.getCp2(), hs, false);
388 } else {
389
390 segRep.updateEnd(delta, hs);
391 }
392 return true;
393 }
394
395 @Override
396 boolean processMouseUp(AddPathCommandFactory factory, OMSVGPoint p, float hs) {
397 SVGSegRep segRep = factory.danglingSegment();
398 if (factory.ctrlPressed) {
399 factory.updateStatus(ModelConstants.INSTANCE.addPathCmdFactoryCubicCp2a());
400 factory.p0.setX(segRep.getX());
401 factory.p0.setY(segRep.getY());
402 } else {
403 factory.updateStatus(ModelConstants.INSTANCE.addPathCmdFactoryCubicCp1a());
404
405 factory.validateSegment(hs);
406 factory.appendSegment(new SVGCubicSegRep(factory, factory.path.createSVGPathSegCurvetoCubicAbs(segRep.getX(), segRep.getY(), segRep.getX(), segRep.getY(), segRep.getX(), segRep.getY())));
407 factory.setState(CUBIC1);
408 }
409 return true;
410 }
411 };
412 boolean processMouseDown(AddPathCommandFactory factory, OMSVGPoint p, float hs) {
413 return false;
414 }
415 boolean processMouseMove(AddPathCommandFactory factory, OMSVGPoint delta, float hs) {
416 return false;
417 }
418 boolean processMouseUp(AddPathCommandFactory factory, OMSVGPoint p, float hs) {
419 return false;
420 }
421 }
422
423
424
425
426 private AddPathToolBar toolBar;
427
428
429
430
431 protected OMSVGGElement g;
432
433
434
435 protected OMSVGGElement tangentGroup;
436
437
438
439 protected OMSVGGElement vertexGroup;
440
441
442
443 protected OMSVGPathElement path;
444
445
446
447 OMSVGPathSegList segList;
448
449
450
451 protected List<SVGSegRep> segments;
452
453
454
455 protected State state;
456
457
458
459 protected boolean mousePressed;
460
461
462
463 protected boolean ctrlPressed;
464
465
466
467 protected String segType;
468
469
470
471 protected OMSVGPoint p0;
472
473
474
475
476 protected SVGSegRep danglingSegment;
477
478
479
480 protected int last;
481
482
483
484 protected HandlerRegistration scalingHandlerReg;
485
486 public AddPathCommandFactory() {
487
488 segments = new ArrayList<SVGSegRep>();
489 toolBar = new AddPathToolBar();
490 }
491
492 @Override
493 public void start(Object requester) {
494 GWT.log("AddPathCommandFactory.start(" + requester + ")");
495 super.start(requester);
496 toolBar.getPathButton(ModelConstants.INSTANCE.segLineTo()).toggle(true);
497 setSegmentType(ModelConstants.INSTANCE.segLineTo());
498 toolBar.show();
499 }
500
501 @Override
502 public void stop() {
503 GWT.log("AddPathCommandFactory.stop()");
504 clear();
505 toolBar.hide();
506 super.stop();
507 }
508
509 public void setSegmentType(String segType) {
510 GWT.log("AddPathCommandFactory.setSegmentType(" + segType + ")");
511 this.segType = segType;
512 removeDanglingSegment();
513 ModelConstants constants = ModelConstants.INSTANCE;
514 switch(SVGPathSegType.INSTANCE.fromName(segType)) {
515 case OMSVGPathSeg.PATHSEG_MOVETO_ABS:
516 setState(State.MOVE);
517 updateStatus(constants.addPathCmdFactoryMove());
518 break;
519 case OMSVGPathSeg.PATHSEG_LINETO_ABS:
520 setState(State.LINE);
521 if (last == 0) {
522 updateStatus(constants.addPathCmdFactoryLineFirst());
523 } else {
524 updateStatus(constants.addPathCmdFactoryLineP2());
525 SVGSegRep segRep = lastSegment();
526 appendSegment(new SVGLineSegRep(this, path.createSVGPathSegLinetoAbs(segRep.getX(), segRep.getY())));
527 p0.setX(segRep.getX());
528 p0.setY(segRep.getY());
529 }
530 break;
531 case OMSVGPathSeg.PATHSEG_CURVETO_QUADRATIC_ABS:
532 setState(State.QUADRATIC);
533 updateStatus(last == 0 ? constants.addPathCmdFactoryQuadraticCp1First() : constants.addPathCmdFactoryQuadraticCp1a());
534 break;
535 case OMSVGPathSeg.PATHSEG_CURVETO_CUBIC_ABS:
536 setState(State.CUBIC1);
537 updateStatus(last == 0 ? constants.addPathCmdFactoryCubicCp1First() : constants.addPathCmdFactoryCubicCp1a());
538 break;
539 }
540 }
541
542 public void update() {
543 toolBar.undoButton.setEnabled(last > 0);
544 toolBar.redoButton.setEnabled(last < segments.size());
545 toolBar.commitButton.setEnabled(last > 0);
546 }
547
548 public void undoSegment() {
549 GWT.log("AddPathCommandFactory.undoSegment()");
550 removeDanglingSegment();
551 SVGSegRep segRep = lastSegment();
552 last--;
553 segList.removeItem(last);
554 vertexGroup.removeChild(segRep.getVertex());
555 tangentGroup.removeChild(segRep.getTangents());
556 update();
557 setSegmentType(segType);
558 }
559
560 public void redoSegment() {
561 GWT.log("AddPathCommandFactory.redoSegment()");
562 removeDanglingSegment();
563 last++;
564 SVGSegRep segRep = lastSegment();
565 segList.appendItem(segRep.getElement());
566 vertexGroup.appendChild(segRep.getVertex());
567 tangentGroup.appendChild(segRep.getTangents());
568 update();
569 setSegmentType(segType);
570 }
571
572 public void commit() {
573 GWT.log("AddPathCommandFactory.commit()");
574 if (owner != null) {
575 removeDanglingSegment();
576 g.removeChild(path);
577 owner.getTwinGroup().appendChild(path);
578 applyCssContextStyle((SVGElement) path.getElement().cast());
579
580
581 path.getStyle().setSVGProperty(SVGConstants.CSS_VISIBILITY_PROPERTY, SVGConstants.CSS_VISIBLE_VALUE);
582 createCommand(path);
583 clear();
584 setSegmentType(segType);
585 }
586 }
587
588 public void clear() {
589 if (owner != null) {
590 scalingHandlerReg.removeHandler();
591 OMSVGElement parent = owner.getTwinGroup();
592 parent.removeChild(g);
593 owner = null;
594 g = null;
595 path = null;
596 tangentGroup = null;
597 vertexGroup = null;
598 segList = null;
599 segments.clear();
600 danglingSegment = null;
601 last = 0;
602 update();
603 }
604 }
605
606 @Override
607 public boolean processMouseDown(MouseDownEvent event) {
608
609 if (owner == null) {
610 owner = SvgrealApp.getApp().getActiveModel();
611 scalingHandlerReg = owner.addScalingHandler(this);
612 OMSVGElement parent = owner.getTwinGroup();
613 path = new OMSVGPathElement();
614 segList = path.getPathSegList();
615 g = new OMSVGGElement();
616 Mode.TANGENT.write(g);
617 g.setClassNameBaseVal(AppBundle.INSTANCE.css().pathGeometryManipulator());
618 tangentGroup = new OMSVGGElement();
619 vertexGroup = new OMSVGGElement();
620 g.appendChild(path);
621 g.appendChild(tangentGroup);
622 g.appendChild(vertexGroup);
623 parent.appendChild(g);
624
625 }
626
627 mousePressed = true;
628 ctrlPressed = event.isControlKeyDown();
629 p0 = owner.getCoordinates(event, true);
630
631 boolean processed = state.processMouseDown(this, owner.getCoordinates(event, true), SVGModel.getVertexSize(path));
632 if (processed) {
633 event.stopPropagation();
634 event.preventDefault();
635 }
636 return processed;
637 }
638
639 @Override
640 public boolean processMouseMove(MouseMoveEvent event) {
641 if (p0 == null) {
642 return false;
643 }
644
645 OMSVGPoint p1 = owner.getCoordinates(event, true);
646 OMSVGPoint delta = p1.substract(p0, owner.getSvgElement().createSVGPoint());
647 p1.assignTo(p0);
648 float hs = SVGModel.getVertexSize(path);
649 boolean processed = state.processMouseMove(this, delta, hs);
650 if (processed) {
651 event.stopPropagation();
652 event.preventDefault();
653 }
654 if (danglingSegment != null) {
655 if (danglingAtOrigin(hs)) {
656 danglingSegment.setState(VertexState.CLOSING);
657 } else {
658 danglingSegment.setState(VertexState.NONE);
659 }
660 }
661 return processed;
662 }
663
664 private boolean danglingAtOrigin(float hs) {
665 float x = danglingSegment.getX() - segments.get(0).getX();
666 float y = danglingSegment.getY() - segments.get(0).getY();
667 return x*x + y*y < hs*hs;
668 }
669
670 @Override
671 public boolean processMouseUp(MouseUpEvent event) {
672
673 boolean processed = state.processMouseUp(this, owner.getCoordinates(event, true), SVGModel.getVertexSize(path));
674 if (processed) {
675 event.stopPropagation();
676 event.preventDefault();
677 }
678 mousePressed = false;
679 ctrlPressed = false;
680 return processed;
681 }
682
683 public void setState(State state) {
684 this.state = state;
685 }
686
687 @Override
688 public OMSVGSVGElement getSvg() {
689 return owner.getSvgElement();
690 }
691
692 @Override
693 public List<SVGSegRep> getSegments() {
694 return segments;
695 }
696
697 public int getSegCount() {
698 return last;
699 }
700
701 public SVGSegRep danglingSegment() {
702 return danglingSegment;
703 }
704
705 public SVGSegRep lastSegment() {
706 return last > 0 ? segments.get(last - 1) : null;
707 }
708
709 public SVGSegRep previousCurve() {
710 SVGSegRep segRep = lastSegment();
711 if (segRep instanceof SVGQuadraticSegRep || segRep instanceof SVGCubicSegRep) {
712 return segRep;
713 }
714 return null;
715 }
716
717 @Override
718 public Mode getMode() {
719 return Mode.TANGENT;
720 }
721
722
723
724
725
726
727
728
729
730 public SVGSegRep appendSegment(SVGSegRep segRep) {
731 SVGSegRep prevSeg = lastSegment();
732 segList.appendItem(segRep.getElement());
733 if (prevSeg != null) {
734 prevSeg.setNext(segRep);
735 segRep.setPrevious(prevSeg);
736 }
737 vertexGroup.appendChild(segRep.getVertex());
738 tangentGroup.appendChild(segRep.getTangents());
739 danglingSegment = segRep;
740 segRep.update(SVGModel.getVertexSize(path));
741 return segRep;
742 }
743
744
745
746
747
748 public void validateSegment(float hs) {
749
750 for (int i = segments.size() - 1; i >= last; i--) {
751 segments.remove(i);
752 }
753
754 if (last > 0 && danglingAtOrigin(hs) && path.getTotalLength() > hs) {
755
756
757
758
759 danglingSegment.setX(segments.get(0).getX());
760 danglingSegment.setY(segments.get(0).getY());
761 danglingSegment.update(hs);
762 }
763
764 segments.add(danglingSegment);
765 danglingSegment.setState(VertexState.SELECTED);
766
767 danglingSegment = null;
768 last++;
769 update();
770 }
771
772 public void removeDanglingSegment() {
773 if (danglingSegment != null) {
774 int index = segList.getNumberOfItems() - 1;
775 segList.removeItem(index);
776 vertexGroup.removeChild(danglingSegment.getVertex());
777 tangentGroup.removeChild(danglingSegment.getTangents());
778 danglingSegment = null;
779 }
780 }
781
782 @Override
783 public void onScale(ScalingEvent event) {
784 if (path != null) {
785 float size = SVGModel.getVertexSize(path);
786 for (SVGSegRep rep : segments) {
787 rep.update(size);
788 }
789 if (danglingSegment != null) {
790 danglingSegment.update(size);
791 }
792 }
793 }
794 }