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.edit;
19
20 import java.util.ArrayList;
21 import java.util.HashMap;
22 import java.util.HashSet;
23 import java.util.List;
24 import java.util.Map;
25 import java.util.Set;
26
27 import org.vectomatic.dom.svg.OMSVGElement;
28 import org.vectomatic.dom.svg.OMSVGGElement;
29 import org.vectomatic.dom.svg.OMSVGMatrix;
30 import org.vectomatic.dom.svg.OMSVGPathElement;
31 import org.vectomatic.dom.svg.OMSVGPathSeg;
32 import org.vectomatic.dom.svg.OMSVGPathSegArcAbs;
33 import org.vectomatic.dom.svg.OMSVGPathSegCurvetoCubicAbs;
34 import org.vectomatic.dom.svg.OMSVGPathSegCurvetoQuadraticAbs;
35 import org.vectomatic.dom.svg.OMSVGPathSegLinetoAbs;
36 import org.vectomatic.dom.svg.OMSVGPathSegList;
37 import org.vectomatic.dom.svg.OMSVGPathSegMovetoAbs;
38 import org.vectomatic.dom.svg.OMSVGPoint;
39 import org.vectomatic.dom.svg.OMSVGRectElement;
40 import org.vectomatic.dom.svg.OMSVGSVGElement;
41 import org.vectomatic.dom.svg.utils.SVGConstants;
42 import org.vectomatic.svg.edit.client.AppBundle;
43 import org.vectomatic.svg.edit.client.command.path.IPathRepOwner;
44 import org.vectomatic.svg.edit.client.command.path.SVGCubicSegRep;
45 import org.vectomatic.svg.edit.client.command.path.SVGLineSegRep;
46 import org.vectomatic.svg.edit.client.command.path.SVGMoveSegRep;
47 import org.vectomatic.svg.edit.client.command.path.SVGQuadraticSegRep;
48 import org.vectomatic.svg.edit.client.command.path.SVGSegRep;
49 import org.vectomatic.svg.edit.client.engine.SVGModel;
50 import org.vectomatic.svg.edit.client.event.ScalingEvent;
51 import org.vectomatic.svg.edit.client.event.ScalingHandler;
52 import org.vectomatic.svg.edit.client.event.SelectionChangedProcessor;
53 import org.vectomatic.svg.edit.client.event.SelectionChangedProxy;
54 import org.vectomatic.svg.edit.client.model.svg.SVGElementModel;
55 import org.vectomatic.svg.edit.client.model.svg.SVGPathElementModel;
56 import org.vectomatic.svg.edit.client.model.svg.path.SVGSegModel;
57 import org.vectomatic.svg.edit.client.model.svg.path.SVGSegStore;
58
59 import com.extjs.gxt.ui.client.data.ChangeEvent;
60 import com.extjs.gxt.ui.client.event.SelectionChangedEvent;
61 import com.extjs.gxt.ui.client.store.Record;
62 import com.extjs.gxt.ui.client.widget.grid.GridSelectionModel;
63 import com.google.gwt.core.client.GWT;
64 import com.google.gwt.dom.client.Element;
65 import com.google.gwt.event.dom.client.MouseDownEvent;
66 import com.google.gwt.event.dom.client.MouseMoveEvent;
67 import com.google.gwt.event.dom.client.MouseUpEvent;
68 import com.google.gwt.event.shared.HandlerRegistration;
69
70
71
72
73 public class EditPathGeometryManipulator extends EditManipulatorBase implements SelectionChangedProcessor<SVGSegModel>, IPathRepOwner, ScalingHandler {
74
75
76
77
78
79
80 protected OMSVGPathElement path;
81
82
83
84 protected List<SVGSegRep> segments;
85
86
87
88 protected Map<SVGSegRep, SVGSegModel> repToModel;
89
90
91
92 protected Map<SVGSegModel, SVGSegRep> modelToRep;
93
94
95
96 protected OMSVGGElement tangentGroup;
97
98
99
100 protected OMSVGGElement vertexGroup;
101
102
103
104
105 private Map<Element, SVGSegRep> vertexToSeg;
106
107
108
109
110 private Map<Element, SVGSegRep> tangentToSeg;
111
112
113
114 protected Element target;
115
116
117
118 protected OMSVGPoint p0;
119
120
121
122
123 protected OMSVGMatrix m;
124
125
126
127 protected SelectionChangedProxy<SVGSegModel> selChangeProxy = new SelectionChangedProxy<SVGSegModel>(this);
128
129
130
131 protected HandlerRegistration scalingHandlerReg;
132
133 public EditPathGeometryManipulator() {
134 segments = new ArrayList<SVGSegRep>();
135 repToModel = new HashMap<SVGSegRep, SVGSegModel>();
136 modelToRep = new HashMap<SVGSegModel, SVGSegRep>();
137 vertexToSeg = new HashMap<Element, SVGSegRep>();
138 tangentToSeg = new HashMap<Element, SVGSegRep>();
139 }
140
141 @Override
142 public OMSVGElement bind(Record record) {
143 this.record = record;
144 SVGPathElementModel model = (SVGPathElementModel) record.getModel();
145 scalingHandlerReg = model.getOwner().addScalingHandler(this);
146 svg = model.getOwner().getSvgElement();
147
148 path = new OMSVGPathElement();
149 g = new OMSVGGElement();
150 Mode.VERTEX.write(g);
151 g.setClassNameBaseVal(AppBundle.INSTANCE.css().pathGeometryManipulator());
152 tangentGroup = new OMSVGGElement();
153 vertexGroup = new OMSVGGElement();
154 g.appendChild(path);
155 g.appendChild(tangentGroup);
156 g.appendChild(vertexGroup);
157
158 monitorModel = true;
159 model.addChangeListener(this);
160 getSelectionModel().addSelectionChangedListener(selChangeProxy);
161 scheduleInit();
162 return g;
163 }
164
165
166 @Override
167 public void unbind() {
168 if (g != null) {
169 Element parent = g.getElement().getParentElement();
170 if (parent != null) {
171 parent.removeChild(g.getElement());
172 }
173 g = null;
174 tangentGroup = null;
175 vertexGroup = null;
176 target = null;
177 p0 = null;
178 m = null;
179
180 SVGPathElementModel model = (SVGPathElementModel) record.getModel();
181 model.removeChangeListener(this);
182 getSelectionModel().removeSelectionListener(selChangeProxy);
183 record = null;
184 repToModel.clear();
185 modelToRep.clear();
186 vertexToSeg.clear();
187 tangentToSeg.clear();
188 scalingHandlerReg.removeHandler();
189 }
190 }
191
192 @Override
193 public void modelChanged(ChangeEvent event) {
194 GWT.log("SVGPathManipulator.modelChanged(" + monitorModel + "," + toString() + ")");
195 if (monitorModel && record != null) {
196 SVGPathElementModel model = (SVGPathElementModel) record.getModel();
197 super.modelChanged(event);
198
199
200 path.setAttribute(SVGConstants.SVG_D_ATTRIBUTE, model.getElement().getAttribute(SVGConstants.SVG_D_ATTRIBUTE));
201 segments.clear();
202
203
204 repToModel.clear();
205 modelToRep.clear();
206 vertexToSeg.clear();
207 tangentToSeg.clear();
208
209
210 while (vertexGroup.hasChildNodes()) {
211 vertexGroup.removeChild(vertexGroup.getLastChild());
212 };
213 while (tangentGroup.hasChildNodes()) {
214 tangentGroup.removeChild(tangentGroup.getLastChild());
215 };
216
217
218 SVGSegStore store = model.getSegStore();
219 List<SVGSegModel> segModels = store.getModels();
220 OMSVGPathSegList segs = path.getPathSegList();
221 for (int i = 0, size = segModels.size(); i < size; i++) {
222 SVGSegModel segModel = segModels.get(i);
223 OMSVGPathSeg seg = segs.getItem(i);
224 switch(seg.getPathSegType()) {
225 case OMSVGPathSeg.PATHSEG_CLOSEPATH:
226 {
227 }
228 break;
229 case OMSVGPathSeg.PATHSEG_MOVETO_ABS:
230 {
231 appendSegment(segModel, new SVGMoveSegRep(this, (OMSVGPathSegMovetoAbs)seg));
232 }
233 break;
234 case OMSVGPathSeg.PATHSEG_LINETO_ABS:
235 {
236 appendSegment(segModel, new SVGLineSegRep(this, (OMSVGPathSegLinetoAbs)seg));
237 }
238 break;
239 case OMSVGPathSeg.PATHSEG_CURVETO_CUBIC_ABS:
240 {
241 appendSegment(segModel, new SVGCubicSegRep(this, (OMSVGPathSegCurvetoCubicAbs)seg));
242 }
243 break;
244 case OMSVGPathSeg.PATHSEG_CURVETO_QUADRATIC_ABS:
245 {
246 appendSegment(segModel, new SVGQuadraticSegRep(this, (OMSVGPathSegCurvetoQuadraticAbs)seg));
247 }
248 break;
249 case OMSVGPathSeg.PATHSEG_ARC_ABS:
250 {
251 OMSVGPathSegArcAbs arcAbs = (OMSVGPathSegArcAbs)seg;
252 }
253 break;
254 case OMSVGPathSeg.PATHSEG_UNKNOWN:
255 case OMSVGPathSeg.PATHSEG_MOVETO_REL:
256 case OMSVGPathSeg.PATHSEG_LINETO_REL:
257 case OMSVGPathSeg.PATHSEG_CURVETO_CUBIC_REL:
258 case OMSVGPathSeg.PATHSEG_CURVETO_QUADRATIC_REL:
259 case OMSVGPathSeg.PATHSEG_ARC_REL:
260 case OMSVGPathSeg.PATHSEG_LINETO_HORIZONTAL_ABS:
261 case OMSVGPathSeg.PATHSEG_LINETO_HORIZONTAL_REL:
262 case OMSVGPathSeg.PATHSEG_LINETO_VERTICAL_ABS:
263 case OMSVGPathSeg.PATHSEG_LINETO_VERTICAL_REL:
264 case OMSVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_ABS:
265 case OMSVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_REL:
266 case OMSVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS:
267 case OMSVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL:
268 GWT.log("Invalid segment type:" + seg.getPathSegTypeAsLetter());
269 assert(false);
270 break;
271 }
272 }
273
274
275 for (int i = 0, size = segments.size(); i < size - 1; i++) {
276 SVGSegRep seg = segments.get(i);
277 SVGSegRep nextSeg = segments.get(i + 1);
278 seg.setNext(nextSeg);
279 nextSeg.setPrevious(seg);
280 }
281
282 update(SVGModel.getVertexSize((SVGElementModel) record.getModel()));
283 processSelectionChanged(new SelectionChangedEvent<SVGSegModel>(getSelectionModel(), getSelectionModel().getSelectedItems()));
284 }
285
286 }
287
288 public SVGSegModel getModel(SVGSegRep rep) {
289 SVGSegModel model = repToModel.get(rep);
290 assert model != null;
291 return model;
292 }
293 public SVGSegRep getRepresentation(SVGSegModel model) {
294 SVGSegRep rep = modelToRep.get(model);
295 assert rep != null;
296 return rep;
297 }
298
299 @Override
300 public OMSVGSVGElement getSvg() {
301 return svg;
302 }
303
304 public OMSVGGElement getRootGroup() {
305 return g;
306 }
307
308 public void appendSegment(SVGSegModel modelSeg, SVGSegRep repSeg) {
309 segments.add(repSeg);
310
311 OMSVGRectElement vertex = repSeg.getVertex();
312 vertexGroup.appendChild(vertex);
313 vertexToSeg.put(vertex.getElement(), repSeg);
314
315 OMSVGGElement tangents = repSeg.getTangents();
316 tangentGroup.appendChild(tangents);
317 for (OMSVGElement element : tangents.<OMSVGElement>getChildNodes()) {
318 tangentToSeg.put(element.getElement(), repSeg);
319 }
320
321 modelToRep.put(modelSeg, repSeg);
322 repToModel.put(repSeg, modelSeg);
323 }
324
325 public SVGSegStore getSegStore() {
326 return ((SVGPathElementModel) record.getModel()).getSegStore();
327 }
328
329 public GridSelectionModel<SVGSegModel> getSelectionModel() {
330 return getSegStore().getSelectionModel();
331 }
332
333 public void selectSegment(SVGSegRep segment, boolean shiftDown, boolean ctrlDown) {
334 GridSelectionModel<SVGSegModel> selectionModel = getSelectionModel();
335 boolean selected = segment.getState() == VertexState.SELECTED;
336 if (shiftDown) {
337 List<SVGSegModel> selectedSegs = selectionModel.getSelectedItems();
338 if (selectedSegs.size() > 0) {
339
340
341 int ix1 = segments.indexOf(getRepresentation(selectedSegs.get(selectedSegs.size() - 1)));
342 int ix2 = segments.indexOf(segment);
343 for (int i = Math.min(ix1, ix2); i <= Math.max(ix1, ix2); i++) {
344 SVGSegRep s = segments.get(i);
345 if (s.getState() == VertexState.NONE) {
346 selectionModel.select(i, true);
347 }
348 }
349 } else {
350
351 selectionModel.select(getModel(segment), true);
352 }
353 } else if (ctrlDown) {
354
355 if (selected) {
356 selectionModel.deselect(getModel(segment));
357 } else {
358 selectionModel.select(getModel(segment), true);
359 }
360 } else {
361
362 selectionModel.deselectAll();
363
364
365 selectionModel.select(getModel(segment), false);
366 }
367 }
368
369 @Override
370 public boolean processSelectionChanged(SelectionChangedEvent<SVGSegModel> se) {
371 List<SVGSegModel> selection = (se == null) ? null : se.getSelection();
372 Set<SVGSegModel> selected = (selection != null) ? new HashSet<SVGSegModel>(selection) : new HashSet<SVGSegModel>();
373
374 for (SVGSegRep rep : segments) {
375 rep.setState(selected.contains(getModel(rep)) ? VertexState.SELECTED : VertexState.NONE);
376 }
377 return true;
378 }
379
380 @Override
381 public boolean processMouseDown(MouseDownEvent event) {
382 GWT.log("SVGPathManipulator.processMouseDown");
383 Element eventTarget = event.getNativeEvent().getEventTarget().cast();
384 if (SVGConstants.SVG_SVG_TAG.equals(eventTarget.getTagName())) {
385
386 getSelectionModel().deselectAll();
387 } else if (path.getElement() == eventTarget) {
388 Mode state = getMode();
389 if (state == Mode.VERTEX) {
390 Mode.TANGENT.write(g);
391 update(SVGModel.getVertexSize((SVGElementModel) record.getModel()));
392 } else if (state == Mode.TANGENT) {
393 Mode.VERTEX.write(g);
394 update(SVGModel.getVertexSize((SVGElementModel) record.getModel()));
395 } else {
396 assert false;
397 }
398 } else {
399 m = path.getScreenCTM().inverse();
400 p0 = getCoordinates(event, m);
401 SVGSegRep segment = vertexToSeg.get(eventTarget);
402 if (segment != null) {
403 if (event.isControlKeyDown() || segment.getState() == VertexState.NONE) {
404 selectSegment(segment, event.isShiftKeyDown(), event.isControlKeyDown());
405 }
406 if (segment.getState() == VertexState.SELECTED) {
407 target = eventTarget;
408 }
409 } else if (tangentToSeg.containsKey(eventTarget)) {
410 target = eventTarget;
411 }
412 }
413 event.preventDefault();
414 event.stopPropagation();
415 return true;
416 }
417
418 @Override
419 public boolean processMouseMove(MouseMoveEvent event) {
420 if (target != null) {
421
422 OMSVGPoint p1 = getCoordinates(event, m);
423 OMSVGPoint delta = p1.substract(p0, svg.createSVGPoint());
424 p1.assignTo(p0);
425
426 SVGSegRep segment = tangentToSeg.get(target);
427 float hs = SVGModel.getVertexSize((SVGElementModel) record.getModel());
428 if (segment != null) {
429 segment.processMouseMove(delta, target, hs, event.isControlKeyDown());
430 } else {
431 GridSelectionModel<SVGSegModel> selectionModel = getSelectionModel();
432 for (SVGSegModel model : selectionModel.getSelectedItems()) {
433 getRepresentation(model).processMouseMove(delta, null, hs, false);
434 }
435 }
436 event.preventDefault();
437 event.stopPropagation();
438 }
439 return true;
440 }
441
442 @Override
443 public boolean processMouseUp(MouseUpEvent event) {
444 if (target != null) {
445 GWT.log("SVGPathManipulator.processMouseUp");
446
447 record.beginEdit();
448 record.set(SVGConstants.SVG_D_ATTRIBUTE, path.getAttribute(SVGConstants.SVG_D_ATTRIBUTE));
449 record.endEdit();
450 record.commit(false);
451
452 target = null;
453 }
454 event.preventDefault();
455 event.stopPropagation();
456 return true;
457 }
458
459 @Override
460 public Mode getMode() {
461 return Mode.read(g);
462 }
463
464 public void update(float hs) {
465 for (int i = 0; i < segments.size(); i++) {
466 segments.get(i).update(hs);
467 }
468 }
469
470 @Override
471 public List<SVGSegRep> getSegments() {
472 return segments;
473 }
474
475 @Override
476 public void onScale(ScalingEvent event) {
477 update(SVGModel.getVertexSize((SVGElementModel) record.getModel()));
478 }
479 }