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