xref: /haiku/src/apps/icon-o-matic/transformable/TransformBoxStates.cpp (revision e1c4049fed1047bdb957b0529e1921e97ef94770)
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 
28 using namespace TransformBoxStates;
29 
30 
31 DragState::DragState(TransformBox* parent)
32 	:
33 	fOrigin(0.0, 0.0),
34 	fParent(parent)
35 {
36 }
37 
38 
39 const char*
40 DragState::ActionName() const
41 {
42 	return B_TRANSLATE("Transformation");
43 }
44 
45 
46 void
47 DragState::_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 
57 DragCornerState::DragCornerState(TransformBox* parent, uint32 corner)
58 	:
59 	DragState(parent),
60 	fCorner(corner)
61 {
62 }
63 
64 
65 void
66 DragCornerState::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 
125 void
126 DragCornerState::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 
167 void
168 DragCornerState::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 
269 const char*
270 DragCornerState::ActionName() const
271 {
272 	return B_TRANSLATE("Scale");
273 }
274 
275 
276 // #pragma mark - DragSideState
277 
278 
279 DragSideState::DragSideState(TransformBox* parent, uint32 side)
280 	:
281 	DragState(parent),
282 	fSide(side)
283 {
284 }
285 
286 
287 void
288 DragSideState::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 
335 void
336 DragSideState::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 
371 void
372 DragSideState::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 
423 const char*
424 DragSideState::ActionName() const
425 {
426 	return B_TRANSLATE("Scale");
427 }
428 
429 
430 // #pragma mark - DragBoxState
431 
432 
433 void
434 DragBoxState::SetOrigin(BPoint origin)
435 {
436 	fOldTranslation = fParent->Translation();
437 	DragState::SetOrigin(origin);
438 }
439 
440 
441 void
442 DragBoxState::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 
456 void
457 DragBoxState::UpdateViewCursor(BView* view, BPoint current) const
458 {
459 	_SetViewCursor(view, kMoveCursor);
460 }
461 
462 
463 const char*
464 DragBoxState::ActionName() const
465 {
466 	return B_TRANSLATE("Move");
467 }
468 
469 
470 // #pragma mark - RotateBoxState
471 
472 
473 RotateBoxState::RotateBoxState(TransformBox* parent)
474 	:
475 	DragState(parent),
476 	fOldAngle(0.0)
477 {
478 }
479 
480 
481 void
482 RotateBoxState::SetOrigin(BPoint origin)
483 {
484 	DragState::SetOrigin(origin);
485 	fOldAngle = fParent->LocalRotation();
486 }
487 
488 
489 void
490 RotateBoxState::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 
508 void
509 RotateBoxState::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 
539 const char*
540 RotateBoxState::ActionName() const
541 {
542 	return B_TRANSLATE("Rotate");
543 }
544 
545 
546 // #pragma mark - OffsetCenterState
547 
548 
549 void
550 OffsetCenterState::SetOrigin(BPoint origin)
551 {
552 	fParent->InverseTransform(&origin);
553 	DragState::SetOrigin(origin);
554 }
555 
556 
557 void
558 OffsetCenterState::DragTo(BPoint current, uint32 modifiers)
559 {
560 	fParent->InverseTransform(&current);
561 	fParent->OffsetCenter(current - fOrigin);
562 	fOrigin = current;
563 }
564 
565 
566 void
567 OffsetCenterState::UpdateViewCursor(BView* view, BPoint current) const
568 {
569 	_SetViewCursor(view, kPathMoveCursor);
570 }
571 
572 
573 const char*
574 OffsetCenterState::ActionName() const
575 {
576 	return B_TRANSLATE("Move pivot");
577 }
578