1 /* 2 Open Tracker License 3 4 Terms and Conditions 5 6 Copyright (c) 1991-2000, Be Incorporated. All rights reserved. 7 8 Permission is hereby granted, free of charge, to any person obtaining a copy of 9 this software and associated documentation files (the "Software"), to deal in 10 the Software without restriction, including without limitation the rights to 11 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 12 of the Software, and to permit persons to whom the Software is furnished to do 13 so, subject to the following conditions: 14 15 The above copyright notice and this permission notice applies to all licensees 16 and shall be included in all copies or substantial portions of the Software. 17 18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF TITLE, MERCHANTABILITY, 20 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 21 BE INCORPORATED BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN 22 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION 23 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 25 Except as contained in this notice, the name of Be Incorporated shall not be 26 used in advertising or otherwise to promote the sale, use or other dealings in 27 this Software without prior written authorization from Be Incorporated. 28 29 Tracker(TM), Be(R), BeOS(R), and BeIA(TM) are trademarks or registered trademarks 30 of Be Incorporated in the United States and other countries. Other brand product 31 names are registered trademarks or trademarks of their respective holders. 32 All rights reserved. 33 */ 34 35 // ListView title drawing and mouse manipulation classes 36 #include "TitleView.h" 37 38 #include <Alert.h> 39 #include <Application.h> 40 #include <ControlLook.h> 41 #include <Debug.h> 42 #include <PopUpMenu.h> 43 #include <Window.h> 44 45 #include <stdio.h> 46 #include <string.h> 47 48 #include "Commands.h" 49 #include "ContainerWindow.h" 50 #include "PoseView.h" 51 #include "Utilities.h" 52 53 #define APP_SERVER_CLEARS_BACKGROUND 1 54 55 static rgb_color sTitleBackground; 56 static rgb_color sDarkTitleBackground; 57 static rgb_color sShineColor; 58 static rgb_color sLightShadowColor; 59 static rgb_color sShadowColor; 60 static rgb_color sDarkShadowColor; 61 62 const rgb_color kHighlightColor = {100, 100, 210, 255}; 63 64 const unsigned char kHorizontalResizeCursor[] = { 65 16, 1, 7, 7, 66 0, 0, 1, 0, 1, 0, 1, 0, 9, 32, 25, 48, 57, 56, 121, 60, 67 57, 56, 25, 48, 9, 32, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 68 3, 128, 3, 128, 3, 128, 15, 224, 31, 240, 63, 248, 127, 252, 255, 254, 69 127, 252, 63, 248, 31, 240, 15, 224, 3, 128, 3, 128, 3, 128, 0, 0 70 }; 71 72 73 static void 74 _DrawLine(BPoseView *view, BPoint from, BPoint to) 75 { 76 rgb_color highColor = view->HighColor(); 77 view->SetHighColor(tint_color(view->LowColor(), B_DARKEN_1_TINT)); 78 view->StrokeLine(from, to); 79 view->SetHighColor(highColor); 80 } 81 82 83 static void 84 _UndrawLine(BPoseView *view, BPoint from, BPoint to) 85 { 86 view->StrokeLine(from, to, B_SOLID_LOW); 87 } 88 89 90 static void 91 _DrawOutline(BView *view, BRect where) 92 { 93 if (be_control_look != NULL) { 94 where.right++; 95 where.bottom--; 96 } else 97 where.InsetBy(1, 1); 98 rgb_color highColor = view->HighColor(); 99 view->SetHighColor(kHighlightColor); 100 view->StrokeRect(where); 101 view->SetHighColor(highColor); 102 } 103 104 105 // #pragma mark - 106 107 108 BTitleView::BTitleView(BRect frame, BPoseView *view) 109 : BView(frame, "TitleView", B_FOLLOW_LEFT_RIGHT, B_WILL_DRAW), 110 fPoseView(view), 111 fTitleList(10, true), 112 fHorizontalResizeCursor(kHorizontalResizeCursor), 113 fPreviouslyClickedColumnTitle(0), 114 fTrackingState(NULL) 115 { 116 sTitleBackground = tint_color(ui_color(B_PANEL_BACKGROUND_COLOR), 0.88f); // 216 -> 220 117 sDarkTitleBackground = tint_color(sTitleBackground, B_DARKEN_1_TINT); 118 sShineColor = tint_color(sTitleBackground, B_LIGHTEN_MAX_TINT); 119 sLightShadowColor = tint_color(sTitleBackground, B_DARKEN_2_TINT); 120 sShadowColor = tint_color(sTitleBackground, B_DARKEN_4_TINT); 121 sDarkShadowColor = tint_color(sShadowColor, B_DARKEN_2_TINT); 122 123 SetHighColor(sTitleBackground); 124 SetLowColor(sTitleBackground); 125 #if APP_SERVER_CLEARS_BACKGROUND 126 SetViewColor(sTitleBackground); 127 #else 128 SetViewColor(B_TRANSPARENT_COLOR); 129 #endif 130 131 BFont font(be_plain_font); 132 font.SetSize(9); 133 SetFont(&font); 134 135 Reset(); 136 } 137 138 139 BTitleView::~BTitleView() 140 { 141 delete fTrackingState; 142 } 143 144 145 void 146 BTitleView::Reset() 147 { 148 fTitleList.MakeEmpty(); 149 150 for (int32 index = 0; ; index++) { 151 BColumn *column = fPoseView->ColumnAt(index); 152 if (!column) 153 break; 154 fTitleList.AddItem(new BColumnTitle(this, column)); 155 } 156 } 157 158 159 void 160 BTitleView::AddTitle(BColumn *column, const BColumn *after) 161 { 162 int32 count = fTitleList.CountItems(); 163 int32 index; 164 if (after) { 165 for (index = 0; index < count; index++) { 166 BColumn *titleColumn = fTitleList.ItemAt(index)->Column(); 167 168 if (after == titleColumn) { 169 index++; 170 break; 171 } 172 } 173 } else 174 index = count; 175 176 fTitleList.AddItem(new BColumnTitle(this, column), index); 177 Invalidate(); 178 } 179 180 181 void 182 BTitleView::RemoveTitle(BColumn *column) 183 { 184 int32 count = fTitleList.CountItems(); 185 for (int32 index = 0; index < count; index++) { 186 BColumnTitle *title = fTitleList.ItemAt(index); 187 if (title->Column() == column) { 188 fTitleList.RemoveItem(title); 189 break; 190 } 191 } 192 193 Invalidate(); 194 } 195 196 197 void 198 BTitleView::Draw(BRect rect) 199 { 200 Draw(rect, false); 201 } 202 203 204 void 205 BTitleView::Draw(BRect /*updateRect*/, bool useOffscreen, bool updateOnly, 206 const BColumnTitle *pressedColumn, 207 void (*trackRectBlitter)(BView *, BRect), BRect passThru) 208 { 209 BRect bounds(Bounds()); 210 BView *view; 211 212 if (useOffscreen) { 213 ASSERT(sOffscreen); 214 BRect frame(bounds); 215 frame.right += frame.left; 216 // this is kind of messy way of avoiding being clipped by the ammount the 217 // title is scrolled to the left 218 // ToDo: fix this 219 view = sOffscreen->BeginUsing(frame); 220 view->SetOrigin(-bounds.left, 0); 221 view->SetLowColor(LowColor()); 222 view->SetHighColor(HighColor()); 223 BFont font(be_plain_font); 224 font.SetSize(9); 225 view->SetFont(&font); 226 } else 227 view = this; 228 229 if (be_control_look != NULL) { 230 rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR); 231 view->SetHighColor(tint_color(base, B_DARKEN_2_TINT)); 232 view->StrokeLine(bounds.LeftBottom(), bounds.RightBottom()); 233 bounds.bottom--; 234 235 be_control_look->DrawButtonBackground(view, bounds, bounds, base, 0, 236 BControlLook::B_TOP_BORDER | BControlLook::B_BOTTOM_BORDER); 237 } else { 238 // fill background with light gray background 239 if (!updateOnly) 240 view->FillRect(bounds, B_SOLID_LOW); 241 242 view->BeginLineArray(4); 243 view->AddLine(bounds.LeftTop(), bounds.RightTop(), sShadowColor); 244 view->AddLine(bounds.LeftBottom(), bounds.RightBottom(), sShadowColor); 245 // draw lighter gray and white inset lines 246 bounds.InsetBy(0, 1); 247 view->AddLine(bounds.LeftBottom(), bounds.RightBottom(), sLightShadowColor); 248 view->AddLine(bounds.LeftTop(), bounds.RightTop(), sShineColor); 249 view->EndLineArray(); 250 } 251 252 int32 count = fTitleList.CountItems(); 253 float minx = bounds.right; 254 float maxx = bounds.left; 255 for (int32 index = 0; index < count; index++) { 256 BColumnTitle *title = fTitleList.ItemAt(index); 257 title->Draw(view, title == pressedColumn); 258 BRect titleBounds(title->Bounds()); 259 if (titleBounds.left < minx) 260 minx = titleBounds.left; 261 if (titleBounds.right > maxx) 262 maxx = titleBounds.right; 263 } 264 265 if (be_control_look != NULL) { 266 bounds = Bounds(); 267 minx--; 268 view->SetHighColor(sLightShadowColor); 269 view->StrokeLine(BPoint(minx, bounds.top), BPoint(minx, bounds.bottom - 1)); 270 } else { 271 // first and last shades before and after first column 272 maxx++; 273 minx--; 274 view->BeginLineArray(2); 275 view->AddLine(BPoint(minx, bounds.top), 276 BPoint(minx, bounds.bottom), sShadowColor); 277 view->AddLine(BPoint(maxx, bounds.top), 278 BPoint(maxx, bounds.bottom), sShineColor); 279 view->EndLineArray(); 280 } 281 282 #if !(APP_SERVER_CLEARS_BACKGROUND) 283 FillRect(BRect(bounds.left, bounds.top + 1, minx - 1, bounds.bottom - 1), B_SOLID_LOW); 284 FillRect(BRect(maxx + 1, bounds.top + 1, bounds.right, bounds.bottom - 1), B_SOLID_LOW); 285 #endif 286 287 if (useOffscreen) { 288 if (trackRectBlitter) 289 (trackRectBlitter)(view, passThru); 290 view->Sync(); 291 DrawBitmap(sOffscreen->Bitmap()); 292 sOffscreen->DoneUsing(); 293 } else if (trackRectBlitter) 294 (trackRectBlitter)(view, passThru); 295 } 296 297 298 void 299 BTitleView::MouseDown(BPoint where) 300 { 301 if (!Window()->IsActive()) { 302 // wasn't active, just activate and bail 303 Window()->Activate(); 304 return; 305 } 306 307 // finish any pending edits 308 fPoseView->CommitActivePose(); 309 310 BColumnTitle *title = FindColumnTitle(where); 311 BColumnTitle *resizedTitle = InColumnResizeArea(where); 312 313 uint32 buttons; 314 GetMouse(&where, &buttons); 315 316 // Check if the user clicked the secondary mouse button. 317 // if so, display the attribute menu: 318 319 if (buttons & B_SECONDARY_MOUSE_BUTTON) { 320 BContainerWindow *window = dynamic_cast<BContainerWindow *> 321 (Window()); 322 BPopUpMenu *menu = new BPopUpMenu("Attributes", false, false); 323 menu->SetFont(be_plain_font); 324 window->NewAttributeMenu(menu); 325 window->AddMimeTypesToMenu(menu); 326 window->MarkAttributeMenu(menu); 327 menu->SetTargetForItems(window->PoseView()); 328 menu->Go(ConvertToScreen(where), true, false); 329 return; 330 } 331 332 bigtime_t doubleClickSpeed; 333 get_click_speed(&doubleClickSpeed); 334 335 if (resizedTitle) { 336 bool force = static_cast<bool>(buttons & B_TERTIARY_MOUSE_BUTTON); 337 if (force || buttons & B_PRIMARY_MOUSE_BUTTON) { 338 if (force || fPreviouslyClickedColumnTitle != 0) { 339 if (force || system_time() - fPreviousLeftClickTime < doubleClickSpeed) { 340 if (fPoseView->ResizeColumnToWidest(resizedTitle->Column())) { 341 Invalidate(); 342 return; 343 } 344 } 345 } 346 fPreviousLeftClickTime = system_time(); 347 fPreviouslyClickedColumnTitle = resizedTitle; 348 } 349 } else if (!title) 350 return; 351 352 SetMouseEventMask(B_POINTER_EVENTS, B_NO_POINTER_HISTORY | B_LOCK_WINDOW_FOCUS); 353 354 // track the mouse 355 if (resizedTitle) { 356 fTrackingState = new ColumnResizeState(this, resizedTitle, where, 357 system_time() + doubleClickSpeed); 358 } else { 359 fTrackingState = new ColumnDragState(this, title, where, 360 system_time() + doubleClickSpeed); 361 } 362 } 363 364 365 void 366 BTitleView::MouseUp(BPoint where) 367 { 368 if (fTrackingState == NULL) 369 return; 370 371 fTrackingState->MouseUp(where); 372 373 delete fTrackingState; 374 fTrackingState = NULL; 375 } 376 377 378 void 379 BTitleView::MouseMoved(BPoint where, uint32 code, const BMessage *message) 380 { 381 if (fTrackingState != NULL) { 382 int32 buttons = 0; 383 if (Looper() != NULL && Looper()->CurrentMessage() != NULL) 384 Looper()->CurrentMessage()->FindInt32("buttons", &buttons); 385 fTrackingState->MouseMoved(where, buttons); 386 return; 387 } 388 389 switch (code) { 390 default: 391 if (InColumnResizeArea(where) && Window()->IsActive()) 392 SetViewCursor(&fHorizontalResizeCursor); 393 else 394 SetViewCursor(B_CURSOR_SYSTEM_DEFAULT); 395 break; 396 397 case B_EXITED_VIEW: 398 SetViewCursor(B_CURSOR_SYSTEM_DEFAULT); 399 break; 400 } 401 _inherited::MouseMoved(where, code, message); 402 } 403 404 405 BColumnTitle * 406 BTitleView::InColumnResizeArea(BPoint where) const 407 { 408 int32 count = fTitleList.CountItems(); 409 for (int32 index = 0; index < count; index++) { 410 BColumnTitle *title = fTitleList.ItemAt(index); 411 if (title->InColumnResizeArea(where)) 412 return title; 413 } 414 415 return NULL; 416 } 417 418 419 BColumnTitle * 420 BTitleView::FindColumnTitle(BPoint where) const 421 { 422 int32 count = fTitleList.CountItems(); 423 for (int32 index = 0; index < count; index++) { 424 BColumnTitle *title = fTitleList.ItemAt(index); 425 if (title->Bounds().Contains(where)) 426 return title; 427 } 428 429 return NULL; 430 } 431 432 433 BColumnTitle * 434 BTitleView::FindColumnTitle(const BColumn *column) const 435 { 436 int32 count = fTitleList.CountItems(); 437 for (int32 index = 0; index < count; index++) { 438 BColumnTitle *title = fTitleList.ItemAt(index); 439 if (title->Column() == column) 440 return title; 441 } 442 443 return NULL; 444 } 445 446 447 // #pragma mark - 448 449 450 BColumnTitle::BColumnTitle(BTitleView *view, BColumn *column) 451 : 452 fColumn(column), 453 fParent(view) 454 { 455 } 456 457 458 bool 459 BColumnTitle::InColumnResizeArea(BPoint where) const 460 { 461 BRect edge(Bounds()); 462 edge.left = edge.right - kEdgeSize; 463 edge.right += kEdgeSize; 464 465 return edge.Contains(where); 466 } 467 468 469 BRect 470 BColumnTitle::Bounds() const 471 { 472 BRect bounds(fColumn->Offset() - kTitleColumnLeftExtraMargin, 0, 0, kTitleViewHeight); 473 bounds.right = bounds.left + fColumn->Width() + kTitleColumnExtraMargin; 474 475 return bounds; 476 } 477 478 479 void 480 BColumnTitle::Draw(BView *view, bool pressed) 481 { 482 BRect bounds(Bounds()); 483 BPoint loc(0, bounds.bottom - 4); 484 485 if (pressed) { 486 if (be_control_look != NULL) { 487 bounds.bottom--; 488 BRect rect(bounds); 489 rect.right--; 490 rgb_color base = tint_color(ui_color(B_PANEL_BACKGROUND_COLOR), 491 B_DARKEN_1_TINT); 492 493 be_control_look->DrawButtonBackground(view, rect, rect, base, 0, 494 BControlLook::B_TOP_BORDER | BControlLook::B_BOTTOM_BORDER); 495 } else { 496 view->SetLowColor(pressed ? sDarkTitleBackground : sTitleBackground); 497 view->FillRect(bounds, B_SOLID_LOW); 498 } 499 } 500 501 BString titleString(fColumn->Title()); 502 view->TruncateString(&titleString, B_TRUNCATE_END, 503 bounds.Width() - kTitleColumnExtraMargin); 504 float resultingWidth = view->StringWidth(titleString.String()); 505 506 switch (fColumn->Alignment()) { 507 case B_ALIGN_LEFT: 508 default: 509 loc.x = bounds.left + 1 + kTitleColumnLeftExtraMargin; 510 break; 511 512 case B_ALIGN_CENTER: 513 loc.x = bounds.left + (bounds.Width() / 2) - (resultingWidth / 2); 514 break; 515 516 case B_ALIGN_RIGHT: 517 loc.x = bounds.right - resultingWidth - kTitleColumnRightExtraMargin; 518 break; 519 } 520 521 view->SetHighColor(tint_color(ui_color(B_PANEL_BACKGROUND_COLOR), 1.75)); 522 view->DrawString(titleString.String(), loc); 523 524 // show sort columns 525 bool secondary = (fColumn->AttrHash() == fParent->PoseView()->SecondarySort()); 526 if (secondary || (fColumn->AttrHash() == fParent->PoseView()->PrimarySort())) { 527 528 BPoint center(loc.x - 6, roundf((bounds.top + bounds.bottom) / 2.0)); 529 BPoint triangle[3]; 530 if (fParent->PoseView()->ReverseSort()) { 531 triangle[0] = center + BPoint(-3.5, 1.5); 532 triangle[1] = center + BPoint(3.5, 1.5); 533 triangle[2] = center + BPoint(0.0, -2.0); 534 } else { 535 triangle[0] = center + BPoint(-3.5, -1.5); 536 triangle[1] = center + BPoint(3.5, -1.5); 537 triangle[2] = center + BPoint(0.0, 2.0); 538 } 539 540 uint32 flags = view->Flags(); 541 view->SetFlags(flags | B_SUBPIXEL_PRECISE); 542 543 if (secondary) { 544 view->SetHighColor(tint_color(ui_color(B_PANEL_BACKGROUND_COLOR), 1.3)); 545 view->FillTriangle(triangle[0], triangle[1], triangle[2]); 546 } else { 547 view->SetHighColor(tint_color(ui_color(B_PANEL_BACKGROUND_COLOR), 1.6)); 548 view->FillTriangle(triangle[0], triangle[1], triangle[2]); 549 } 550 551 view->SetFlags(flags); 552 } 553 554 if (be_control_look != NULL) { 555 view->SetHighColor(sLightShadowColor); 556 view->StrokeLine(bounds.RightTop(), bounds.RightBottom()); 557 } else { 558 BRect rect(bounds); 559 560 view->SetHighColor(sShadowColor); 561 view->StrokeRect(rect); 562 563 view->BeginLineArray(4); 564 // draw lighter gray and white inset lines 565 rect.InsetBy(1, 1); 566 view->AddLine(rect.LeftBottom(), rect.RightBottom(), 567 pressed ? sLightShadowColor : sLightShadowColor); 568 view->AddLine(rect.LeftTop(), rect.RightTop(), 569 pressed ? sDarkShadowColor : sShineColor); 570 571 view->AddLine(rect.LeftTop(), rect.LeftBottom(), 572 pressed ? sDarkShadowColor : sShineColor); 573 view->AddLine(rect.RightTop(), rect.RightBottom(), 574 pressed ? sLightShadowColor : sLightShadowColor); 575 576 view->EndLineArray(); 577 } 578 } 579 580 581 // #pragma mark - 582 583 584 ColumnTrackState::ColumnTrackState(BTitleView *view, BColumnTitle *title, 585 BPoint where, bigtime_t pastClickTime) 586 : 587 fTitleView(view), 588 fTitle(title), 589 fFirstClickPoint(where), 590 fPastClickTime(pastClickTime), 591 fHasMoved(false) 592 { 593 } 594 595 596 void 597 ColumnTrackState::MouseUp(BPoint where) 598 { 599 // if it is pressed shortly and not moved, it is a click 600 // all else is a track 601 if (system_time() <= fPastClickTime && !fHasMoved) 602 Clicked(where); 603 else 604 Done(where); 605 } 606 607 608 void 609 ColumnTrackState::MouseMoved(BPoint where, uint32 buttons) 610 { 611 if (!fHasMoved && system_time() < fPastClickTime) { 612 BRect moveMargin(fFirstClickPoint, fFirstClickPoint); 613 moveMargin.InsetBy(-1, -1); 614 615 if (moveMargin.Contains(where)) 616 return; 617 } 618 619 Moved(where, buttons); 620 fHasMoved = true; 621 } 622 623 624 // #pragma mark - 625 626 627 ColumnResizeState::ColumnResizeState(BTitleView *view, BColumnTitle *title, 628 BPoint where, bigtime_t pastClickTime) 629 : ColumnTrackState(view, title, where, pastClickTime), 630 fLastLineDrawPos(-1), 631 fInitialTrackOffset((title->fColumn->Offset() + title->fColumn->Width()) - where.x) 632 { 633 DrawLine(); 634 } 635 636 637 bool 638 ColumnResizeState::ValueChanged(BPoint where) 639 { 640 float newWidth = where.x + fInitialTrackOffset - fTitle->fColumn->Offset(); 641 if (newWidth < kMinColumnWidth) 642 newWidth = kMinColumnWidth; 643 644 return newWidth != fTitle->fColumn->Width(); 645 } 646 647 648 void 649 ColumnResizeState::Moved(BPoint where, uint32) 650 { 651 float newWidth = where.x + fInitialTrackOffset - fTitle->fColumn->Offset(); 652 if (newWidth < kMinColumnWidth) 653 newWidth = kMinColumnWidth; 654 655 BPoseView *poseView = fTitleView->PoseView(); 656 657 // bool shrink = (newWidth < fTitle->fColumn->Width()); 658 659 // resize the column 660 poseView->ResizeColumn(fTitle->fColumn, newWidth, &fLastLineDrawPos, 661 _DrawLine, _UndrawLine); 662 663 BRect bounds(fTitleView->Bounds()); 664 bounds.left = fTitle->fColumn->Offset(); 665 666 // force title redraw 667 fTitleView->Draw(bounds, true, false); 668 } 669 670 671 void 672 ColumnResizeState::Done(BPoint /*where*/) 673 { 674 UndrawLine(); 675 } 676 677 678 void 679 ColumnResizeState::Clicked(BPoint /*where*/) 680 { 681 UndrawLine(); 682 } 683 684 685 void 686 ColumnResizeState::DrawLine() 687 { 688 BPoseView *poseView = fTitleView->PoseView(); 689 ASSERT(!poseView->IsDesktopWindow()); 690 691 BRect poseViewBounds(poseView->Bounds()); 692 // remember the line location 693 poseViewBounds.left = fTitle->Bounds().right; 694 fLastLineDrawPos = poseViewBounds.left; 695 696 // draw the line in the new location 697 _DrawLine(poseView, poseViewBounds.LeftTop(), poseViewBounds.LeftBottom()); 698 } 699 700 701 void 702 ColumnResizeState::UndrawLine() 703 { 704 if (fLastLineDrawPos < 0) 705 return; 706 707 BRect poseViewBounds(fTitleView->PoseView()->Bounds()); 708 poseViewBounds.left = fLastLineDrawPos; 709 710 _UndrawLine(fTitleView->PoseView(), poseViewBounds.LeftTop(), 711 poseViewBounds.LeftBottom()); 712 } 713 714 715 // #pragma mark - 716 717 718 ColumnDragState::ColumnDragState(BTitleView *view, BColumnTitle *columnTitle, 719 BPoint where, bigtime_t pastClickTime) 720 : ColumnTrackState(view, columnTitle, where, pastClickTime), 721 fInitialMouseTrackOffset(where.x), 722 fTrackingRemovedColumn(false) 723 { 724 ASSERT(columnTitle); 725 ASSERT(fTitle); 726 ASSERT(fTitle->Column()); 727 DrawPressNoOutline(); 728 } 729 730 731 // ToDo: 732 // Autoscroll when dragging column left/right 733 // fix dragging back a column before the first column (now adds as last) 734 // make column swaps/adds not invalidate/redraw columns to the left 735 void 736 ColumnDragState::Moved(BPoint where, uint32) 737 { 738 // figure out where we are with the mouse 739 BRect titleBounds(fTitleView->Bounds()); 740 bool overTitleView = titleBounds.Contains(where); 741 BColumnTitle *overTitle = overTitleView 742 ? fTitleView->FindColumnTitle(where) : 0; 743 BRect titleBoundsWithMargin(titleBounds); 744 titleBoundsWithMargin.InsetBy(0, -kRemoveTitleMargin); 745 bool inMarginRect = overTitleView || titleBoundsWithMargin.Contains(where); 746 747 bool drawOutline = false; 748 bool undrawOutline = false; 749 750 if (fTrackingRemovedColumn) { 751 if (overTitleView) { 752 // tracked back with a removed title into the title bar, add it 753 // back 754 fTitleView->EndRectTracking(); 755 fColumnArchive.Seek(0, SEEK_SET); 756 BColumn *column = BColumn::InstantiateFromStream(&fColumnArchive); 757 ASSERT(column); 758 const BColumn *after = NULL; 759 if (overTitle) 760 after = overTitle->Column(); 761 fTitleView->PoseView()->AddColumn(column, after); 762 fTrackingRemovedColumn = false; 763 fTitle = fTitleView->FindColumnTitle(column); 764 fInitialMouseTrackOffset += fTitle->Bounds().left; 765 drawOutline = true; 766 } 767 } else { 768 if (!inMarginRect) { 769 // dragged a title out of the hysteresis margin around the 770 // title bar - remove it and start dragging it as a dotted outline 771 772 BRect rect(fTitle->Bounds()); 773 rect.OffsetBy(where.x - fInitialMouseTrackOffset, where.y - 5); 774 fColumnArchive.Seek(0, SEEK_SET); 775 fTitle->Column()->ArchiveToStream(&fColumnArchive); 776 fInitialMouseTrackOffset -= fTitle->Bounds().left; 777 if (fTitleView->PoseView()->RemoveColumn(fTitle->Column(), false)) { 778 fTitle = 0; 779 fTitleView->BeginRectTracking(rect); 780 fTrackingRemovedColumn = true; 781 undrawOutline = true; 782 } 783 } else if (overTitle && overTitle != fTitle 784 // over a different column 785 && (overTitle->Bounds().left >= fTitle->Bounds().right 786 // over the one to the right 787 || where.x < overTitle->Bounds().left + fTitle->Bounds().Width())){ 788 // over the one to the left, far enough to not snap right back 789 790 BColumn *column = fTitle->Column(); 791 fInitialMouseTrackOffset -= fTitle->Bounds().left; 792 // swap the columns 793 fTitleView->PoseView()->MoveColumnTo(column, overTitle->Column()); 794 // re-grab the title object looking it up by the column 795 fTitle = fTitleView->FindColumnTitle(column); 796 // recalc initialMouseTrackOffset 797 fInitialMouseTrackOffset += fTitle->Bounds().left; 798 drawOutline = true; 799 } else 800 drawOutline = true; 801 } 802 803 if (drawOutline) 804 DrawOutline(where.x - fInitialMouseTrackOffset); 805 else if (undrawOutline) 806 UndrawOutline(); 807 } 808 809 810 void 811 ColumnDragState::Done(BPoint /*where*/) 812 { 813 if (fTrackingRemovedColumn) 814 fTitleView->EndRectTracking(); 815 UndrawOutline(); 816 } 817 818 819 void 820 ColumnDragState::Clicked(BPoint /*where*/) 821 { 822 BPoseView *poseView = fTitleView->PoseView(); 823 uint32 hash = fTitle->Column()->AttrHash(); 824 uint32 primarySort = poseView->PrimarySort(); 825 uint32 secondarySort = poseView->SecondarySort(); 826 bool shift = (modifiers() & B_SHIFT_KEY) != 0; 827 828 // For now: 829 // if we hit the primary sort field again 830 // then if shift key was down, switch primary and secondary 831 if (hash == primarySort) { 832 if (shift && secondarySort) { 833 poseView->SetPrimarySort(secondarySort); 834 poseView->SetSecondarySort(primarySort); 835 } else 836 poseView->SetReverseSort(!poseView->ReverseSort()); 837 } else if (shift) { 838 // hit secondary sort column with shift key, disable 839 if (hash == secondarySort) 840 poseView->SetSecondarySort(0); 841 else 842 poseView->SetSecondarySort(hash); 843 } else { 844 poseView->SetPrimarySort(hash); 845 poseView->SetReverseSort(false); 846 } 847 848 if (poseView->PrimarySort() == poseView->SecondarySort()) 849 poseView->SetSecondarySort(0); 850 851 UndrawOutline(); 852 853 poseView->SortPoses(); 854 poseView->Invalidate(); 855 } 856 857 858 bool 859 ColumnDragState::ValueChanged(BPoint) 860 { 861 return true; 862 } 863 864 865 void 866 ColumnDragState::DrawPressNoOutline() 867 { 868 fTitleView->Draw(fTitleView->Bounds(), true, false, fTitle); 869 } 870 871 872 void 873 ColumnDragState::DrawOutline(float pos) 874 { 875 BRect outline(fTitle->Bounds()); 876 outline.OffsetBy(pos, 0); 877 fTitleView->Draw(fTitleView->Bounds(), true, false, fTitle, _DrawOutline, outline); 878 } 879 880 881 void 882 ColumnDragState::UndrawOutline() 883 { 884 fTitleView->Draw(fTitleView->Bounds(), true, false); 885 } 886 887 888 OffscreenBitmap *BTitleView::sOffscreen = new OffscreenBitmap; 889