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
31DragState::DragState(TransformBox* parent)
32	:
33	fOrigin(0.0, 0.0),
34	fParent(parent)
35{
36}
37
38
39// SetOrigin
40void
41DragState::SetOrigin(BPoint origin)
42{
43	fOrigin = origin;
44}
45
46
47// ActionName
48const char*
49DragState::ActionName() const
50{
51	return B_TRANSLATE("Transformation");
52}
53
54
55// ActionNameIndex
56uint32
57DragState::ActionNameIndex() const
58{
59	return TRANSFORMATION;
60}
61
62
63// _SetViewCursor
64void
65DragState::_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
76DragCornerState::DragCornerState(TransformBox* parent, uint32 corner)
77	:
78	DragState(parent),
79	fCorner(corner)
80{
81}
82
83
84// SetOrigin
85void
86DragCornerState::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
147void
148DragCornerState::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
191void
192DragCornerState::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
294const char*
295DragCornerState::ActionName() const
296{
297	return B_TRANSLATE("Scale");
298}
299
300
301// ActionNameIndex
302uint32
303DragCornerState::ActionNameIndex() const
304{
305	return SCALE;
306}
307
308
309// #pragma mark - DragSideState
310
311
312DragSideState::DragSideState(TransformBox* parent, uint32 side)
313	:
314	DragState(parent),
315	fSide(side)
316{
317}
318
319
320// SetOrigin
321void
322DragSideState::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
371void
372DragSideState::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
409void
410DragSideState::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
462const char*
463DragSideState::ActionName() const
464{
465	return B_TRANSLATE("Scale");
466}
467
468
469// ActionNameIndex
470uint32
471DragSideState::ActionNameIndex() const
472{
473	return SCALE;
474}
475
476
477// #pragma mark - DragBoxState
478
479
480// SetOrigin
481void
482DragBoxState::SetOrigin(BPoint origin)
483{
484	fOldTranslation = fParent->Translation();
485	DragState::SetOrigin(origin);
486}
487
488
489// DragTo
490void
491DragBoxState::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
506void
507DragBoxState::UpdateViewCursor(BView* view, BPoint current) const
508{
509	_SetViewCursor(view, kMoveCursor);
510}
511
512
513// ActionName
514const char*
515DragBoxState::ActionName() const
516{
517	return B_TRANSLATE("Move");
518}
519
520
521// ActionNameIndex
522uint32
523DragBoxState::ActionNameIndex() const
524{
525	return MOVE;
526}
527
528
529// #pragma mark - RotateBoxState
530
531
532// constructor
533RotateBoxState::RotateBoxState(TransformBox* parent)
534	:
535	DragState(parent),
536	fOldAngle(0.0)
537{
538}
539
540
541// SetOrigin
542void
543RotateBoxState::SetOrigin(BPoint origin)
544{
545	DragState::SetOrigin(origin);
546	fOldAngle = fParent->LocalRotation();
547}
548
549
550// DragTo
551void
552RotateBoxState::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
571void
572RotateBoxState::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
603const char*
604RotateBoxState::ActionName() const
605{
606	return B_TRANSLATE("Rotate");
607}
608
609
610// ActionNameIndex
611uint32
612RotateBoxState::ActionNameIndex() const
613{
614	return ROTATE;
615}
616
617
618// #pragma mark - OffsetCenterState
619
620
621// SetOrigin
622void
623OffsetCenterState::SetOrigin(BPoint origin)
624{
625	fParent->InverseTransform(&origin);
626	DragState::SetOrigin(origin);
627}
628
629
630// DragTo
631void
632OffsetCenterState::DragTo(BPoint current, uint32 modifiers)
633{
634	fParent->InverseTransform(&current);
635	fParent->OffsetCenter(current - fOrigin);
636	fOrigin = current;
637}
638
639
640// UpdateViewCursor
641void
642OffsetCenterState::UpdateViewCursor(BView* view, BPoint current) const
643{
644	_SetViewCursor(view, kPathMoveCursor);
645}
646
647
648// ActionName
649const char*
650OffsetCenterState::ActionName() const
651{
652	return B_TRANSLATE("Move Pivot");
653}
654
655
656// ActionNameIndex
657uint32
658OffsetCenterState::ActionNameIndex() const
659{
660	return MOVE_PIVOT;
661}
662