1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.vectomatic.client.rep.controller;
19
20 import java.util.ArrayList;
21 import java.util.Iterator;
22 import java.util.List;
23
24 import org.vectomatic.client.rep.RepApplication;
25 import org.vectomatic.client.rep.command.ICommand;
26 import org.vectomatic.client.rep.command.TransformShapeCommand;
27 import org.vectomatic.client.rep.view.Cursor;
28 import org.vectomatic.client.rep.view.DrawingView;
29 import org.vectomatic.common.model.Attribute;
30 import org.vectomatic.common.model.FloatAttributeValue;
31 import org.vectomatic.common.model.Shape;
32 import org.vectomatic.common.model.geometry.BoundingBox;
33 import org.vectomatic.common.model.geometry.Point;
34 import org.vectomatic.common.model.geometry.Rect;
35 import org.vectomatic.common.model.geometry.ShapeGroup;
36 import org.vectomatic.common.model.geometry.TransformMatrix;
37 import org.vectomatic.common.model.style.Color;
38 import org.vectomatic.common.model.style.NoneStyle;
39
40 import com.google.gwt.user.client.DOM;
41 import com.google.gwt.user.client.Event;
42 import com.google.gwt.user.client.ui.KeyboardListener;
43 import com.google.gwt.user.client.ui.MenuBar;
44
45
46
47
48
49 public class SelectShapeController extends ControllerBase {
50 private static final float HANDLE_SIZE = 3f;
51 private static final int HANDLE_MOVE = 10;
52 private static final int HANDLE_NONE = 11;
53
54
55
56
57
58 private static final int MODE_HOVER = 0;
59
60
61
62 private static final int MODE_RECT = 1;
63
64
65
66 private static final int MODE_TRANSLATE = 2;
67
68
69
70 private static final int MODE_ROTATE = 3;
71
72
73
74 private static final int MODE_SCALE = 4;
75
76 private static final Cursor[] _cursors = {
77 Cursor.CURSOR_NW_RESIZE,
78 Cursor.CURSOR_SW_RESIZE,
79 Cursor.CURSOR_SE_RESIZE,
80 Cursor.CURSOR_NE_RESIZE,
81 Cursor.CURSOR_N_RESIZE,
82 Cursor.CURSOR_W_RESIZE,
83 Cursor.CURSOR_S_RESIZE,
84 Cursor.CURSOR_E_RESIZE,
85 Cursor.CURSOR_MOVE,
86 Cursor.CURSOR_ROTATE,
87 Cursor.CURSOR_POINTER,
88 Cursor.CURSOR_POINTER
89 };
90
91 private TransformMatrix _mTmp, _mOld, _mInv;
92 private Rect _rect;
93 private Point _p0, _p1, _p2, _p3;
94 private float _r1, _r2, _r3;
95 private int _mode;
96 private int _handle;
97 private MouseControllerButton _button;
98 private DeleteController _deleteController;
99 private ContextualMenuVisitor _contextVisitor;
100
101 public SelectShapeController(RepApplication app, DeleteController deleteController, ContextualMenuVisitor contextVisitor) {
102 super(app);
103 _deleteController = deleteController;
104 _contextVisitor = contextVisitor;
105 _button = new MouseControllerButton(_app.getIcons().selectIcon().createImage(), _app.getConstants().selectCommand(), this);
106 _mTmp = new TransformMatrix();
107 _mOld = new TransformMatrix();
108 _mInv = new TransformMatrix();
109 _rect = new Rect();
110 _rect.setAttribute(Attribute.LINE_STYLE, Color.BLACK);
111 _rect.setAttribute(Attribute.LINE_OPACITY, new FloatAttributeValue(1f));
112 _rect.setAttribute(Attribute.FILL_STYLE, NoneStyle.NONE);
113 _rect.setAttribute(Attribute.FILL_OPACITY, new FloatAttributeValue(0f));
114 _rect.setAttribute(Attribute.LINE_WIDTH, new FloatAttributeValue(1f));
115
116 _p0 = new Point();
117 _p1 = new Point();
118 _p2 = new Point();
119 _p3 = new Point();
120 }
121
122 public MouseControllerButton getButton() {
123 return _button;
124 }
125
126 @Override
127 public void render(DrawingView view) {
128 Shape rootShape = _app.getSelection().getRootShape();
129 if (rootShape != null) {
130 BoundingBox bbox = rootShape.getBoundingBox();
131 rootShape.getTransform().copyTo(_mTmp);
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148 rootShape.acceptVisitor(view.getRenderer());
149 Color.BLACK.acceptVisitor(view.getStrokeStyleVisitor());
150 Color.BLACK.acceptVisitor(view.getFillStyleVisitor());
151 view.setGlobalAlpha(1f);
152 view.setLineWidth(1f);
153 view.beginPath();
154 bbox.getPoint(BoundingBox.PT_NE, _p0).transform(_mTmp);
155 view.moveTo(_p0.x, _p0.y);
156 for (int i = BoundingBox.PT_NW; i <= BoundingBox.PT_NE; i++) {
157 bbox.getPoint(i, _p0).transform(_mTmp);
158 view.lineTo(_p0.x, _p0.y);
159 }
160 bbox.getPoint(BoundingBox.PT_E, _p0);
161 bbox.getPoint(BoundingBox.PT_E, _p0).transform(_mTmp);
162 view.moveTo(_p0.x, _p0.y);
163 bbox.getPoint(BoundingBox.PT_R, _p0);
164 _p0.transform(_mTmp);
165 view.lineTo(_p0.x, _p0.y);
166 view.stroke();
167 view.beginPath();
168 bbox.getPoint(BoundingBox.PT_R, _p0);
169 _p0.transform(_mTmp);
170 view.arc(_p0.x, _p0.y, HANDLE_SIZE, 0f, (float)(2 * Math.PI), true);
171 view.fill();
172 for (int i = BoundingBox.PT_NW; i <= BoundingBox.PT_E; i++) {
173 bbox.getPoint(i, _p0).transform(_mTmp);
174 view.fillRect(_p0.x - HANDLE_SIZE, _p0.y - HANDLE_SIZE, HANDLE_SIZE * 2, HANDLE_SIZE * 2);
175 }
176 }
177 if (_mode == MODE_RECT) {
178 Color.BLACK.acceptVisitor(view.getStrokeStyleVisitor());
179 Color.BLACK.acceptVisitor(view.getFillStyleVisitor());
180 view.setGlobalAlpha(1f);
181 view.getRenderer().visitRect(_rect);
182 }
183 }
184
185 @Override
186 public void activate(DrawingView view) {
187 _mode = MODE_HOVER;
188 _app.getSelection().select(new ArrayList<Shape>());
189 view.setCursor(_cursors[_handle]);
190 }
191
192 @Override
193 public void mouseDown(DrawingView view, Point p, int modifiers) {
194 p.copyTo(_p1);
195 view.toModelCoordinates(p);
196 Event event = DOM.eventGetCurrentEvent();
197 if (DOM.eventGetButton(event) == Event.BUTTON_RIGHT) {
198 MenuBar bar = null;
199 if (view.getPicker().pick(p, _app.getSelection().iterator()) == null) {
200 Shape shape = view.getPicker().pick(p, _app.getModel().reverseIterator());
201 if (shape != null) {
202 List<Shape> newSelection = new ArrayList<Shape>();
203 newSelection.add(shape);
204 _app.getSelection().select(newSelection);
205 bar = _contextVisitor.getContextualMenu(_app.getSelection());
206 }
207 } else {
208 bar = _contextVisitor.getContextualMenu(_app.getSelection());
209 }
210 if (bar != null) {
211 ControllerContextItem.popup.setWidget(bar);
212 ControllerContextItem.popup.setPopupPosition(DOM.eventGetClientX(event), DOM.eventGetClientY(event));
213 ControllerContextItem.popup.show();
214 }
215 } else {
216 boolean ctrlPressed = (modifiers & KeyboardListener.MODIFIER_CTRL) != 0;
217 Shape rootShape = _app.getSelection().getRootShape();
218
219 _handle = getHandle(view, p);
220 switch(_handle) {
221 case BoundingBox.PT_NW:
222 case BoundingBox.PT_SW:
223 case BoundingBox.PT_SE:
224 case BoundingBox.PT_NE:
225 case BoundingBox.PT_N:
226 case BoundingBox.PT_W:
227 case BoundingBox.PT_S:
228 case BoundingBox.PT_E:
229 _mode = MODE_SCALE;
230 rootShape.getTransform().copyTo(_mOld);
231
232 rootShape.getScaling(_p1);
233 rootShape.getTransform().invert(_mInv);
234 break;
235
236 case BoundingBox.PT_R:
237 _mode = MODE_ROTATE;
238 rootShape.getTransform().copyTo(_mOld);
239
240
241 _r1 = rootShape.getRotation();
242 rootShape.getTransform().invert(_mInv);
243
244
245
246 p.transform(_mInv).subtract(rootShape.getBoundingBox().getPoint(BoundingBox.PT_C, _p0));
247
248
249 _r2 = (float)(Math.acos(p.x / p.length()));
250 if (p.y < 0) {
251 _r2 = -_r2;
252 }
253 _r3 = _r2;
254 break;
255 case HANDLE_MOVE:
256 if (ctrlPressed) {
257 List<Shape> oldSelection = _app.getSelection().getSelectedShapes();
258 List<Shape> newSelection = new ArrayList<Shape>(oldSelection);
259 newSelection.remove(view.getPicker().pick(p, _app.getSelection().iterator()));
260 _app.getSelection().select(newSelection);
261 _mode = MODE_HOVER;
262 } else {
263 _mode = MODE_TRANSLATE;
264 rootShape.getTransform().copyTo(_mOld);
265
266 p.copyTo(_p1);
267
268
269 p.subtract(rootShape.getTranslation(_p2), _p2);
270 }
271 break;
272 case HANDLE_NONE:
273 {
274 Shape shape = view.getPicker().pick(p, _app.getModel().reverseIterator());
275 if (shape == null) {
276 RepApplication.app.debugPrint("rect");
277 if (!ctrlPressed) {
278 _app.getSelection().select(new ArrayList<Shape>());
279 }
280 _mode = MODE_RECT;
281 _rect.setTranslation(p);
282 _rect.setRotation(-view.getRotation());
283 _rect.setScaling(Point.UNIT);
284 view.getScaling(_p3);
285 } else {
286 RepApplication.app.debugPrint("select");
287 if (!ctrlPressed) {
288 _app.getSelection().select(new ArrayList<Shape>());
289 }
290 List<Shape> oldSelection = _app.getSelection().getSelectedShapes();
291 List<Shape> newSelection = new ArrayList<Shape>(oldSelection);
292 newSelection.add(shape);
293 rootShape = _app.getSelection().select(newSelection);
294 _mode = MODE_TRANSLATE;
295 rootShape.getTransform().copyTo(_mOld);
296
297 p.copyTo(_p1);
298
299
300 p.subtract(rootShape.getTranslation(_p2), _p2);
301 }
302
303 }
304 break;
305 }
306 }
307 }
308
309 private int getHandle(DrawingView view, Point p) {
310 Shape rootShape = _app.getSelection().getRootShape();
311 if (rootShape != null) {
312 BoundingBox bbox = rootShape.getBoundingBox();
313
314
315 for (int i = BoundingBox.PT_NW; i <= BoundingBox.PT_E; i++) {
316 bbox.getPoint(i, _p0).transform(rootShape.getTransform());
317 if (p.x >= _p0.x - HANDLE_SIZE && p.x <= _p0.x + HANDLE_SIZE
318 && p.y >= _p0.y - HANDLE_SIZE && p.y <= _p0.y + HANDLE_SIZE) {
319 return i;
320 }
321 }
322
323
324 bbox.getPoint(BoundingBox.PT_R, _p0).transform(rootShape.getTransform());
325 if (p.subtract(_p0, _p0).length() <= HANDLE_SIZE) {
326 return BoundingBox.PT_R;
327 }
328
329
330 if (view.getPicker().pick(p, _app.getSelection().iterator()) != null) {
331 return HANDLE_MOVE;
332 }
333 }
334 return HANDLE_NONE;
335 }
336
337 @Override
338 public void mouseMove(DrawingView view, Point p, int modifiers) {
339 p.copyTo(_p0);
340 view.toModelCoordinates(p);
341 Shape rootShape = _app.getSelection().getRootShape();
342 switch(_mode) {
343 case MODE_RECT:
344 {
345 _p0.add(_p1, _p2).multiply(0.5f).subtract(_p1);
346 _p2.x /= _p3.x;
347 _p2.y /= _p3.y;
348 _rect.setScaling(_p2);
349 float cos = (float)Math.cos(view.getRotation());
350 float sin = (float)Math.sin(view.getRotation());
351 p.x = p.x - _p2.x * cos - _p2.y * sin;
352 p.y = p.y + _p2.x * sin - _p2.y * cos;
353 _rect.setTranslation(p);
354 }
355 break;
356 case MODE_HOVER:
357 {
358 _handle = getHandle(view, p);
359 view.setCursor(_cursors[_handle]);
360 }
361 break;
362 case MODE_TRANSLATE:
363 {
364 rootShape.setTranslation(p.subtract(_p2));
365 }
366 break;
367 case MODE_ROTATE:
368 {
369
370
371 p.transform(_mInv).subtract(rootShape.getBoundingBox().getPoint(BoundingBox.PT_C, _p0));
372
373
374 float d = p.length();
375 if (d > 0f) {
376 _r3 = (float)(Math.acos(p.x / d));
377 if (p.y < 0) {
378 _r3 = -_r3;
379 }
380 float dr = _r3 - _r2;
381 rootShape.getScaling(_p0);
382 if (_p0.x * _p0.y < 0) {
383 dr = - dr;
384 }
385 rootShape.setRotation(_r1 + dr);
386 }
387 }
388 break;
389 case MODE_SCALE:
390 {
391 BoundingBox bbox = rootShape.getBoundingBox();
392 rootShape.getScaling(_p2);
393 bbox.getPoint(BoundingBox.PT_C, _p3);
394 bbox.getPoint(_handle, _p0).subtract(_p3);
395 p.transform(_mInv).subtract(_p3);
396 switch(_handle) {
397 case BoundingBox.PT_NW:
398 case BoundingBox.PT_SW:
399 case BoundingBox.PT_SE:
400 case BoundingBox.PT_NE:
401 case BoundingBox.PT_N:
402 case BoundingBox.PT_S:
403 _p2.y = _p1.y * p.y / _p0.y;
404 }
405 switch(_handle) {
406 case BoundingBox.PT_NW:
407 case BoundingBox.PT_SW:
408 case BoundingBox.PT_SE:
409 case BoundingBox.PT_NE:
410 case BoundingBox.PT_W:
411 case BoundingBox.PT_E:
412 _p2.x = _p1.x * p.x / _p0.x;
413 }
414 if (rootShape instanceof ShapeGroup && ((ShapeGroup)rootShape).containsRotatedShape()) {
415
416
417 _p2.x = Math.max(_p2.x, _p2.y);
418 _p2.y = _p2.x;
419 }
420 rootShape.setScaling(_p2);
421 }
422 break;
423 }
424 }
425
426 @Override
427 public void mouseUp(DrawingView view, Point p, int modifiers) {
428 mouseMove(view, p, modifiers);
429 deactivate(view);
430 }
431
432 @Override
433 public void keyDown(DrawingView view, char keyCode, int modifiers) {
434 RepApplication.app.debugPrint("keyDown: " + keyCode);
435 if (keyCode == KeyboardListener.KEY_DELETE || keyCode == KeyboardListener.KEY_BACKSPACE && _app.getSelection().getSelectedShapes().size() > 0) {
436 _deleteController.activate(view);
437 }
438 }
439
440 @Override
441 public void deactivate(DrawingView view) {
442 Shape rootShape = _app.getSelection().getRootShape();
443 switch(_mode) {
444 case MODE_RECT:
445 {
446 List<Shape> newSelection = new ArrayList<Shape>();
447
448
449 _rect.getScaling(_p0);
450 if (_p0.x != 0f && _p0.y != 0f) {
451
452 Iterator<Shape> iterator = _app.getModel().iterator();
453 RepApplication.app.debugPrint("size = " + _app.getModel().count());
454 while (iterator.hasNext()) {
455 Shape shape = iterator.next();
456 BoundingBox bbox = shape.getBoundingBox();
457 boolean inSelectionRect = true;
458
459
460 shape.getTransform().preMultiply(_rect.getTransform().invert(_mTmp), _mTmp);
461 for (int i = BoundingBox.PT_NW; i <= BoundingBox.PT_NE; i++) {
462 if (!_rect.getBoundingBox().containsPoint(bbox.getPoint(i, _p1).transform(_mTmp))) {
463 inSelectionRect = false;
464 break;
465 }
466 }
467 if (inSelectionRect) {
468 newSelection.add(shape);
469 }
470 }
471 }
472 _app.getSelection().select(newSelection);
473 }
474 break;
475 case MODE_TRANSLATE:
476 {
477
478 rootShape.getTranslation(_p0).subtract(_p1).add(_p2);
479 if (_p0.squaredLength() > 0f) {
480
481 _mOld.invert(_mTmp).preMultiply(rootShape.getTransform());
482 ICommand command = new TransformShapeCommand(_app, _mTmp, _mOld, TransformShapeCommand.TRANSLATE, _app.getSelector(), this);
483 command.execute();
484 _app.getHistory().addCommand(command);
485 }
486 }
487 break;
488 case MODE_ROTATE:
489 {
490 float dr = _r3 - _r2;
491 if (dr != 0f) {
492
493 _mOld.invert(_mTmp).preMultiply(rootShape.getTransform());
494 ICommand command = new TransformShapeCommand(_app, _mTmp, _mOld, TransformShapeCommand.ROTATE, _app.getSelector(), this);
495 command.execute();
496 _app.getHistory().addCommand(command);
497 }
498 }
499 break;
500 case MODE_SCALE:
501 {
502 rootShape.getScaling(_p2);
503 if (!_p1.equals(_p2)) {
504
505 _mOld.invert(_mTmp).preMultiply(rootShape.getTransform());
506 ICommand command = new TransformShapeCommand(_app, _mTmp, _mOld, TransformShapeCommand.SCALE, _app.getSelector(), this);
507 command.execute();
508 _app.getHistory().addCommand(command);
509 }
510 }
511 break;
512 }
513 _mode = MODE_HOVER;
514 RepApplication.app.debugPrint("end deactivate1");
515 RepApplication.app.debugPrint("end deactivate2");
516 }
517 }