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(¤t); 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