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