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