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