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