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