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