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