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