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