xref: /haiku/src/apps/icon-o-matic/transformable/TransformBoxStates.cpp (revision 546208a53940a26c6379c48a7854ade1a8250fc5)
1 /*
2  * Copyright 2006-2009, Haiku.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Stephan Aßmus <superstippi@gmx.de>
7  */
8 
9 #include "TransformBoxStates.h"
10 
11 #include <math.h>
12 #include <stdio.h>
13 
14 #include <Catalog.h>
15 #include <Cursor.h>
16 #include <InterfaceDefs.h>
17 #include <Locale.h>
18 #include <View.h>
19 
20 #include "cursors.h"
21 #include "support.h"
22 
23 #include "TransformBox.h"
24 
25 
26 #undef B_TRANSLATION_CONTEXT
27 #define B_TRANSLATION_CONTEXT "Icon-O-Matic-TransformationBoxStates"
28 
29 
30 // constructor
31 DragState::DragState(TransformBox* parent)
32 	:
33 	fOrigin(0.0, 0.0),
34 	fParent(parent)
35 {
36 }
37 
38 
39 // SetOrigin
40 void
41 DragState::SetOrigin(BPoint origin)
42 {
43 	fOrigin = origin;
44 }
45 
46 
47 // ActionName
48 const char*
49 DragState::ActionName() const
50 {
51 	return B_TRANSLATE("Transformation");
52 }
53 
54 
55 // ActionNameIndex
56 uint32
57 DragState::ActionNameIndex() const
58 {
59 	return TRANSFORMATION;
60 }
61 
62 
63 // _SetViewCursor
64 void
65 DragState::_SetViewCursor(BView* view, const uchar* cursorData) const
66 {
67 	BCursor cursor(cursorData);
68 	view->SetViewCursor(&cursor);
69 }
70 
71 
72 // #pragma mark - DragCornerState
73 
74 
75 // constructor
76 DragCornerState::DragCornerState(TransformBox* parent, uint32 corner)
77 	:
78 	DragState(parent),
79 	fCorner(corner)
80 {
81 }
82 
83 
84 // SetOrigin
85 void
86 DragCornerState::SetOrigin(BPoint origin)
87 {
88 	fOldXScale = fParent->LocalXScale();
89 	fOldYScale = fParent->LocalYScale();
90 
91 	fOldOffset = fParent->Translation();
92 
93 	// copy the matrix at the start of the drag procedure
94 	fMatrix.reset();
95 	fMatrix.multiply(agg::trans_affine_scaling(fOldXScale, fOldYScale));
96 	fMatrix.multiply(agg::trans_affine_rotation(fParent->LocalRotation()
97 		* M_PI / 180.0));
98 	fMatrix.multiply(agg::trans_affine_translation(fParent->Translation().x,
99 		fParent->Translation().y));
100 
101 	double x = origin.x;
102 	double y = origin.y;
103 	fMatrix.inverse_transform(&x, &y);
104 	origin.x = x;
105 	origin.y = y;
106 
107 	BRect box = fParent->Box();
108 	switch (fCorner) {
109 		case LEFT_TOP_CORNER:
110 			fXOffsetFromCorner = origin.x - box.left;
111 			fYOffsetFromCorner = origin.y - box.top;
112 			fOldWidth = box.left - box.right;
113 			fOldHeight = box.top - box.bottom;
114 			origin.x = box.right;
115 			origin.y = box.bottom;
116 			break;
117 		case RIGHT_TOP_CORNER:
118 			fXOffsetFromCorner = origin.x - box.right;
119 			fYOffsetFromCorner = origin.y - box.top;
120 			fOldWidth = box.right - box.left;
121 			fOldHeight = box.top - box.bottom;
122 			origin.x = box.left;
123 			origin.y = box.bottom;
124 			break;
125 		case LEFT_BOTTOM_CORNER:
126 			fXOffsetFromCorner = origin.x - box.left;
127 			fYOffsetFromCorner = origin.y - box.bottom;
128 			fOldWidth = box.left - box.right;
129 			fOldHeight = box.bottom - box.top;
130 			origin.x = box.right;
131 			origin.y = box.top;
132 			break;
133 		case RIGHT_BOTTOM_CORNER:
134 			fXOffsetFromCorner = origin.x - box.right;
135 			fYOffsetFromCorner = origin.y - box.bottom;
136 			fOldWidth = box.right - box.left;
137 			fOldHeight = box.bottom - box.top;
138 			origin.x = box.left;
139 			origin.y = box.top;
140 			break;
141 	}
142 	DragState::SetOrigin(origin);
143 }
144 
145 
146 // DragTo
147 void
148 DragCornerState::DragTo(BPoint current, uint32 modifiers)
149 {
150 	double x = current.x;
151 	double y = current.y;
152 	fMatrix.inverse_transform(&x, &y);
153 
154 	double xScale = 1.0;
155 	double yScale = 1.0;
156 	BPoint translation(0.0, 0.0);
157 	switch (fCorner) {
158 		case LEFT_TOP_CORNER:
159 		case RIGHT_TOP_CORNER:
160 		case LEFT_BOTTOM_CORNER:
161 		case RIGHT_BOTTOM_CORNER:
162 			x -= fOrigin.x;
163 			y -= fOrigin.y;
164 			if (fOldWidth != 0.0)
165 				xScale = (x - fXOffsetFromCorner) / (fOldWidth);
166 			if (fOldHeight != 0.0)
167 				yScale = (y - fYOffsetFromCorner) / (fOldHeight);
168 			// constrain aspect ratio if shift is pressed
169 			if (modifiers & B_SHIFT_KEY) {
170 				if (fabs(xScale) > fabs(yScale))
171 					yScale = yScale > 0.0 ? fabs(xScale) : -fabs(xScale);
172 				else
173 					xScale = xScale > 0.0 ? fabs(yScale) : -fabs(yScale);
174 			}
175 			translation.x = fOrigin.x - fOrigin.x * xScale;
176 			translation.y = fOrigin.y - fOrigin.y * yScale;
177 			break;
178 	}
179 	x = translation.x;
180 	y = translation.y;
181 	fMatrix.transform(&x, &y);
182 	translation.x = x;
183 	translation.y = y;
184 
185 	fParent->SetTranslationAndScale(translation, xScale * fOldXScale,
186 		yScale * fOldYScale);
187 }
188 
189 
190 // UpdateViewCursor
191 void
192 DragCornerState::UpdateViewCursor(BView* view, BPoint current) const
193 {
194 	float rotation = fmod(360.0 - fParent->ViewSpaceRotation() + 22.5, 180.0);
195 	bool flipX = fParent->LocalXScale() < 0.0;
196 	bool flipY = fParent->LocalYScale() < 0.0;
197 	if (rotation < 45.0) {
198 		switch (fCorner) {
199 			case LEFT_TOP_CORNER:
200 			case RIGHT_BOTTOM_CORNER:
201 				if (flipX) {
202 					_SetViewCursor(view, flipY
203 						? kLeftTopRightBottomCursor : kLeftBottomRightTopCursor);
204 				} else {
205 					_SetViewCursor(view, flipY
206 						? kLeftBottomRightTopCursor : kLeftTopRightBottomCursor);
207 				}
208 				break;
209 			case RIGHT_TOP_CORNER:
210 			case LEFT_BOTTOM_CORNER:
211 				if (flipX) {
212 					_SetViewCursor(view, flipY
213 						? kLeftBottomRightTopCursor : kLeftTopRightBottomCursor);
214 				} else {
215 					_SetViewCursor(view, flipY
216 						? kLeftTopRightBottomCursor : kLeftBottomRightTopCursor);
217 				}
218 				break;
219 		}
220 	} else if (rotation < 90.0) {
221 		switch (fCorner) {
222 			case LEFT_TOP_CORNER:
223 			case RIGHT_BOTTOM_CORNER:
224 				if (flipX) {
225 					_SetViewCursor(view,
226 						flipY ? kLeftRightCursor : kUpDownCursor);
227 				} else {
228 					_SetViewCursor(view,
229 						flipY ? kUpDownCursor : kLeftRightCursor);
230 				}
231 				break;
232 			case RIGHT_TOP_CORNER:
233 			case LEFT_BOTTOM_CORNER:
234 				if (flipX) {
235 					_SetViewCursor(view,
236 						flipY ? kUpDownCursor : kLeftRightCursor);
237 				} else {
238 					_SetViewCursor(view,
239 						flipY ? kLeftRightCursor : kUpDownCursor);
240 				}
241 				break;
242 		}
243 	} else if (rotation < 135.0) {
244 		switch (fCorner) {
245 			case LEFT_TOP_CORNER:
246 			case RIGHT_BOTTOM_CORNER:
247 				if (flipX) {
248 					_SetViewCursor(view, flipY
249 						? kLeftBottomRightTopCursor : kLeftTopRightBottomCursor);
250 				} else {
251 					_SetViewCursor(view, flipY
252 						? kLeftTopRightBottomCursor : kLeftBottomRightTopCursor);
253 				}
254 				break;
255 			case RIGHT_TOP_CORNER:
256 			case LEFT_BOTTOM_CORNER:
257 				if (flipX) {
258 					_SetViewCursor(view, flipY
259 						? kLeftTopRightBottomCursor : kLeftBottomRightTopCursor);
260 				} else {
261 					_SetViewCursor(view, flipY
262 						? kLeftBottomRightTopCursor : kLeftTopRightBottomCursor);
263 				}
264 				break;
265 		}
266 	} else {
267 		switch (fCorner) {
268 			case LEFT_TOP_CORNER:
269 			case RIGHT_BOTTOM_CORNER:
270 				if (flipX) {
271 					_SetViewCursor(view,
272 						flipY ? kUpDownCursor : kLeftRightCursor);
273 				} else {
274 					_SetViewCursor(view,
275 						flipY ? kLeftRightCursor : kUpDownCursor);
276 				}
277 				break;
278 			case RIGHT_TOP_CORNER:
279 			case LEFT_BOTTOM_CORNER:
280 				if (flipX) {
281 					_SetViewCursor(view,
282 						flipY ? kLeftRightCursor : kUpDownCursor);
283 				} else {
284 					_SetViewCursor(view,
285 						flipY ? kUpDownCursor : kLeftRightCursor);
286 				}
287 				break;
288 		}
289 	}
290 }
291 
292 
293 // ActionName
294 const char*
295 DragCornerState::ActionName() const
296 {
297 	return B_TRANSLATE("Scale");
298 }
299 
300 
301 // ActionNameIndex
302 uint32
303 DragCornerState::ActionNameIndex() const
304 {
305 	return SCALE;
306 }
307 
308 
309 // #pragma mark - DragSideState
310 
311 
312 DragSideState::DragSideState(TransformBox* parent, uint32 side)
313 	:
314 	DragState(parent),
315 	fSide(side)
316 {
317 }
318 
319 
320 // SetOrigin
321 void
322 DragSideState::SetOrigin(BPoint origin)
323 {
324 	fOldXScale = fParent->LocalXScale();
325 	fOldYScale = fParent->LocalYScale();
326 
327 	fOldOffset = fParent->Translation();
328 
329 	// copy the matrix at the start of the drag procedure
330 	fMatrix.reset();
331 	fMatrix.multiply(agg::trans_affine_scaling(fOldXScale, fOldYScale));
332 	fMatrix.multiply(agg::trans_affine_rotation(fParent->LocalRotation()
333 		* M_PI / 180.0));
334 	fMatrix.multiply(agg::trans_affine_translation(fParent->Translation().x,
335 		fParent->Translation().y));
336 
337 	double x = origin.x;
338 	double y = origin.y;
339 	fMatrix.inverse_transform(&x, &y);
340 	origin.x = x;
341 	origin.y = y;
342 
343 	BRect box = fParent->Box();
344 	switch (fSide) {
345 		case LEFT_SIDE:
346 			fOffsetFromSide = origin.x - box.left;
347 			fOldSideDist = box.left - box.right;
348 			origin.x = box.right;
349 			break;
350 		case RIGHT_SIDE:
351 			fOffsetFromSide = origin.x - box.right;
352 			fOldSideDist = box.right - box.left;
353 			origin.x = box.left;
354 			break;
355 		case TOP_SIDE:
356 			fOffsetFromSide = origin.y - box.top;
357 			fOldSideDist = box.top - box.bottom;
358 			origin.y = box.bottom;
359 			break;
360 		case BOTTOM_SIDE:
361 			fOffsetFromSide = origin.y - box.bottom;
362 			fOldSideDist = box.bottom - box.top;
363 			origin.y = box.top;
364 			break;
365 	}
366 	DragState::SetOrigin(origin);
367 }
368 
369 
370 // DragTo
371 void
372 DragSideState::DragTo(BPoint current, uint32 modifiers)
373 {
374 	double x = current.x;
375 	double y = current.y;
376 	fMatrix.inverse_transform(&x, &y);
377 
378 	double xScale = 1.0;
379 	double yScale = 1.0;
380 	BPoint translation(0.0, 0.0);
381 	switch (fSide) {
382 		case LEFT_SIDE:
383 		case RIGHT_SIDE:
384 			x -= fOrigin.x;
385 			if (fOldSideDist != 0.0)
386 				xScale = (x - fOffsetFromSide) / (fOldSideDist);
387 			translation.x = fOrigin.x - fOrigin.x * xScale;
388 			break;
389 		case TOP_SIDE:
390 		case BOTTOM_SIDE:
391 			y -= fOrigin.y;
392 			if (fOldSideDist != 0.0)
393 				yScale = (y - fOffsetFromSide) / (fOldSideDist);
394 			translation.y = fOrigin.y - fOrigin.y * yScale;
395 			break;
396 	}
397 	x = translation.x;
398 	y = translation.y;
399 	fMatrix.transform(&x, &y);
400 	translation.x = x;
401 	translation.y = y;
402 
403 	fParent->SetTranslationAndScale(translation, xScale * fOldXScale,
404 		yScale * fOldYScale);
405 }
406 
407 
408 // UpdateViewCursor
409 void
410 DragSideState::UpdateViewCursor(BView* view, BPoint current) const
411 {
412 	float rotation = fmod(360.0 - fParent->ViewSpaceRotation() + 22.5, 180.0);
413 	if (rotation < 45.0) {
414 		switch (fSide) {
415 			case LEFT_SIDE:
416 			case RIGHT_SIDE:
417 				_SetViewCursor(view, kLeftRightCursor);
418 				break;
419 			case TOP_SIDE:
420 			case BOTTOM_SIDE:
421 				_SetViewCursor(view, kUpDownCursor);
422 				break;
423 		}
424 	} else if (rotation < 90.0) {
425 		switch (fSide) {
426 			case LEFT_SIDE:
427 			case RIGHT_SIDE:
428 				_SetViewCursor(view, kLeftBottomRightTopCursor);
429 				break;
430 			case TOP_SIDE:
431 			case BOTTOM_SIDE:
432 				_SetViewCursor(view, kLeftTopRightBottomCursor);
433 				break;
434 		}
435 	} else if (rotation < 135.0) {
436 		switch (fSide) {
437 			case LEFT_SIDE:
438 			case RIGHT_SIDE:
439 				_SetViewCursor(view, kUpDownCursor);
440 				break;
441 			case TOP_SIDE:
442 			case BOTTOM_SIDE:
443 				_SetViewCursor(view, kLeftRightCursor);
444 				break;
445 		}
446 	} else {
447 		switch (fSide) {
448 			case LEFT_SIDE:
449 			case RIGHT_SIDE:
450 				_SetViewCursor(view, kLeftTopRightBottomCursor);
451 				break;
452 			case TOP_SIDE:
453 			case BOTTOM_SIDE:
454 				_SetViewCursor(view, kLeftBottomRightTopCursor);
455 				break;
456 		}
457 	}
458 }
459 
460 
461 // ActionName
462 const char*
463 DragSideState::ActionName() const
464 {
465 	return B_TRANSLATE("Scale");
466 }
467 
468 
469 // ActionNameIndex
470 uint32
471 DragSideState::ActionNameIndex() const
472 {
473 	return SCALE;
474 }
475 
476 
477 // #pragma mark - DragBoxState
478 
479 
480 // SetOrigin
481 void
482 DragBoxState::SetOrigin(BPoint origin)
483 {
484 	fOldTranslation = fParent->Translation();
485 	DragState::SetOrigin(origin);
486 }
487 
488 
489 // DragTo
490 void
491 DragBoxState::DragTo(BPoint current, uint32 modifiers)
492 {
493 	BPoint offset = current - fOrigin;
494 	BPoint newTranslation = fOldTranslation + offset;
495 	if (modifiers & B_SHIFT_KEY) {
496 		if (fabs(offset.x) > fabs(offset.y))
497 			newTranslation.y = fOldTranslation.y;
498 		else
499 			newTranslation.x = fOldTranslation.x;
500 	}
501 	fParent->TranslateBy(newTranslation - fParent->Translation());
502 }
503 
504 
505 // UpdateViewCursor
506 void
507 DragBoxState::UpdateViewCursor(BView* view, BPoint current) const
508 {
509 	_SetViewCursor(view, kMoveCursor);
510 }
511 
512 
513 // ActionName
514 const char*
515 DragBoxState::ActionName() const
516 {
517 	return B_TRANSLATE("Move");
518 }
519 
520 
521 // ActionNameIndex
522 uint32
523 DragBoxState::ActionNameIndex() const
524 {
525 	return MOVE;
526 }
527 
528 
529 // #pragma mark - RotateBoxState
530 
531 
532 // constructor
533 RotateBoxState::RotateBoxState(TransformBox* parent)
534 	:
535 	DragState(parent),
536 	fOldAngle(0.0)
537 {
538 }
539 
540 
541 // SetOrigin
542 void
543 RotateBoxState::SetOrigin(BPoint origin)
544 {
545 	DragState::SetOrigin(origin);
546 	fOldAngle = fParent->LocalRotation();
547 }
548 
549 
550 // DragTo
551 void
552 RotateBoxState::DragTo(BPoint current, uint32 modifiers)
553 {
554 	double angle = calc_angle(fParent->Center(), fOrigin, current);
555 
556 	if (modifiers & B_SHIFT_KEY) {
557 		if (angle < 0.0)
558 			angle -= 22.5;
559 		else
560 			angle += 22.5;
561 		angle = 45.0 * ((int32)angle / 45);
562 	}
563 
564 	double newAngle = fOldAngle + angle;
565 
566 	fParent->RotateBy(fParent->Center(), newAngle - fParent->LocalRotation());
567 }
568 
569 
570 // UpdateViewCursor
571 void
572 RotateBoxState::UpdateViewCursor(BView* view, BPoint current) const
573 {
574 	BPoint origin(fParent->Center());
575 	fParent->TransformToCanvas(origin);
576 	fParent->TransformToCanvas(current);
577 	BPoint from = origin + BPoint(sinf(22.5 * 180.0 / M_PI) * 50.0,
578 		-cosf(22.5 * 180.0 / M_PI) * 50.0);
579 
580 	float rotation = calc_angle(origin, from, current) + 180.0;
581 
582 	if (rotation < 45.0) {
583 		_SetViewCursor(view, kRotateLCursor);
584 	} else if (rotation < 90.0) {
585 		_SetViewCursor(view, kRotateLTCursor);
586 	} else if (rotation < 135.0) {
587 		_SetViewCursor(view, kRotateTCursor);
588 	} else if (rotation < 180.0) {
589 		_SetViewCursor(view, kRotateRTCursor);
590 	} else if (rotation < 225.0) {
591 		_SetViewCursor(view, kRotateRCursor);
592 	} else if (rotation < 270.0) {
593 		_SetViewCursor(view, kRotateRBCursor);
594 	} else if (rotation < 315.0) {
595 		_SetViewCursor(view, kRotateBCursor);
596 	} else {
597 		_SetViewCursor(view, kRotateLBCursor);
598 	}
599 }
600 
601 
602 // ActionName
603 const char*
604 RotateBoxState::ActionName() const
605 {
606 	return B_TRANSLATE("Rotate");
607 }
608 
609 
610 // ActionNameIndex
611 uint32
612 RotateBoxState::ActionNameIndex() const
613 {
614 	return ROTATE;
615 }
616 
617 
618 // #pragma mark - OffsetCenterState
619 
620 
621 // SetOrigin
622 void
623 OffsetCenterState::SetOrigin(BPoint origin)
624 {
625 	fParent->InverseTransform(&origin);
626 	DragState::SetOrigin(origin);
627 }
628 
629 
630 // DragTo
631 void
632 OffsetCenterState::DragTo(BPoint current, uint32 modifiers)
633 {
634 	fParent->InverseTransform(&current);
635 	fParent->OffsetCenter(current - fOrigin);
636 	fOrigin = current;
637 }
638 
639 
640 // UpdateViewCursor
641 void
642 OffsetCenterState::UpdateViewCursor(BView* view, BPoint current) const
643 {
644 	_SetViewCursor(view, kPathMoveCursor);
645 }
646 
647 
648 // ActionName
649 const char*
650 OffsetCenterState::ActionName() const
651 {
652 	return B_TRANSLATE("Move Pivot");
653 }
654 
655 
656 // ActionNameIndex
657 uint32
658 OffsetCenterState::ActionNameIndex() const
659 {
660 	return MOVE_PIVOT;
661 }
662