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