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