6 Copyright (c) 1991-2000, Be Incorporated. All rights reserved.
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:
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.
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.
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.
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.
35 /*******************************************************************************
37 / File: ColumnListView.cpp
39 / Description: Experimental multi-column list view.
41 / Copyright 2000+, Be Incorporated, All Rights Reserved
44 *******************************************************************************/
46 #include "ColumnListView.h"
54 #include <Application.h>
56 #include <ControlLook.h>
59 #include <GraphicsDefs.h>
60 #include <LayoutUtils.h>
62 #include <PopUpMenu.h>
64 #include <ScrollBar.h>
66 #include <SupportDefs.h>
69 #include <ObjectListPrivate.h>
71 #include "ColorTools.h"
72 #include "ObjectList.h"
75 #define DOUBLE_BUFFERED_COLUMN_RESIZE 1
76 #define SMART_REDRAW 1
77 #define DRAG_TITLE_OUTLINE 1
78 #define CONSTRAIN_CLIPPING_REGION 1
79 #define LOWER_SCROLLBAR 0
84 static const unsigned char kDownSortArrow8x8
[] = {
85 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff,
86 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff,
87 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff,
88 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff,
89 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
90 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff,
91 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff,
92 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff
95 static const unsigned char kUpSortArrow8x8
[] = {
96 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff,
97 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff,
98 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff,
99 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
100 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff,
101 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff,
102 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff,
103 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff
106 static const unsigned char kDownSortArrow8x8Invert
[] = {
107 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
108 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0xff,
109 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
110 0xff, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0xff, 0xff,
111 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
112 0xff, 0xff, 0x1f, 0x1f, 0x1f, 0xff, 0xff, 0xff,
113 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
114 0xff, 0xff, 0xff, 0x1f, 0xff, 0xff, 0xff, 0xff
117 static const unsigned char kUpSortArrow8x8Invert
[] = {
118 0xff, 0xff, 0xff, 0x1f, 0xff, 0xff, 0xff, 0xff,
119 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
120 0xff, 0xff, 0x1f, 0x1f, 0x1f, 0xff, 0xff, 0xff,
121 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
122 0xff, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0xff, 0xff,
123 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
124 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0xff,
125 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
128 static const float kTintedLineTint
= 1.04;
130 static const float kMinTitleHeight
= 16.0;
131 static const float kMinRowHeight
= 16.0;
132 static const float kTitleSpacing
= 1.4;
133 static const float kRowSpacing
= 1.4;
134 static const float kLatchWidth
= 15.0;
136 static const int32 kMaxDepth
= 1024;
137 static const float kLeftMargin
= kLatchWidth
;
138 static const float kRightMargin
= 8;
139 static const float kOutlineLevelIndent
= kLatchWidth
;
140 static const float kColumnResizeAreaWidth
= 10.0;
141 static const float kRowDragSensitivity
= 5.0;
142 static const float kDoubleClickMoveSensitivity
= 4.0;
143 static const float kSortIndicatorWidth
= 9.0;
144 static const float kDropHighlightLineHeight
= 2.0;
146 static const uint32 kToggleColumn
= 'BTCL';
148 class BRowContainer
: public BObjectList
<BRow
>
152 class TitleView
: public BView
{
153 typedef BView _inherited
;
155 TitleView(BRect frame
, OutlineView
* outlineView
,
156 BList
* visibleColumns
, BList
* sortColumns
,
157 BColumnListView
* masterView
,
158 uint32 resizingMode
);
159 virtual ~TitleView();
161 void ColumnAdded(BColumn
* column
);
162 void ColumnResized(BColumn
* column
, float oldWidth
);
163 void SetColumnVisible(BColumn
* column
, bool visible
);
165 virtual void Draw(BRect updateRect
);
166 virtual void ScrollTo(BPoint where
);
167 virtual void MessageReceived(BMessage
* message
);
168 virtual void MouseDown(BPoint where
);
169 virtual void MouseMoved(BPoint where
, uint32 transit
,
170 const BMessage
* dragMessage
);
171 virtual void MouseUp(BPoint where
);
172 virtual void FrameResized(float width
, float height
);
174 void MoveColumn(BColumn
* column
, int32 index
);
175 void SetColumnFlags(column_flags flags
);
177 void SetEditMode(bool state
)
178 { fEditMode
= state
; }
180 float MarginWidth() const;
183 void GetTitleRect(BColumn
* column
, BRect
* _rect
);
184 int32
FindColumn(BPoint where
, float* _leftEdge
);
185 void FixScrollBar(bool scrollToFit
);
186 void DragSelectedColumn(BPoint where
);
187 void ResizeSelectedColumn(BPoint where
,
188 bool preferred
= false);
189 void ComputeDragBoundries(BColumn
* column
,
191 void DrawTitle(BView
* view
, BRect frame
,
192 BColumn
* column
, bool depressed
);
194 float _VirtualWidth() const;
196 OutlineView
* fOutlineView
;
199 // float fColumnsWidth;
202 #if DOUBLE_BUFFERED_COLUMN_RESIZE
203 BBitmap
* fDrawBuffer
;
204 BView
* fDrawBufferView
;
211 DRAG_COLUMN_INSIDE_TITLE
,
212 DRAG_COLUMN_OUTSIDE_TITLE
215 BPopUpMenu
* fColumnPop
;
216 BColumnListView
* fMasterView
;
220 // State information for resizing/dragging
221 BColumn
* fSelectedColumn
;
222 BRect fSelectedColumnRect
;
223 bool fResizingFirstColumn
;
224 BPoint fClickPoint
; // offset within cell
225 float fLeftDragBoundry
;
226 float fRightDragBoundry
;
227 BPoint fCurrentDragPosition
;
230 BBitmap
* fUpSortArrow
;
231 BBitmap
* fDownSortArrow
;
233 BCursor
* fResizeCursor
;
234 BCursor
* fMinResizeCursor
;
235 BCursor
* fMaxResizeCursor
;
236 BCursor
* fColumnMoveCursor
;
240 class OutlineView
: public BView
{
241 typedef BView _inherited
;
243 OutlineView(BRect
, BList
* visibleColumns
,
245 BColumnListView
* listView
);
246 virtual ~OutlineView();
248 virtual void Draw(BRect
);
249 const BRect
& VisibleRect() const;
251 void RedrawColumn(BColumn
* column
, float leftEdge
,
254 float GetColumnPreferredWidth(BColumn
* column
);
256 void AddRow(BRow
*, int32 index
, BRow
* TheRow
);
257 BRow
* CurrentSelection(BRow
* lastSelected
) const;
258 void ToggleFocusRowSelection(bool selectRange
);
259 void ToggleFocusRowOpen();
260 void ChangeFocusRow(bool up
, bool updateSelection
,
261 bool addToCurrentSelection
);
262 void MoveFocusToVisibleRect();
263 void ExpandOrCollapse(BRow
* parent
, bool expand
);
264 void RemoveRow(BRow
*);
265 BRowContainer
* RowList();
266 void UpdateRow(BRow
*);
267 bool FindParent(BRow
* row
, BRow
** _parent
,
269 int32
IndexOf(BRow
* row
);
270 void Deselect(BRow
*);
271 void AddToSelection(BRow
*);
273 BRow
* FocusRow() const;
274 void SetFocusRow(BRow
* row
, bool select
);
275 BRow
* FindRow(float ypos
, int32
* _indent
,
277 bool FindRect(const BRow
* row
, BRect
* _rect
);
278 void ScrollTo(const BRow
* row
);
281 void SetSelectionMode(list_view_type type
);
282 list_view_type
SelectionMode() const;
283 void SetMouseTrackingEnabled(bool);
284 void FixScrollBar(bool scrollToFit
);
285 void SetEditMode(bool state
)
286 { fEditMode
= state
; }
288 virtual void FrameResized(float width
, float height
);
289 virtual void ScrollTo(BPoint where
);
290 virtual void MouseDown(BPoint where
);
291 virtual void MouseMoved(BPoint where
, uint32 transit
,
292 const BMessage
* dragMessage
);
293 virtual void MouseUp(BPoint where
);
294 virtual void MessageReceived(BMessage
* message
);
297 bool SortList(BRowContainer
* list
, bool isVisible
);
298 static int32
DeepSortThreadEntry(void* outlineView
);
300 void SelectRange(BRow
* start
, BRow
* end
);
301 int32
CompareRows(BRow
* row1
, BRow
* row2
);
302 void AddSorted(BRowContainer
* list
, BRow
* row
);
303 void RecursiveDeleteRows(BRowContainer
* list
,
305 void InvalidateCachedPositions();
306 bool FindVisibleRect(BRow
* row
, BRect
* _rect
);
314 #if DOUBLE_BUFFERED_COLUMN_RESIZE
315 BBitmap
* fDrawBuffer
;
316 BView
* fDrawBufferView
;
323 BRow fSelectionListDummyHead
;
324 BRow
* fLastSelectedItem
;
325 BRow
* fFirstSelectedItem
;
327 thread_id fSortThread
;
338 CurrentState fCurrentState
;
341 BColumnListView
* fMasterView
;
342 list_view_type fSelectionMode
;
344 BField
* fCurrentField
;
346 BColumn
* fCurrentColumn
;
352 // State information for mouse/keyboard interaction
359 float fDropHighlightY
;
361 friend class RecursiveOutlineIterator
;
365 class RecursiveOutlineIterator
{
367 RecursiveOutlineIterator(
368 BRowContainer
* container
,
369 bool openBranchesOnly
= true);
371 BRow
* CurrentRow() const;
372 int32
CurrentLevel() const;
377 BRowContainer
* fRowSet
;
383 BRowContainer
* fCurrentList
;
384 int32 fCurrentListIndex
;
385 int32 fCurrentListDepth
;
386 bool fOpenBranchesOnly
;
389 } // namespace BPrivate
392 using namespace BPrivate
;
409 BColumn::MouseMoved(BColumnListView
* /*parent*/, BRow
* /*row*/,
410 BField
* /*field*/, BRect
/*field_rect*/, BPoint
/*point*/,
411 uint32
/*buttons*/, int32
/*code*/)
417 BColumn::MouseDown(BColumnListView
* /*parent*/, BRow
* /*row*/,
418 BField
* /*field*/, BRect
/*field_rect*/, BPoint
/*point*/,
425 BColumn::MouseUp(BColumnListView
* /*parent*/, BRow
* /*row*/, BField
* /*field*/)
437 fHeight(std::max(kMinRowHeight
,
438 ceilf(be_plain_font
->Size() * kRowSpacing
))),
447 BRow::BRow(float height
)
463 BField
* field
= (BField
*) fFields
.RemoveItem((int32
)0);
473 BRow::HasLatch() const
475 return fChildList
!= 0;
480 BRow::CountFields() const
482 return fFields
.CountItems();
487 BRow::GetField(int32 index
)
489 return (BField
*)fFields
.ItemAt(index
);
494 BRow::GetField(int32 index
) const
496 return (const BField
*)fFields
.ItemAt(index
);
501 BRow::SetField(BField
* field
, int32 logicalFieldIndex
)
503 if (fFields
.ItemAt(logicalFieldIndex
) != 0)
504 delete (BField
*)fFields
.RemoveItem(logicalFieldIndex
);
507 ValidateField(field
, logicalFieldIndex
);
511 fFields
.AddItem(field
, logicalFieldIndex
);
523 BRow::IsExpanded() const
530 BRow::IsSelected() const
532 return fPrevSelected
!= NULL
;
540 fList
->InvalidateRow(this);
545 BRow::ValidateFields() const
547 for (int32 i
= 0; i
< CountFields(); i
++)
548 ValidateField(GetField(i
), i
);
553 BRow::ValidateField(const BField
* field
, int32 logicalFieldIndex
) const
555 // The Fields may be moved by the user, but the logicalFieldIndexes
556 // do not change, so we need to map them over when checking the
558 BColumn
* column
= NULL
;
559 int32 items
= fList
->CountColumns();
560 for (int32 i
= 0 ; i
< items
; ++i
) {
561 column
= fList
->ColumnAt(i
);
562 if(column
->LogicalFieldNum() == logicalFieldIndex
)
566 if (column
== NULL
) {
567 BString
dbmessage("\n\n\tThe parent BColumnListView does not have "
568 "\n\ta BColumn at the logical field index ");
569 dbmessage
<< logicalFieldIndex
<< ".\n\n";
570 printf(dbmessage
.String());
572 if (!column
->AcceptsField(field
)) {
573 BString
dbmessage("\n\n\tThe BColumn of type ");
574 dbmessage
<< typeid(*column
).name() << "\n\tat logical field index "
575 << logicalFieldIndex
<< "\n\tdoes not support the field type "
576 << typeid(*field
).name() << ".\n\n";
577 debugger(dbmessage
.String());
586 BColumn::BColumn(float width
, float minWidth
, float maxWidth
, alignment align
)
605 BColumn::Width() const
612 BColumn::SetWidth(float width
)
619 BColumn::MinWidth() const
626 BColumn::MaxWidth() const
633 BColumn::DrawTitle(BRect
, BView
*)
639 BColumn::DrawField(BField
*, BRect
, BView
*)
645 BColumn::CompareFields(BField
*, BField
*)
652 BColumn::GetColumnName(BString
* into
) const
659 BColumn::GetPreferredWidth(BField
* field
, BView
* parent
) const
666 BColumn::IsVisible() const
673 BColumn::SetVisible(bool visible
)
675 if (fList
&& (fVisible
!= visible
))
676 fList
->SetColumnVisible(this, visible
);
681 BColumn::ShowHeading() const
688 BColumn::SetShowHeading(bool state
)
690 fShowHeading
= state
;
695 BColumn::Alignment() const
702 BColumn::SetAlignment(alignment align
)
709 BColumn::WantsEvents() const
716 BColumn::SetWantsEvents(bool state
)
718 fWantsEvents
= state
;
723 BColumn::LogicalFieldNum() const
730 BColumn::AcceptsField(const BField
*) const
739 BColumnListView::BColumnListView(BRect rect
, const char* name
,
740 uint32 resizingMode
, uint32 flags
, border_style border
,
741 bool showHorizontalScrollbar
)
743 BView(rect
, name
, resizingMode
,
744 flags
| B_WILL_DRAW
| B_FRAME_EVENTS
| B_FULL_UPDATE_ON_RESIZE
),
746 fSelectionMessage(NULL
),
747 fSortingEnabled(true),
748 fLatchWidth(kLatchWidth
),
749 fBorderStyle(border
),
750 fShowingHorizontalScrollBar(showHorizontalScrollbar
)
756 BColumnListView::BColumnListView(const char* name
, uint32 flags
,
757 border_style border
, bool showHorizontalScrollbar
)
759 BView(name
, flags
| B_WILL_DRAW
| B_FRAME_EVENTS
| B_FULL_UPDATE_ON_RESIZE
),
761 fSelectionMessage(NULL
),
762 fSortingEnabled(true),
763 fLatchWidth(kLatchWidth
),
764 fBorderStyle(border
),
765 fShowingHorizontalScrollBar(showHorizontalScrollbar
)
771 BColumnListView::~BColumnListView()
773 while (BColumn
* column
= (BColumn
*)fColumns
.RemoveItem((int32
)0))
779 BColumnListView::InitiateDrag(BPoint
, bool)
786 BColumnListView::MessageDropped(BMessage
*, BPoint
)
792 BColumnListView::ExpandOrCollapse(BRow
* row
, bool Open
)
794 fOutlineView
->ExpandOrCollapse(row
, Open
);
799 BColumnListView::Invoke(BMessage
* message
)
804 return BInvoker::Invoke(message
);
809 BColumnListView::ItemInvoked()
816 BColumnListView::SetInvocationMessage(BMessage
* message
)
823 BColumnListView::InvocationMessage() const
830 BColumnListView::InvocationCommand() const
837 BColumnListView::FocusRow() const
839 return fOutlineView
->FocusRow();
844 BColumnListView::SetFocusRow(int32 Index
, bool Select
)
846 SetFocusRow(RowAt(Index
), Select
);
851 BColumnListView::SetFocusRow(BRow
* row
, bool Select
)
853 fOutlineView
->SetFocusRow(row
, Select
);
858 BColumnListView::SetMouseTrackingEnabled(bool Enabled
)
860 fOutlineView
->SetMouseTrackingEnabled(Enabled
);
865 BColumnListView::SelectionMode() const
867 return fOutlineView
->SelectionMode();
872 BColumnListView::Deselect(BRow
* row
)
874 fOutlineView
->Deselect(row
);
879 BColumnListView::AddToSelection(BRow
* row
)
881 fOutlineView
->AddToSelection(row
);
886 BColumnListView::DeselectAll()
888 fOutlineView
->DeselectAll();
893 BColumnListView::CurrentSelection(BRow
* lastSelected
) const
895 return fOutlineView
->CurrentSelection(lastSelected
);
900 BColumnListView::SelectionChanged()
902 if (fSelectionMessage
)
903 Invoke(fSelectionMessage
);
908 BColumnListView::SetSelectionMessage(BMessage
* message
)
910 if (fSelectionMessage
== message
)
913 delete fSelectionMessage
;
914 fSelectionMessage
= message
;
919 BColumnListView::SelectionMessage()
921 return fSelectionMessage
;
926 BColumnListView::SelectionCommand() const
928 if (fSelectionMessage
)
929 return fSelectionMessage
->what
;
936 BColumnListView::SetSelectionMode(list_view_type mode
)
938 fOutlineView
->SetSelectionMode(mode
);
943 BColumnListView::SetSortingEnabled(bool enabled
)
945 fSortingEnabled
= enabled
;
946 fSortColumns
.MakeEmpty();
947 fTitleView
->Invalidate();
948 // erase sort indicators
953 BColumnListView::SortingEnabled() const
955 return fSortingEnabled
;
960 BColumnListView::SetSortColumn(BColumn
* column
, bool add
, bool ascending
)
962 if (!SortingEnabled())
966 fSortColumns
.MakeEmpty();
968 if (!fSortColumns
.HasItem(column
))
969 fSortColumns
.AddItem(column
);
971 column
->fSortAscending
= ascending
;
972 fTitleView
->Invalidate();
973 fOutlineView
->StartSorting();
978 BColumnListView::ClearSortColumns()
980 fSortColumns
.MakeEmpty();
981 fTitleView
->Invalidate();
982 // erase sort indicators
987 BColumnListView::AddStatusView(BView
* view
)
989 BRect bounds
= Bounds();
990 float width
= view
->Bounds().Width();
991 if (width
> bounds
.Width() / 2)
992 width
= bounds
.Width() / 2;
996 Window()->BeginViewTransaction();
997 fHorizontalScrollBar
->ResizeBy(-(width
+ 1), 0);
998 fHorizontalScrollBar
->MoveBy((width
+ 1), 0);
1001 BRect
viewRect(bounds
);
1002 viewRect
.right
= width
;
1003 viewRect
.top
= viewRect
.bottom
- B_H_SCROLL_BAR_HEIGHT
;
1004 if (fBorderStyle
== B_PLAIN_BORDER
)
1005 viewRect
.OffsetBy(1, -1);
1006 else if (fBorderStyle
== B_FANCY_BORDER
)
1007 viewRect
.OffsetBy(2, -2);
1009 view
->SetResizingMode(B_FOLLOW_LEFT
| B_FOLLOW_BOTTOM
);
1010 view
->ResizeTo(viewRect
.Width(), viewRect
.Height());
1011 view
->MoveTo(viewRect
.left
, viewRect
.top
);
1012 Window()->EndViewTransaction();
1017 BColumnListView::RemoveStatusView()
1020 float width
= fStatusView
->Bounds().Width();
1021 Window()->BeginViewTransaction();
1022 fStatusView
->RemoveSelf();
1023 fHorizontalScrollBar
->MoveBy(-width
, 0);
1024 fHorizontalScrollBar
->ResizeBy(width
, 0);
1025 Window()->EndViewTransaction();
1028 BView
* view
= fStatusView
;
1035 BColumnListView::AddColumn(BColumn
* column
, int32 logicalFieldIndex
)
1037 ASSERT(column
!= NULL
);
1039 column
->fList
= this;
1040 column
->fFieldID
= logicalFieldIndex
;
1042 // sanity check -- if there is already a field with this ID, remove it.
1043 for (int32 index
= 0; index
< fColumns
.CountItems(); index
++) {
1044 BColumn
* existingColumn
= (BColumn
*) fColumns
.ItemAt(index
);
1045 if (existingColumn
&& existingColumn
->fFieldID
== logicalFieldIndex
) {
1046 RemoveColumn(existingColumn
);
1051 if (column
->Width() < column
->MinWidth())
1052 column
->SetWidth(column
->MinWidth());
1053 else if (column
->Width() > column
->MaxWidth())
1054 column
->SetWidth(column
->MaxWidth());
1056 fColumns
.AddItem((void*) column
);
1057 fTitleView
->ColumnAdded(column
);
1062 BColumnListView::MoveColumn(BColumn
* column
, int32 index
)
1064 ASSERT(column
!= NULL
);
1065 fTitleView
->MoveColumn(column
, index
);
1070 BColumnListView::RemoveColumn(BColumn
* column
)
1072 if (fColumns
.HasItem(column
)) {
1073 SetColumnVisible(column
, false);
1074 if (Window() != NULL
)
1075 Window()->UpdateIfNeeded();
1076 fColumns
.RemoveItem(column
);
1082 BColumnListView::CountColumns() const
1084 return fColumns
.CountItems();
1089 BColumnListView::ColumnAt(int32 field
) const
1091 return (BColumn
*) fColumns
.ItemAt(field
);
1096 BColumnListView::ColumnAt(BPoint point
) const
1098 float left
= MAX(kLeftMargin
, LatchWidth());
1100 for (int i
= 0; BColumn
* column
= (BColumn
*)fColumns
.ItemAt(i
); i
++) {
1101 if (column
== NULL
|| !column
->IsVisible())
1104 float right
= left
+ column
->Width();
1105 if (point
.x
>= left
&& point
.x
<= right
)
1116 BColumnListView::SetColumnVisible(BColumn
* column
, bool visible
)
1118 fTitleView
->SetColumnVisible(column
, visible
);
1123 BColumnListView::SetColumnVisible(int32 index
, bool isVisible
)
1125 BColumn
* column
= ColumnAt(index
);
1127 column
->SetVisible(isVisible
);
1132 BColumnListView::IsColumnVisible(int32 index
) const
1134 BColumn
* column
= ColumnAt(index
);
1136 return column
->IsVisible();
1143 BColumnListView::SetColumnFlags(column_flags flags
)
1145 fTitleView
->SetColumnFlags(flags
);
1150 BColumnListView::ResizeColumnToPreferred(int32 index
)
1152 BColumn
* column
= ColumnAt(index
);
1156 // get the preferred column width
1157 float width
= fOutlineView
->GetColumnPreferredWidth(column
);
1160 float oldWidth
= column
->Width();
1161 column
->SetWidth(width
);
1163 fTitleView
->ColumnResized(column
, oldWidth
);
1164 fOutlineView
->Invalidate();
1169 BColumnListView::ResizeAllColumnsToPreferred()
1171 int32 count
= CountColumns();
1172 for (int32 i
= 0; i
< count
; i
++)
1173 ResizeColumnToPreferred(i
);
1178 BColumnListView::RowAt(int32 Index
, BRow
* parentRow
) const
1181 return fOutlineView
->RowList()->ItemAt(Index
);
1183 return parentRow
->fChildList
? parentRow
->fChildList
->ItemAt(Index
) : NULL
;
1188 BColumnListView::RowAt(int32 Index
, BRow
* parentRow
)
1191 return fOutlineView
->RowList()->ItemAt(Index
);
1193 return parentRow
->fChildList
? parentRow
->fChildList
->ItemAt(Index
) : 0;
1198 BColumnListView::RowAt(BPoint point
) const
1202 return fOutlineView
->FindRow(point
.y
, &indent
, &top
);
1207 BColumnListView::RowAt(BPoint point
)
1211 return fOutlineView
->FindRow(point
.y
, &indent
, &top
);
1216 BColumnListView::GetRowRect(const BRow
* row
, BRect
* outRect
) const
1218 return fOutlineView
->FindRect(row
, outRect
);
1223 BColumnListView::FindParent(BRow
* row
, BRow
** _parent
, bool* _isVisible
) const
1225 return fOutlineView
->FindParent(row
, _parent
, _isVisible
);
1230 BColumnListView::IndexOf(BRow
* row
)
1232 return fOutlineView
->IndexOf(row
);
1237 BColumnListView::CountRows(BRow
* parentRow
) const
1240 return fOutlineView
->RowList()->CountItems();
1241 if (parentRow
->fChildList
)
1242 return parentRow
->fChildList
->CountItems();
1249 BColumnListView::AddRow(BRow
* row
, BRow
* parentRow
)
1251 AddRow(row
, -1, parentRow
);
1256 BColumnListView::AddRow(BRow
* row
, int32 index
, BRow
* parentRow
)
1258 row
->fChildList
= 0;
1260 row
->ValidateFields();
1261 fOutlineView
->AddRow(row
, index
, parentRow
);
1266 BColumnListView::RemoveRow(BRow
* row
)
1268 fOutlineView
->RemoveRow(row
);
1274 BColumnListView::UpdateRow(BRow
* row
)
1276 fOutlineView
->UpdateRow(row
);
1281 BColumnListView::SwapRows(int32 index1
, int32 index2
, BRow
* parentRow1
,
1287 BRowContainer
* container1
= NULL
;
1288 BRowContainer
* container2
= NULL
;
1290 if (parentRow1
== NULL
)
1291 container1
= fOutlineView
->RowList();
1293 container1
= parentRow1
->fChildList
;
1295 if (container1
== NULL
)
1298 if (parentRow2
== NULL
)
1299 container2
= fOutlineView
->RowList();
1301 container2
= parentRow2
->fChildList
;
1303 if (container2
== NULL
)
1306 row1
= container1
->ItemAt(index1
);
1311 row2
= container2
->ItemAt(index2
);
1316 container1
->ReplaceItem(index2
, row1
);
1317 container2
->ReplaceItem(index1
, row2
);
1323 fOutlineView
->FindRect(row1
, &rect1
);
1324 fOutlineView
->FindRect(row2
, &rect2
);
1326 rect
= rect1
| rect2
;
1328 fOutlineView
->Invalidate(rect
);
1335 BColumnListView::ScrollTo(const BRow
* row
)
1337 fOutlineView
->ScrollTo(row
);
1342 BColumnListView::ScrollTo(BPoint point
)
1344 fOutlineView
->ScrollTo(point
);
1349 BColumnListView::Clear()
1351 fOutlineView
->Clear();
1356 BColumnListView::InvalidateRow(BRow
* row
)
1359 GetRowRect(row
, &updateRect
);
1360 fOutlineView
->Invalidate(updateRect
);
1364 // This method is deprecated.
1366 BColumnListView::SetFont(const BFont
* font
, uint32 mask
)
1368 fOutlineView
->SetFont(font
, mask
);
1369 fTitleView
->SetFont(font
, mask
);
1374 BColumnListView::SetFont(ColumnListViewFont font_num
, const BFont
* font
,
1379 fOutlineView
->SetFont(font
, mask
);
1383 fTitleView
->SetFont(font
, mask
);
1394 BColumnListView::GetFont(ColumnListViewFont font_num
, BFont
* font
) const
1398 fOutlineView
->GetFont(font
);
1402 fTitleView
->GetFont(font
);
1413 BColumnListView::SetColor(ColumnListViewColor colorIndex
, const rgb_color color
)
1415 if ((int)colorIndex
< 0) {
1417 colorIndex
= (ColumnListViewColor
)0;
1420 if ((int)colorIndex
>= (int)B_COLOR_TOTAL
) {
1422 colorIndex
= (ColumnListViewColor
)(B_COLOR_TOTAL
- 1);
1425 fColorList
[colorIndex
] = color
;
1430 BColumnListView::Color(ColumnListViewColor colorIndex
) const
1432 if ((int)colorIndex
< 0) {
1434 colorIndex
= (ColumnListViewColor
)0;
1437 if ((int)colorIndex
>= (int)B_COLOR_TOTAL
) {
1439 colorIndex
= (ColumnListViewColor
)(B_COLOR_TOTAL
- 1);
1442 return fColorList
[colorIndex
];
1447 BColumnListView::SetHighColor(rgb_color color
)
1449 BView::SetHighColor(color
);
1450 // fOutlineView->Invalidate();
1451 // Redraw with the new color.
1452 // Note that this will currently cause an infinite loop, refreshing
1453 // over and over. A better solution is needed.
1458 BColumnListView::SetSelectionColor(rgb_color color
)
1460 fColorList
[B_COLOR_SELECTION
] = color
;
1465 BColumnListView::SetBackgroundColor(rgb_color color
)
1467 fColorList
[B_COLOR_BACKGROUND
] = color
;
1468 fOutlineView
->Invalidate();
1469 // repaint with new color
1474 BColumnListView::SetEditColor(rgb_color color
)
1476 fColorList
[B_COLOR_EDIT_BACKGROUND
] = color
;
1481 BColumnListView::SelectionColor() const
1483 return fColorList
[B_COLOR_SELECTION
];
1488 BColumnListView::BackgroundColor() const
1490 return fColorList
[B_COLOR_BACKGROUND
];
1495 BColumnListView::EditColor() const
1497 return fColorList
[B_COLOR_EDIT_BACKGROUND
];
1502 BColumnListView::SuggestTextPosition(const BRow
* row
,
1503 const BColumn
* inColumn
) const
1505 BRect
rect(GetFieldRect(row
, inColumn
));
1508 fOutlineView
->GetFontHeight(&fh
);
1509 float baseline
= floor(rect
.top
+ fh
.ascent
1510 + (rect
.Height() + 1 - (fh
.ascent
+ fh
.descent
)) / 2);
1511 return BPoint(rect
.left
+ 8, baseline
);
1516 BColumnListView::GetFieldRect(const BRow
* row
, const BColumn
* inColumn
) const
1519 GetRowRect(row
, &rect
);
1520 if (inColumn
!= NULL
) {
1521 float leftEdge
= MAX(kLeftMargin
, LatchWidth());
1522 for (int index
= 0; index
< fColumns
.CountItems(); index
++) {
1523 BColumn
* column
= (BColumn
*) fColumns
.ItemAt(index
);
1524 if (column
== NULL
|| !column
->IsVisible())
1527 if (column
== inColumn
) {
1528 rect
.left
= leftEdge
;
1529 rect
.right
= rect
.left
+ column
->Width();
1533 leftEdge
+= column
->Width() + 1;
1542 BColumnListView::SetLatchWidth(float width
)
1544 fLatchWidth
= width
;
1550 BColumnListView::LatchWidth() const
1556 BColumnListView::DrawLatch(BView
* view
, BRect rect
, LatchType position
, BRow
*)
1558 const int32 rectInset
= 4;
1561 int32 sideLen
= rect
.IntegerWidth();
1562 if (sideLen
> rect
.IntegerHeight())
1563 sideLen
= rect
.IntegerHeight();
1566 int32 halfWidth
= rect
.IntegerWidth() / 2;
1567 int32 halfHeight
= rect
.IntegerHeight() / 2;
1568 int32 halfSide
= sideLen
/ 2;
1570 float left
= rect
.left
+ halfWidth
- halfSide
;
1571 float top
= rect
.top
+ halfHeight
- halfSide
;
1573 BRect
itemRect(left
, top
, left
+ sideLen
, top
+ sideLen
);
1575 // Why it is a pixel high? I don't know.
1576 itemRect
.OffsetBy(0, -1);
1578 itemRect
.InsetBy(rectInset
, rectInset
);
1580 // make it an odd number of pixels wide, the latch looks better this way
1581 if ((itemRect
.IntegerWidth() % 2) == 1) {
1582 itemRect
.right
+= 1;
1583 itemRect
.bottom
+= 1;
1586 rgb_color highColor
= view
->HighColor();
1587 view
->SetHighColor(0, 0, 0);
1591 view
->StrokeRect(itemRect
);
1593 BPoint(itemRect
.left
+ 2,
1594 (itemRect
.top
+ itemRect
.bottom
) / 2),
1595 BPoint(itemRect
.right
- 2,
1596 (itemRect
.top
+ itemRect
.bottom
) / 2));
1599 case B_PRESSED_LATCH
:
1600 view
->StrokeRect(itemRect
);
1602 BPoint(itemRect
.left
+ 2,
1603 (itemRect
.top
+ itemRect
.bottom
) / 2),
1604 BPoint(itemRect
.right
- 2,
1605 (itemRect
.top
+ itemRect
.bottom
) / 2));
1607 BPoint((itemRect
.left
+ itemRect
.right
) / 2,
1609 BPoint((itemRect
.left
+ itemRect
.right
) / 2,
1610 itemRect
.bottom
- 2));
1611 view
->InvertRect(itemRect
);
1614 case B_CLOSED_LATCH
:
1615 view
->StrokeRect(itemRect
);
1617 BPoint(itemRect
.left
+ 2,
1618 (itemRect
.top
+ itemRect
.bottom
) / 2),
1619 BPoint(itemRect
.right
- 2,
1620 (itemRect
.top
+ itemRect
.bottom
) / 2));
1622 BPoint((itemRect
.left
+ itemRect
.right
) / 2,
1624 BPoint((itemRect
.left
+ itemRect
.right
) / 2,
1625 itemRect
.bottom
- 2));
1634 view
->SetHighColor(highColor
);
1639 BColumnListView::MakeFocus(bool isFocus
)
1641 if (fBorderStyle
!= B_NO_BORDER
) {
1642 // Redraw focus marks around view
1644 fHorizontalScrollBar
->SetBorderHighlighted(isFocus
);
1645 fVerticalScrollBar
->SetBorderHighlighted(isFocus
);
1648 BView::MakeFocus(isFocus
);
1653 BColumnListView::MessageReceived(BMessage
* message
)
1655 // Propagate mouse wheel messages down to child, so that it can
1656 // scroll. Note we have done so, so we don't go into infinite
1657 // recursion if this comes back up here.
1658 if (message
->what
== B_MOUSE_WHEEL_CHANGED
) {
1660 if (message
->FindBool("be:clvhandled", &handled
) != B_OK
) {
1661 message
->AddBool("be:clvhandled", true);
1662 fOutlineView
->MessageReceived(message
);
1667 BView::MessageReceived(message
);
1672 BColumnListView::KeyDown(const char* bytes
, int32 numBytes
)
1679 if ((modifiers() & B_SHIFT_KEY
) != 0) {
1680 float minVal
, maxVal
;
1681 fHorizontalScrollBar
->GetRange(&minVal
, &maxVal
);
1682 float smallStep
, largeStep
;
1683 fHorizontalScrollBar
->GetSteps(&smallStep
, &largeStep
);
1684 float oldVal
= fHorizontalScrollBar
->Value();
1685 float newVal
= oldVal
;
1687 if (c
== B_LEFT_ARROW
)
1688 newVal
-= smallStep
;
1689 else if (c
== B_RIGHT_ARROW
)
1690 newVal
+= smallStep
;
1692 if (newVal
< minVal
)
1694 else if (newVal
> maxVal
)
1697 fHorizontalScrollBar
->SetValue(newVal
);
1699 BRow
* focusRow
= fOutlineView
->FocusRow();
1700 if (focusRow
== NULL
)
1703 bool expanded
= focusRow
->IsExpanded();
1704 if ((c
== B_RIGHT_ARROW
&& !expanded
)
1705 || (c
== B_LEFT_ARROW
&& expanded
)) {
1706 fOutlineView
->ToggleFocusRowOpen();
1713 fOutlineView
->ChangeFocusRow(false,
1714 (modifiers() & B_CONTROL_KEY
) == 0,
1715 (modifiers() & B_SHIFT_KEY
) != 0);
1719 fOutlineView
->ChangeFocusRow(true,
1720 (modifiers() & B_CONTROL_KEY
) == 0,
1721 (modifiers() & B_SHIFT_KEY
) != 0);
1727 float minValue
, maxValue
;
1728 fVerticalScrollBar
->GetRange(&minValue
, &maxValue
);
1729 float smallStep
, largeStep
;
1730 fVerticalScrollBar
->GetSteps(&smallStep
, &largeStep
);
1731 float currentValue
= fVerticalScrollBar
->Value();
1732 float newValue
= currentValue
;
1735 newValue
-= largeStep
;
1737 newValue
+= largeStep
;
1739 if (newValue
> maxValue
)
1740 newValue
= maxValue
;
1741 else if (newValue
< minValue
)
1742 newValue
= minValue
;
1744 fVerticalScrollBar
->SetValue(newValue
);
1746 // Option + pgup or pgdn scrolls and changes the selection.
1747 if (modifiers() & B_OPTION_KEY
)
1748 fOutlineView
->MoveFocusToVisibleRect();
1758 fOutlineView
->ToggleFocusRowSelection(
1759 (modifiers() & B_SHIFT_KEY
) != 0);
1763 fOutlineView
->ToggleFocusRowOpen();
1767 BView::KeyDown(bytes
, numBytes
);
1773 BColumnListView::AttachedToWindow()
1775 if (!Messenger().IsValid())
1776 SetTarget(Window());
1778 if (SortingEnabled()) fOutlineView
->StartSorting();
1783 BColumnListView::WindowActivated(bool active
)
1785 fOutlineView
->Invalidate();
1786 // focus and selection appearance changes with focus
1789 // redraw focus marks around view
1790 BView::WindowActivated(active
);
1795 BColumnListView::Draw(BRect updateRect
)
1797 BRect rect
= Bounds();
1800 if (IsFocus() && Window()->IsActive())
1801 flags
|= BControlLook::B_FOCUSED
;
1803 rgb_color base
= ui_color(B_PANEL_BACKGROUND_COLOR
);
1805 BRect verticalScrollBarFrame
;
1806 if (!fVerticalScrollBar
->IsHidden())
1807 verticalScrollBarFrame
= fVerticalScrollBar
->Frame();
1809 BRect horizontalScrollBarFrame
;
1810 if (!fHorizontalScrollBar
->IsHidden())
1811 horizontalScrollBarFrame
= fHorizontalScrollBar
->Frame();
1813 if (fBorderStyle
== B_NO_BORDER
) {
1814 // We still draw the left/top border, but not focused.
1815 // The scrollbars cannot be displayed without frame and
1816 // it looks bad to have no frame only along the left/top
1818 rgb_color borderColor
= tint_color(base
, B_DARKEN_2_TINT
);
1819 SetHighColor(borderColor
);
1820 StrokeLine(BPoint(rect
.left
, rect
.bottom
),
1821 BPoint(rect
.left
, rect
.top
));
1822 StrokeLine(BPoint(rect
.left
+ 1, rect
.top
),
1823 BPoint(rect
.right
, rect
.top
));
1826 be_control_look
->DrawScrollViewFrame(this, rect
, updateRect
,
1827 verticalScrollBarFrame
, horizontalScrollBarFrame
,
1828 base
, fBorderStyle
, flags
);
1830 if (fStatusView
!= NULL
) {
1832 BRegion
region(rect
& fStatusView
->Frame().InsetByCopy(-2, -2));
1833 ConstrainClippingRegion(®ion
);
1834 rect
.bottom
= fStatusView
->Frame().top
- 1;
1835 be_control_look
->DrawScrollViewFrame(this, rect
, updateRect
,
1836 BRect(), BRect(), base
, fBorderStyle
, flags
);
1842 BColumnListView::SaveState(BMessage
* message
)
1844 message
->MakeEmpty();
1846 for (int32 i
= 0; BColumn
* column
= (BColumn
*)fColumns
.ItemAt(i
); i
++) {
1847 message
->AddInt32("ID", column
->fFieldID
);
1848 message
->AddFloat("width", column
->fWidth
);
1849 message
->AddBool("visible", column
->fVisible
);
1852 message
->AddBool("sortingenabled", fSortingEnabled
);
1854 if (fSortingEnabled
) {
1855 for (int32 i
= 0; BColumn
* column
= (BColumn
*)fSortColumns
.ItemAt(i
);
1857 message
->AddInt32("sortID", column
->fFieldID
);
1858 message
->AddBool("sortascending", column
->fSortAscending
);
1865 BColumnListView::LoadState(BMessage
* message
)
1868 for (int i
= 0; message
->FindInt32("ID", i
, &id
) == B_OK
; i
++) {
1869 for (int j
= 0; BColumn
* column
= (BColumn
*)fColumns
.ItemAt(j
); j
++) {
1870 if (column
->fFieldID
== id
) {
1871 // move this column to position 'i' and set its attributes
1872 MoveColumn(column
, i
);
1874 if (message
->FindFloat("width", i
, &width
) == B_OK
)
1875 column
->SetWidth(width
);
1877 if (message
->FindBool("visible", i
, &visible
) == B_OK
)
1878 column
->SetVisible(visible
);
1883 if (message
->FindBool("sortingenabled", &b
) == B_OK
) {
1884 SetSortingEnabled(b
);
1885 for (int k
= 0; message
->FindInt32("sortID", k
, &id
) == B_OK
; k
++) {
1886 for (int j
= 0; BColumn
* column
= (BColumn
*)fColumns
.ItemAt(j
);
1888 if (column
->fFieldID
== id
) {
1889 // add this column to the sort list
1891 if (message
->FindBool("sortascending", k
, &value
) == B_OK
)
1892 SetSortColumn(column
, true, value
);
1901 BColumnListView::SetEditMode(bool state
)
1903 fOutlineView
->SetEditMode(state
);
1904 fTitleView
->SetEditMode(state
);
1909 BColumnListView::Refresh()
1913 fOutlineView
->FixScrollBar (true);
1914 fOutlineView
->Invalidate();
1915 Window()->UpdateIfNeeded();
1922 BColumnListView::MinSize()
1926 size
.height
= std::max(kMinTitleHeight
,
1927 ceilf(be_plain_font
->Size() * kTitleSpacing
))
1928 + 4 * B_H_SCROLL_BAR_HEIGHT
;
1929 if (!fHorizontalScrollBar
->IsHidden())
1930 size
.height
+= fHorizontalScrollBar
->Frame().Height() + 1;
1931 // TODO: Take border size into account
1933 return BLayoutUtils::ComposeSize(ExplicitMinSize(), size
);
1938 BColumnListView::PreferredSize()
1940 BSize size
= MinSize();
1941 size
.height
+= ceilf(be_plain_font
->Size()) * 20;
1943 // return MinSize().width if there are no columns.
1944 int32 count
= CountColumns();
1948 BRect vScrollBarRect
;
1949 BRect hScrollBarRect
;
1950 _GetChildViewRects(Bounds(), titleRect
, outlineRect
, vScrollBarRect
,
1952 // Start with the extra width for border and scrollbars etc.
1953 size
.width
= titleRect
.left
- Bounds().left
;
1954 size
.width
+= Bounds().right
- titleRect
.right
;
1955 // If we want all columns to be visible at their preferred width,
1956 // we also need to add the extra margin width that the TitleView
1957 // uses to compute its _VirtualWidth() for the horizontal scroll bar.
1958 size
.width
+= fTitleView
->MarginWidth();
1959 for (int32 i
= 0; i
< count
; i
++) {
1960 BColumn
* column
= ColumnAt(i
);
1962 size
.width
+= fOutlineView
->GetColumnPreferredWidth(column
);
1966 return BLayoutUtils::ComposeSize(ExplicitPreferredSize(), size
);
1971 BColumnListView::MaxSize()
1973 BSize
size(B_SIZE_UNLIMITED
, B_SIZE_UNLIMITED
);
1974 return BLayoutUtils::ComposeSize(ExplicitMaxSize(), size
);
1979 BColumnListView::LayoutInvalidated(bool descendants
)
1985 BColumnListView::DoLayout()
1987 if ((Flags() & B_SUPPORTS_LAYOUT
) == 0)
1992 BRect vScrollBarRect
;
1993 BRect hScrollBarRect
;
1994 _GetChildViewRects(Bounds(), titleRect
, outlineRect
, vScrollBarRect
,
1997 fTitleView
->MoveTo(titleRect
.LeftTop());
1998 fTitleView
->ResizeTo(titleRect
.Width(), titleRect
.Height());
2000 fOutlineView
->MoveTo(outlineRect
.LeftTop());
2001 fOutlineView
->ResizeTo(outlineRect
.Width(), outlineRect
.Height());
2003 fVerticalScrollBar
->MoveTo(vScrollBarRect
.LeftTop());
2004 fVerticalScrollBar
->ResizeTo(vScrollBarRect
.Width(),
2005 vScrollBarRect
.Height());
2007 if (fStatusView
!= NULL
) {
2008 BSize size
= fStatusView
->MinSize();
2009 if (size
.height
> B_H_SCROLL_BAR_HEIGHT
)
2010 size
.height
= B_H_SCROLL_BAR_HEIGHT
;
2011 if (size
.width
> Bounds().Width() / 2)
2012 size
.width
= floorf(Bounds().Width() / 2);
2014 BPoint
offset(hScrollBarRect
.LeftTop());
2016 if (fBorderStyle
== B_PLAIN_BORDER
) {
2017 offset
+= BPoint(0, 1);
2018 } else if (fBorderStyle
== B_FANCY_BORDER
) {
2019 offset
+= BPoint(-1, 2);
2023 fStatusView
->MoveTo(offset
);
2024 fStatusView
->ResizeTo(size
.width
, size
.height
);
2025 hScrollBarRect
.left
= offset
.x
+ size
.width
+ 1;
2028 fHorizontalScrollBar
->MoveTo(hScrollBarRect
.LeftTop());
2029 fHorizontalScrollBar
->ResizeTo(hScrollBarRect
.Width(),
2030 hScrollBarRect
.Height());
2032 fOutlineView
->FixScrollBar(true);
2037 BColumnListView::_Init()
2039 SetViewColor(B_TRANSPARENT_32_BIT
);
2041 BRect
bounds(Bounds());
2042 if (bounds
.Width() <= 0)
2045 if (bounds
.Height() <= 0)
2046 bounds
.bottom
= 100;
2048 fColorList
[B_COLOR_BACKGROUND
] = ui_color(B_LIST_BACKGROUND_COLOR
);
2049 fColorList
[B_COLOR_TEXT
] = ui_color(B_LIST_ITEM_TEXT_COLOR
);
2050 fColorList
[B_COLOR_ROW_DIVIDER
] = tint_color(
2051 ui_color(B_LIST_SELECTED_BACKGROUND_COLOR
), B_DARKEN_2_TINT
);
2052 fColorList
[B_COLOR_SELECTION
] = ui_color(B_LIST_SELECTED_BACKGROUND_COLOR
);
2053 fColorList
[B_COLOR_SELECTION_TEXT
] =
2054 ui_color(B_LIST_SELECTED_ITEM_TEXT_COLOR
);
2056 // For non focus selection uses the selection color as BListView
2057 fColorList
[B_COLOR_NON_FOCUS_SELECTION
] =
2058 ui_color(B_LIST_SELECTED_BACKGROUND_COLOR
);
2060 // edit mode doesn't work very well
2061 fColorList
[B_COLOR_EDIT_BACKGROUND
] = tint_color(
2062 ui_color(B_LIST_SELECTED_BACKGROUND_COLOR
), B_DARKEN_1_TINT
);
2063 fColorList
[B_COLOR_EDIT_BACKGROUND
].alpha
= 180;
2066 fColorList
[B_COLOR_EDIT_TEXT
] = ui_color(B_LIST_SELECTED_ITEM_TEXT_COLOR
);
2068 fColorList
[B_COLOR_HEADER_BACKGROUND
] = ui_color(B_PANEL_BACKGROUND_COLOR
);
2069 fColorList
[B_COLOR_HEADER_TEXT
] = ui_color(B_PANEL_TEXT_COLOR
);
2072 fColorList
[B_COLOR_SEPARATOR_LINE
] = ui_color(B_LIST_ITEM_TEXT_COLOR
);
2073 fColorList
[B_COLOR_SEPARATOR_BORDER
] = ui_color(B_LIST_ITEM_TEXT_COLOR
);
2077 BRect vScrollBarRect
;
2078 BRect hScrollBarRect
;
2079 _GetChildViewRects(bounds
, titleRect
, outlineRect
, vScrollBarRect
,
2082 fOutlineView
= new OutlineView(outlineRect
, &fColumns
, &fSortColumns
, this);
2083 AddChild(fOutlineView
);
2086 fTitleView
= new TitleView(titleRect
, fOutlineView
, &fColumns
,
2087 &fSortColumns
, this, B_FOLLOW_LEFT_RIGHT
| B_FOLLOW_TOP
);
2088 AddChild(fTitleView
);
2090 fVerticalScrollBar
= new BScrollBar(vScrollBarRect
, "vertical_scroll_bar",
2091 fOutlineView
, 0.0, bounds
.Height(), B_VERTICAL
);
2092 AddChild(fVerticalScrollBar
);
2094 fHorizontalScrollBar
= new BScrollBar(hScrollBarRect
,
2095 "horizontal_scroll_bar", fTitleView
, 0.0, bounds
.Width(), B_HORIZONTAL
);
2096 AddChild(fHorizontalScrollBar
);
2098 if (!fShowingHorizontalScrollBar
)
2099 fHorizontalScrollBar
->Hide();
2101 fOutlineView
->FixScrollBar(true);
2106 BColumnListView::_GetChildViewRects(const BRect
& bounds
, BRect
& titleRect
,
2107 BRect
& outlineRect
, BRect
& vScrollBarRect
, BRect
& hScrollBarRect
)
2110 titleRect
.bottom
= titleRect
.top
+ std::max(kMinTitleHeight
,
2111 ceilf(be_plain_font
->Size() * kTitleSpacing
));
2112 #if !LOWER_SCROLLBAR
2113 titleRect
.right
-= B_V_SCROLL_BAR_WIDTH
;
2116 outlineRect
= bounds
;
2117 outlineRect
.top
= titleRect
.bottom
+ 1.0;
2118 outlineRect
.right
-= B_V_SCROLL_BAR_WIDTH
;
2119 if (fShowingHorizontalScrollBar
)
2120 outlineRect
.bottom
-= B_H_SCROLL_BAR_HEIGHT
;
2122 vScrollBarRect
= bounds
;
2124 vScrollBarRect
.top
+= std::max(kMinTitleHeight
,
2125 ceilf(be_plain_font
->Size() * kTitleSpacing
));
2128 vScrollBarRect
.left
= vScrollBarRect
.right
- B_V_SCROLL_BAR_WIDTH
;
2129 if (fShowingHorizontalScrollBar
)
2130 vScrollBarRect
.bottom
-= B_H_SCROLL_BAR_HEIGHT
;
2132 hScrollBarRect
= bounds
;
2133 hScrollBarRect
.top
= hScrollBarRect
.bottom
- B_H_SCROLL_BAR_HEIGHT
;
2134 hScrollBarRect
.right
-= B_V_SCROLL_BAR_WIDTH
;
2136 // Adjust stuff so the border will fit.
2137 if (fBorderStyle
== B_PLAIN_BORDER
|| fBorderStyle
== B_NO_BORDER
) {
2138 titleRect
.InsetBy(1, 0);
2139 titleRect
.OffsetBy(0, 1);
2140 outlineRect
.InsetBy(1, 1);
2141 } else if (fBorderStyle
== B_FANCY_BORDER
) {
2142 titleRect
.InsetBy(2, 0);
2143 titleRect
.OffsetBy(0, 2);
2144 outlineRect
.InsetBy(2, 2);
2146 vScrollBarRect
.OffsetBy(-1, 0);
2148 vScrollBarRect
.top
+= 2;
2149 vScrollBarRect
.bottom
-= 1;
2151 vScrollBarRect
.InsetBy(0, 1);
2153 hScrollBarRect
.OffsetBy(0, -1);
2154 hScrollBarRect
.InsetBy(1, 0);
2162 TitleView::TitleView(BRect rect
, OutlineView
* horizontalSlave
,
2163 BList
* visibleColumns
, BList
* sortColumns
, BColumnListView
* listView
,
2164 uint32 resizingMode
)
2166 BView(rect
, "title_view", resizingMode
, B_WILL_DRAW
| B_FRAME_EVENTS
),
2167 fOutlineView(horizontalSlave
),
2168 fColumns(visibleColumns
),
2169 fSortColumns(sortColumns
),
2170 // fColumnsWidth(0),
2171 fVisibleRect(rect
.OffsetToCopy(0, 0)),
2172 fCurrentState(INACTIVE
),
2174 fMasterView(listView
),
2176 fColumnFlags(B_ALLOW_COLUMN_MOVE
| B_ALLOW_COLUMN_RESIZE
2177 | B_ALLOW_COLUMN_POPUP
| B_ALLOW_COLUMN_REMOVE
)
2179 SetViewColor(B_TRANSPARENT_COLOR
);
2181 #if DOUBLE_BUFFERED_COLUMN_RESIZE
2182 // xxx this needs to be smart about the size of the backbuffer.
2183 BRect
doubleBufferRect(0, 0, 600, 35);
2184 fDrawBuffer
= new BBitmap(doubleBufferRect
, B_RGB32
, true);
2185 fDrawBufferView
= new BView(doubleBufferRect
, "double_buffer_view",
2186 B_FOLLOW_ALL_SIDES
, 0);
2187 fDrawBuffer
->Lock();
2188 fDrawBuffer
->AddChild(fDrawBufferView
);
2189 fDrawBuffer
->Unlock();
2192 fUpSortArrow
= new BBitmap(BRect(0, 0, 7, 7), B_CMAP8
);
2193 fDownSortArrow
= new BBitmap(BRect(0, 0, 7, 7), B_CMAP8
);
2195 fUpSortArrow
->SetBits((const void*) kUpSortArrow8x8
, 64, 0, B_CMAP8
);
2196 fDownSortArrow
->SetBits((const void*) kDownSortArrow8x8
, 64, 0, B_CMAP8
);
2198 fResizeCursor
= new BCursor(B_CURSOR_ID_RESIZE_EAST_WEST
);
2199 fMinResizeCursor
= new BCursor(B_CURSOR_ID_RESIZE_EAST
);
2200 fMaxResizeCursor
= new BCursor(B_CURSOR_ID_RESIZE_WEST
);
2201 fColumnMoveCursor
= new BCursor(B_CURSOR_ID_MOVE
);
2207 TitleView::~TitleView()
2212 #if DOUBLE_BUFFERED_COLUMN_RESIZE
2215 delete fUpSortArrow
;
2216 delete fDownSortArrow
;
2218 delete fResizeCursor
;
2219 delete fMaxResizeCursor
;
2220 delete fMinResizeCursor
;
2221 delete fColumnMoveCursor
;
2226 TitleView::ColumnAdded(BColumn
* column
)
2228 // fColumnsWidth += column->Width();
2229 FixScrollBar(false);
2235 TitleView::ColumnResized(BColumn
* column
, float oldWidth
)
2237 // fColumnsWidth += column->Width() - oldWidth;
2238 FixScrollBar(false);
2244 TitleView::SetColumnVisible(BColumn
* column
, bool visible
)
2246 if (column
->fVisible
== visible
)
2249 // If setting it visible, do this first so we can find its position
2250 // to invalidate. If hiding it, do it last.
2252 column
->fVisible
= visible
;
2255 GetTitleRect(column
, &titleInvalid
);
2257 // Now really set the visibility
2258 column
->fVisible
= visible
;
2261 // fColumnsWidth += column->Width();
2263 // fColumnsWidth -= column->Width();
2265 BRect
outlineInvalid(fOutlineView
->VisibleRect());
2266 outlineInvalid
.left
= titleInvalid
.left
;
2267 titleInvalid
.right
= outlineInvalid
.right
;
2269 Invalidate(titleInvalid
);
2270 fOutlineView
->Invalidate(outlineInvalid
);
2275 TitleView::GetTitleRect(BColumn
* findColumn
, BRect
* _rect
)
2277 float leftEdge
= MAX(kLeftMargin
, fMasterView
->LatchWidth());
2278 int32 numColumns
= fColumns
->CountItems();
2279 for (int index
= 0; index
< numColumns
; index
++) {
2280 BColumn
* column
= (BColumn
*) fColumns
->ItemAt(index
);
2281 if (!column
->IsVisible())
2284 if (column
== findColumn
) {
2285 _rect
->Set(leftEdge
, 0, leftEdge
+ column
->Width(),
2286 fVisibleRect
.bottom
);
2290 leftEdge
+= column
->Width() + 1;
2298 TitleView::FindColumn(BPoint position
, float* _leftEdge
)
2300 float leftEdge
= MAX(kLeftMargin
, fMasterView
->LatchWidth());
2301 int32 numColumns
= fColumns
->CountItems();
2302 for (int index
= 0; index
< numColumns
; index
++) {
2303 BColumn
* column
= (BColumn
*) fColumns
->ItemAt(index
);
2304 if (!column
->IsVisible())
2307 if (leftEdge
> position
.x
)
2310 if (position
.x
>= leftEdge
2311 && position
.x
<= leftEdge
+ column
->Width()) {
2312 *_leftEdge
= leftEdge
;
2316 leftEdge
+= column
->Width() + 1;
2324 TitleView::FixScrollBar(bool scrollToFit
)
2326 BScrollBar
* hScrollBar
= ScrollBar(B_HORIZONTAL
);
2327 if (hScrollBar
== NULL
)
2330 float virtualWidth
= _VirtualWidth();
2332 if (virtualWidth
> fVisibleRect
.Width()) {
2333 hScrollBar
->SetProportion(fVisibleRect
.Width() / virtualWidth
);
2335 // Perform the little trick if the user is scrolled over too far.
2336 // See OutlineView::FixScrollBar for a more in depth explanation
2337 float maxScrollBarValue
= virtualWidth
- fVisibleRect
.Width();
2338 if (scrollToFit
|| hScrollBar
->Value() <= maxScrollBarValue
) {
2339 hScrollBar
->SetRange(0.0, maxScrollBarValue
);
2340 hScrollBar
->SetSteps(50, fVisibleRect
.Width());
2342 } else if (hScrollBar
->Value() == 0.0) {
2343 // disable scroll bar.
2344 hScrollBar
->SetRange(0.0, 0.0);
2350 TitleView::DragSelectedColumn(BPoint position
)
2352 float invalidLeft
= fSelectedColumnRect
.left
;
2353 float invalidRight
= fSelectedColumnRect
.right
;
2356 int32 columnIndex
= FindColumn(position
, &leftEdge
);
2357 fSelectedColumnRect
.OffsetTo(leftEdge
, 0);
2359 MoveColumn(fSelectedColumn
, columnIndex
);
2361 fSelectedColumn
->fVisible
= true;
2362 ComputeDragBoundries(fSelectedColumn
, position
);
2364 // Redraw the new column position
2365 GetTitleRect(fSelectedColumn
, &fSelectedColumnRect
);
2366 invalidLeft
= MIN(fSelectedColumnRect
.left
, invalidLeft
);
2367 invalidRight
= MAX(fSelectedColumnRect
.right
, invalidRight
);
2369 Invalidate(BRect(invalidLeft
, 0, invalidRight
, fVisibleRect
.bottom
));
2370 fOutlineView
->Invalidate(BRect(invalidLeft
, 0, invalidRight
,
2371 fOutlineView
->VisibleRect().bottom
));
2373 DrawTitle(this, fSelectedColumnRect
, fSelectedColumn
, true);
2378 TitleView::MoveColumn(BColumn
* column
, int32 index
)
2380 fColumns
->RemoveItem((void*) column
);
2383 // Re-add the column at the end of the list.
2384 fColumns
->AddItem((void*) column
);
2386 fColumns
->AddItem((void*) column
, index
);
2392 TitleView::SetColumnFlags(column_flags flags
)
2394 fColumnFlags
= flags
;
2399 TitleView::MarginWidth() const
2401 return MAX(kLeftMargin
, fMasterView
->LatchWidth()) + kRightMargin
;
2406 TitleView::ResizeSelectedColumn(BPoint position
, bool preferred
)
2408 float minWidth
= fSelectedColumn
->MinWidth();
2409 float maxWidth
= fSelectedColumn
->MaxWidth();
2411 float oldWidth
= fSelectedColumn
->Width();
2412 float originalEdge
= fSelectedColumnRect
.left
+ oldWidth
;
2414 float width
= fOutlineView
->GetColumnPreferredWidth(fSelectedColumn
);
2415 fSelectedColumn
->SetWidth(width
);
2416 } else if (position
.x
> fSelectedColumnRect
.left
+ maxWidth
)
2417 fSelectedColumn
->SetWidth(maxWidth
);
2418 else if (position
.x
< fSelectedColumnRect
.left
+ minWidth
)
2419 fSelectedColumn
->SetWidth(minWidth
);
2421 fSelectedColumn
->SetWidth(position
.x
- fSelectedColumnRect
.left
- 1);
2423 float dX
= fSelectedColumnRect
.left
+ fSelectedColumn
->Width()
2426 float columnHeight
= fVisibleRect
.Height();
2427 BRect
originalRect(originalEdge
, 0, 1000000.0, columnHeight
);
2428 BRect
movedRect(originalRect
);
2429 movedRect
.OffsetBy(dX
, 0);
2431 // Update the size of the title column
2432 BRect
sourceRect(0, 0, fSelectedColumn
->Width(), columnHeight
);
2433 BRect
destRect(sourceRect
);
2434 destRect
.OffsetBy(fSelectedColumnRect
.left
, 0);
2436 #if DOUBLE_BUFFERED_COLUMN_RESIZE
2437 fDrawBuffer
->Lock();
2438 DrawTitle(fDrawBufferView
, sourceRect
, fSelectedColumn
, false);
2439 fDrawBufferView
->Sync();
2440 fDrawBuffer
->Unlock();
2442 CopyBits(originalRect
, movedRect
);
2443 DrawBitmap(fDrawBuffer
, sourceRect
, destRect
);
2445 CopyBits(originalRect
, movedRect
);
2446 DrawTitle(this, destRect
, fSelectedColumn
, false);
2449 // Update the body view
2450 BRect slaveSize
= fOutlineView
->VisibleRect();
2451 BRect
slaveSource(originalRect
);
2452 slaveSource
.bottom
= slaveSize
.bottom
;
2453 BRect
slaveDest(movedRect
);
2454 slaveDest
.bottom
= slaveSize
.bottom
;
2455 fOutlineView
->CopyBits(slaveSource
, slaveDest
);
2456 fOutlineView
->RedrawColumn(fSelectedColumn
, fSelectedColumnRect
.left
,
2457 fResizingFirstColumn
);
2459 // fColumnsWidth += dX;
2461 // Update the cursor
2462 if (fSelectedColumn
->Width() == minWidth
)
2463 SetViewCursor(fMinResizeCursor
, true);
2464 else if (fSelectedColumn
->Width() == maxWidth
)
2465 SetViewCursor(fMaxResizeCursor
, true);
2467 SetViewCursor(fResizeCursor
, true);
2469 ColumnResized(fSelectedColumn
, oldWidth
);
2475 TitleView::ComputeDragBoundries(BColumn
* findColumn
, BPoint
)
2477 float previousColumnLeftEdge
= -1000000.0;
2478 float nextColumnRightEdge
= 1000000.0;
2480 bool foundColumn
= false;
2481 float leftEdge
= MAX(kLeftMargin
, fMasterView
->LatchWidth());
2482 int32 numColumns
= fColumns
->CountItems();
2483 for (int index
= 0; index
< numColumns
; index
++) {
2484 BColumn
* column
= (BColumn
*) fColumns
->ItemAt(index
);
2485 if (!column
->IsVisible())
2488 if (column
== findColumn
) {
2494 nextColumnRightEdge
= leftEdge
+ column
->Width();
2497 previousColumnLeftEdge
= leftEdge
;
2499 leftEdge
+= column
->Width() + 1;
2502 float rightEdge
= leftEdge
+ findColumn
->Width();
2504 fLeftDragBoundry
= MIN(previousColumnLeftEdge
+ findColumn
->Width(),
2506 fRightDragBoundry
= MAX(nextColumnRightEdge
, rightEdge
);
2511 TitleView::DrawTitle(BView
* view
, BRect rect
, BColumn
* column
, bool depressed
)
2514 rgb_color borderColor
= mix_color(
2515 fMasterView
->Color(B_COLOR_HEADER_BACKGROUND
),
2516 make_color(0, 0, 0), 128);
2522 float baseline
= floor(drawRect
.top
+ fh
.ascent
2523 + (drawRect
.Height() + 1 - (fh
.ascent
+ fh
.descent
)) / 2);
2525 BRect bgRect
= rect
;
2527 rgb_color base
= ui_color(B_PANEL_BACKGROUND_COLOR
);
2528 view
->SetHighColor(tint_color(base
, B_DARKEN_2_TINT
));
2529 view
->StrokeLine(bgRect
.LeftBottom(), bgRect
.RightBottom());
2535 base
= tint_color(base
, B_DARKEN_1_TINT
);
2537 be_control_look
->DrawButtonBackground(view
, bgRect
, rect
, base
, 0,
2538 BControlLook::B_TOP_BORDER
| BControlLook::B_BOTTOM_BORDER
);
2540 view
->SetHighColor(tint_color(ui_color(B_PANEL_BACKGROUND_COLOR
),
2542 view
->StrokeLine(rect
.RightTop(), rect
.RightBottom());
2544 // If no column given, nothing else to draw.
2548 view
->SetHighColor(fMasterView
->Color(B_COLOR_HEADER_TEXT
));
2552 view
->SetFont(&font
);
2554 int sortIndex
= fSortColumns
->IndexOf(column
);
2555 if (sortIndex
>= 0) {
2556 // Draw sort notation.
2557 BPoint
upperLeft(drawRect
.right
- kSortIndicatorWidth
, baseline
);
2559 if (fSortColumns
->CountItems() > 1) {
2561 sprintf(str
, "%d", sortIndex
+ 1);
2562 const float w
= view
->StringWidth(str
);
2565 view
->SetDrawingMode(B_OP_COPY
);
2566 view
->MovePenTo(BPoint(upperLeft
.x
+ kSortIndicatorWidth
,
2568 view
->DrawString(str
);
2571 float bmh
= fDownSortArrow
->Bounds().Height()+1;
2573 view
->SetDrawingMode(B_OP_OVER
);
2575 if (column
->fSortAscending
) {
2576 BPoint
leftTop(upperLeft
.x
, drawRect
.top
+ (drawRect
.IntegerHeight()
2577 - fDownSortArrow
->Bounds().IntegerHeight()) / 2);
2578 view
->DrawBitmapAsync(fDownSortArrow
, leftTop
);
2580 BPoint
leftTop(upperLeft
.x
, drawRect
.top
+ (drawRect
.IntegerHeight()
2581 - fUpSortArrow
->Bounds().IntegerHeight()) / 2);
2582 view
->DrawBitmapAsync(fUpSortArrow
, leftTop
);
2585 upperLeft
.y
= baseline
- bmh
+ floor((fh
.ascent
+ fh
.descent
- bmh
) / 2);
2586 if (upperLeft
.y
< drawRect
.top
)
2587 upperLeft
.y
= drawRect
.top
;
2589 // Adjust title stuff for sort indicator
2590 drawRect
.right
= upperLeft
.x
- 2;
2593 if (drawRect
.right
> drawRect
.left
) {
2594 #if CONSTRAIN_CLIPPING_REGION
2595 BRegion
clipRegion(drawRect
);
2597 view
->ConstrainClippingRegion(&clipRegion
);
2599 view
->MovePenTo(BPoint(drawRect
.left
+ 8, baseline
));
2600 view
->SetDrawingMode(B_OP_OVER
);
2601 view
->SetHighColor(fMasterView
->Color(B_COLOR_HEADER_TEXT
));
2602 column
->DrawTitle(drawRect
, view
);
2604 #if CONSTRAIN_CLIPPING_REGION
2612 TitleView::_VirtualWidth() const
2614 float width
= MarginWidth();
2616 int32 count
= fColumns
->CountItems();
2617 for (int32 i
= 0; i
< count
; i
++) {
2618 BColumn
* column
= reinterpret_cast<BColumn
*>(fColumns
->ItemAt(i
));
2619 width
+= column
->Width();
2627 TitleView::Draw(BRect invalidRect
)
2629 float columnLeftEdge
= MAX(kLeftMargin
, fMasterView
->LatchWidth());
2630 for (int32 columnIndex
= 0; columnIndex
< fColumns
->CountItems();
2633 BColumn
* column
= (BColumn
*) fColumns
->ItemAt(columnIndex
);
2634 if (!column
->IsVisible())
2637 if (columnLeftEdge
> invalidRect
.right
)
2640 if (columnLeftEdge
+ column
->Width() >= invalidRect
.left
) {
2641 BRect
titleRect(columnLeftEdge
, 0,
2642 columnLeftEdge
+ column
->Width(), fVisibleRect
.Height());
2643 DrawTitle(this, titleRect
, column
,
2644 (fCurrentState
== DRAG_COLUMN_INSIDE_TITLE
2645 && fSelectedColumn
== column
));
2648 columnLeftEdge
+= column
->Width() + 1;
2652 // bevels for right title margin
2653 if (columnLeftEdge
<= invalidRect
.right
) {
2654 BRect
titleRect(columnLeftEdge
, 0, Bounds().right
+ 2,
2655 fVisibleRect
.Height());
2656 DrawTitle(this, titleRect
, NULL
, false);
2659 // bevels for left title margin
2660 if (invalidRect
.left
< MAX(kLeftMargin
, fMasterView
->LatchWidth())) {
2661 BRect
titleRect(0, 0, MAX(kLeftMargin
, fMasterView
->LatchWidth()) - 1,
2662 fVisibleRect
.Height());
2663 DrawTitle(this, titleRect
, NULL
, false);
2666 #if DRAG_TITLE_OUTLINE
2667 // (internal) column drag indicator
2668 if (fCurrentState
== DRAG_COLUMN_INSIDE_TITLE
) {
2669 BRect
dragRect(fSelectedColumnRect
);
2670 dragRect
.OffsetTo(fCurrentDragPosition
.x
- fClickPoint
.x
, 0);
2671 if (dragRect
.Intersects(invalidRect
)) {
2672 SetHighColor(0, 0, 255);
2673 StrokeRect(dragRect
);
2681 TitleView::ScrollTo(BPoint position
)
2683 fOutlineView
->ScrollBy(position
.x
- fVisibleRect
.left
, 0);
2684 fVisibleRect
.OffsetTo(position
.x
, position
.y
);
2686 // Perform the little trick if the user is scrolled over too far.
2687 // See OutlineView::ScrollTo for a more in depth explanation
2688 float maxScrollBarValue
= _VirtualWidth() - fVisibleRect
.Width();
2689 BScrollBar
* hScrollBar
= ScrollBar(B_HORIZONTAL
);
2691 hScrollBar
->GetRange(&min
, &max
);
2692 if (max
!= maxScrollBarValue
&& position
.x
> maxScrollBarValue
)
2695 _inherited::ScrollTo(position
);
2700 TitleView::MessageReceived(BMessage
* message
)
2702 if (message
->what
== kToggleColumn
) {
2704 if (message
->FindInt32("be:field_num", &num
) == B_OK
) {
2705 for (int index
= 0; index
< fColumns
->CountItems(); index
++) {
2706 BColumn
* column
= (BColumn
*) fColumns
->ItemAt(index
);
2710 if (column
->LogicalFieldNum() == num
)
2711 column
->SetVisible(!column
->IsVisible());
2717 BView::MessageReceived(message
);
2722 TitleView::MouseDown(BPoint position
)
2728 Window()->CurrentMessage()->FindInt32("buttons", &buttons
);
2729 if (buttons
== B_SECONDARY_MOUSE_BUTTON
2730 && (fColumnFlags
& B_ALLOW_COLUMN_POPUP
)) {
2731 // Right mouse button -- bring up menu to show/hide columns.
2732 if (fColumnPop
== NULL
)
2733 fColumnPop
= new BPopUpMenu("Columns", false, false);
2735 fColumnPop
->RemoveItems(0, fColumnPop
->CountItems(), true);
2736 BMessenger
me(this);
2737 for (int index
= 0; index
< fColumns
->CountItems(); index
++) {
2738 BColumn
* column
= (BColumn
*) fColumns
->ItemAt(index
);
2743 column
->GetColumnName(&name
);
2744 BMessage
* message
= new BMessage(kToggleColumn
);
2745 message
->AddInt32("be:field_num", column
->LogicalFieldNum());
2746 BMenuItem
* item
= new BMenuItem(name
.String(), message
);
2747 item
->SetMarked(column
->IsVisible());
2748 item
->SetTarget(me
);
2749 fColumnPop
->AddItem(item
);
2752 BPoint screenPosition
= ConvertToScreen(position
);
2753 BRect
sticky(screenPosition
, screenPosition
);
2754 sticky
.InsetBy(-5, -5);
2755 fColumnPop
->Go(ConvertToScreen(position
), true, false, sticky
, true);
2760 fResizingFirstColumn
= true;
2761 float leftEdge
= MAX(kLeftMargin
, fMasterView
->LatchWidth());
2762 for (int index
= 0; index
< fColumns
->CountItems(); index
++) {
2763 BColumn
* column
= (BColumn
*)fColumns
->ItemAt(index
);
2764 if (column
== NULL
|| !column
->IsVisible())
2767 if (leftEdge
> position
.x
+ kColumnResizeAreaWidth
/ 2)
2770 // check for resizing a column
2771 float rightEdge
= leftEdge
+ column
->Width();
2773 if (column
->ShowHeading()) {
2774 if (position
.x
> rightEdge
- kColumnResizeAreaWidth
/ 2
2775 && position
.x
< rightEdge
+ kColumnResizeAreaWidth
/ 2
2776 && column
->MaxWidth() > column
->MinWidth()
2777 && (fColumnFlags
& B_ALLOW_COLUMN_RESIZE
) != 0) {
2780 fSelectedColumn
= column
;
2781 fSelectedColumnRect
.Set(leftEdge
, 0, rightEdge
,
2782 fVisibleRect
.Height());
2783 Window()->CurrentMessage()->FindInt32("clicks", &clicks
);
2784 if (clicks
== 2 || buttons
== B_TERTIARY_MOUSE_BUTTON
) {
2785 ResizeSelectedColumn(position
, true);
2786 fCurrentState
= INACTIVE
;
2789 fCurrentState
= RESIZING_COLUMN
;
2790 fClickPoint
= BPoint(position
.x
- rightEdge
- 1,
2791 position
.y
- fSelectedColumnRect
.top
);
2792 SetMouseEventMask(B_POINTER_EVENTS
,
2793 B_LOCK_WINDOW_FOCUS
| B_NO_POINTER_HISTORY
);
2797 fResizingFirstColumn
= false;
2799 // check for clicking on a column
2800 if (position
.x
> leftEdge
&& position
.x
< rightEdge
) {
2801 fCurrentState
= PRESSING_COLUMN
;
2802 fSelectedColumn
= column
;
2803 fSelectedColumnRect
.Set(leftEdge
, 0, rightEdge
,
2804 fVisibleRect
.Height());
2805 DrawTitle(this, fSelectedColumnRect
, fSelectedColumn
, true);
2806 fClickPoint
= BPoint(position
.x
- fSelectedColumnRect
.left
,
2807 position
.y
- fSelectedColumnRect
.top
);
2808 SetMouseEventMask(B_POINTER_EVENTS
,
2809 B_LOCK_WINDOW_FOCUS
| B_NO_POINTER_HISTORY
);
2813 leftEdge
= rightEdge
+ 1;
2819 TitleView::MouseMoved(BPoint position
, uint32 transit
,
2820 const BMessage
* dragMessage
)
2825 // Handle column manipulation
2826 switch (fCurrentState
) {
2827 case RESIZING_COLUMN
:
2828 ResizeSelectedColumn(position
- BPoint(fClickPoint
.x
, 0));
2831 case PRESSING_COLUMN
: {
2832 if (abs((int32
)(position
.x
- (fClickPoint
.x
2833 + fSelectedColumnRect
.left
))) > kColumnResizeAreaWidth
2834 || abs((int32
)(position
.y
- (fClickPoint
.y
2835 + fSelectedColumnRect
.top
))) > kColumnResizeAreaWidth
) {
2836 // User has moved the mouse more than the tolerable amount,
2838 if (transit
== B_INSIDE_VIEW
|| transit
== B_ENTERED_VIEW
) {
2839 if(fColumnFlags
& B_ALLOW_COLUMN_MOVE
) {
2840 fCurrentState
= DRAG_COLUMN_INSIDE_TITLE
;
2841 ComputeDragBoundries(fSelectedColumn
, position
);
2842 SetViewCursor(fColumnMoveCursor
, true);
2843 #if DRAG_TITLE_OUTLINE
2844 BRect
invalidRect(fSelectedColumnRect
);
2845 invalidRect
.OffsetTo(position
.x
- fClickPoint
.x
, 0);
2846 fCurrentDragPosition
= position
;
2847 Invalidate(invalidRect
);
2851 if(fColumnFlags
& B_ALLOW_COLUMN_REMOVE
) {
2852 // Dragged outside view
2853 fCurrentState
= DRAG_COLUMN_OUTSIDE_TITLE
;
2854 fSelectedColumn
->SetVisible(false);
2855 BRect
dragRect(fSelectedColumnRect
);
2857 // There is a race condition where the mouse may have
2858 // moved by the time we get to handle this message.
2859 // If the user drags a column very quickly, this
2860 // results in the annoying bug where the cursor is
2861 // outside of the rectangle that is being dragged
2862 // around. Call GetMouse with the checkQueue flag set
2863 // to false so we can get the most recent position of
2864 // the mouse. This minimizes this problem (although
2865 // it is currently not possible to completely eliminate
2868 GetMouse(&position
, &buttons
, false);
2869 dragRect
.OffsetTo(position
.x
- fClickPoint
.x
,
2870 position
.y
- dragRect
.Height() / 2);
2871 BeginRectTracking(dragRect
, B_TRACK_WHOLE_RECT
);
2879 case DRAG_COLUMN_INSIDE_TITLE
: {
2880 if (transit
== B_EXITED_VIEW
2881 && (fColumnFlags
& B_ALLOW_COLUMN_REMOVE
)) {
2882 // Dragged outside view
2883 fCurrentState
= DRAG_COLUMN_OUTSIDE_TITLE
;
2884 fSelectedColumn
->SetVisible(false);
2885 BRect
dragRect(fSelectedColumnRect
);
2887 // See explanation above.
2889 GetMouse(&position
, &buttons
, false);
2891 dragRect
.OffsetTo(position
.x
- fClickPoint
.x
,
2892 position
.y
- fClickPoint
.y
);
2893 BeginRectTracking(dragRect
, B_TRACK_WHOLE_RECT
);
2894 } else if (position
.x
< fLeftDragBoundry
2895 || position
.x
> fRightDragBoundry
) {
2896 DragSelectedColumn(position
- BPoint(fClickPoint
.x
, 0));
2899 #if DRAG_TITLE_OUTLINE
2900 // Set up the invalid rect to include the rect for the previous
2901 // position of the drag rect, as well as the new one.
2902 BRect
invalidRect(fSelectedColumnRect
);
2903 invalidRect
.OffsetTo(fCurrentDragPosition
.x
- fClickPoint
.x
, 0);
2904 if (position
.x
< fCurrentDragPosition
.x
)
2905 invalidRect
.left
-= fCurrentDragPosition
.x
- position
.x
;
2907 invalidRect
.right
+= position
.x
- fCurrentDragPosition
.x
;
2909 fCurrentDragPosition
= position
;
2910 Invalidate(invalidRect
);
2915 case DRAG_COLUMN_OUTSIDE_TITLE
:
2916 if (transit
== B_ENTERED_VIEW
) {
2917 // Drag back into view
2919 fCurrentState
= DRAG_COLUMN_INSIDE_TITLE
;
2920 fSelectedColumn
->SetVisible(true);
2921 DragSelectedColumn(position
- BPoint(fClickPoint
.x
, 0));
2927 // Check for cursor changes if we are over the resize area for
2929 BColumn
* resizeColumn
= 0;
2930 float leftEdge
= MAX(kLeftMargin
, fMasterView
->LatchWidth());
2931 for (int index
= 0; index
< fColumns
->CountItems(); index
++) {
2932 BColumn
* column
= (BColumn
*) fColumns
->ItemAt(index
);
2933 if (!column
->IsVisible())
2936 if (leftEdge
> position
.x
+ kColumnResizeAreaWidth
/ 2)
2939 float rightEdge
= leftEdge
+ column
->Width();
2940 if (position
.x
> rightEdge
- kColumnResizeAreaWidth
/ 2
2941 && position
.x
< rightEdge
+ kColumnResizeAreaWidth
/ 2
2942 && column
->MaxWidth() > column
->MinWidth()) {
2943 resizeColumn
= column
;
2947 leftEdge
= rightEdge
+ 1;
2950 // Update the cursor
2952 if (resizeColumn
->Width() == resizeColumn
->MinWidth())
2953 SetViewCursor(fMinResizeCursor
, true);
2954 else if (resizeColumn
->Width() == resizeColumn
->MaxWidth())
2955 SetViewCursor(fMaxResizeCursor
, true);
2957 SetViewCursor(fResizeCursor
, true);
2959 SetViewCursor(B_CURSOR_SYSTEM_DEFAULT
, true);
2966 TitleView::MouseUp(BPoint position
)
2971 switch (fCurrentState
) {
2972 case RESIZING_COLUMN
:
2973 ResizeSelectedColumn(position
- BPoint(fClickPoint
.x
, 0));
2974 fCurrentState
= INACTIVE
;
2975 FixScrollBar(false);
2978 case PRESSING_COLUMN
: {
2979 if (fMasterView
->SortingEnabled()) {
2980 if (fSortColumns
->HasItem(fSelectedColumn
)) {
2981 if ((modifiers() & B_CONTROL_KEY
) == 0
2982 && fSortColumns
->CountItems() > 1) {
2983 fSortColumns
->MakeEmpty();
2984 fSortColumns
->AddItem(fSelectedColumn
);
2987 fSelectedColumn
->fSortAscending
2988 = !fSelectedColumn
->fSortAscending
;
2990 if ((modifiers() & B_CONTROL_KEY
) == 0)
2991 fSortColumns
->MakeEmpty();
2993 fSortColumns
->AddItem(fSelectedColumn
);
2994 fSelectedColumn
->fSortAscending
= true;
2997 fOutlineView
->StartSorting();
3000 fCurrentState
= INACTIVE
;
3005 case DRAG_COLUMN_INSIDE_TITLE
:
3006 fCurrentState
= INACTIVE
;
3008 #if DRAG_TITLE_OUTLINE
3009 Invalidate(); // xxx Can make this smaller
3011 Invalidate(fSelectedColumnRect
);
3013 SetViewCursor(B_CURSOR_SYSTEM_DEFAULT
, true);
3016 case DRAG_COLUMN_OUTSIDE_TITLE
:
3017 fCurrentState
= INACTIVE
;
3019 SetViewCursor(B_CURSOR_SYSTEM_DEFAULT
, true);
3029 TitleView::FrameResized(float width
, float height
)
3031 fVisibleRect
.right
= fVisibleRect
.left
+ width
;
3032 fVisibleRect
.bottom
= fVisibleRect
.top
+ height
;
3037 // #pragma mark - OutlineView
3040 OutlineView::OutlineView(BRect rect
, BList
* visibleColumns
, BList
* sortColumns
,
3041 BColumnListView
* listView
)
3043 BView(rect
, "outline_view", B_FOLLOW_ALL_SIDES
,
3044 B_WILL_DRAW
| B_FRAME_EVENTS
),
3045 fColumns(visibleColumns
),
3046 fSortColumns(sortColumns
),
3048 fVisibleRect(rect
.OffsetToCopy(0, 0)),
3051 fLastSelectedItem(0),
3052 fFirstSelectedItem(0),
3053 fSortThread(B_BAD_THREAD_ID
),
3054 fCurrentState(INACTIVE
),
3055 fMasterView(listView
),
3056 fSelectionMode(B_MULTIPLE_SELECTION_LIST
),
3062 fCurrentCode(B_OUTSIDE_VIEW
),
3068 SetViewColor(B_TRANSPARENT_COLOR
);
3070 #if DOUBLE_BUFFERED_COLUMN_RESIZE
3071 // TODO: This needs to be smart about the size of the buffer.
3072 // Also, the buffer can be shared with the title's buffer.
3073 BRect
doubleBufferRect(0, 0, 600, 35);
3074 fDrawBuffer
= new BBitmap(doubleBufferRect
, B_RGB32
, true);
3075 fDrawBufferView
= new BView(doubleBufferRect
, "double_buffer_view",
3076 B_FOLLOW_ALL_SIDES
, 0);
3077 fDrawBuffer
->Lock();
3078 fDrawBuffer
->AddChild(fDrawBufferView
);
3079 fDrawBuffer
->Unlock();
3083 fSelectionListDummyHead
.fNextSelected
= &fSelectionListDummyHead
;
3084 fSelectionListDummyHead
.fPrevSelected
= &fSelectionListDummyHead
;
3088 OutlineView::~OutlineView()
3090 #if DOUBLE_BUFFERED_COLUMN_RESIZE
3099 OutlineView::Clear()
3102 // Make sure selection list doesn't point to deleted rows!
3103 RecursiveDeleteRows(&fRows
, false);
3111 OutlineView::SetSelectionMode(list_view_type mode
)
3114 fSelectionMode
= mode
;
3119 OutlineView::SelectionMode() const
3121 return fSelectionMode
;
3126 OutlineView::Deselect(BRow
* row
)
3131 if (row
->fNextSelected
!= 0) {
3132 row
->fNextSelected
->fPrevSelected
= row
->fPrevSelected
;
3133 row
->fPrevSelected
->fNextSelected
= row
->fNextSelected
;
3134 row
->fNextSelected
= 0;
3135 row
->fPrevSelected
= 0;
3142 OutlineView::AddToSelection(BRow
* row
)
3147 if (row
->fNextSelected
== 0) {
3148 if (fSelectionMode
== B_SINGLE_SELECTION_LIST
)
3151 row
->fNextSelected
= fSelectionListDummyHead
.fNextSelected
;
3152 row
->fPrevSelected
= &fSelectionListDummyHead
;
3153 row
->fNextSelected
->fPrevSelected
= row
;
3154 row
->fPrevSelected
->fNextSelected
= row
;
3157 if (FindVisibleRect(row
, &invalidRect
))
3158 Invalidate(invalidRect
);
3164 OutlineView::RecursiveDeleteRows(BRowContainer
* list
, bool isOwner
)
3170 BRow
* row
= list
->RemoveItemAt(0L);
3174 if (row
->fChildList
)
3175 RecursiveDeleteRows(row
->fChildList
, true);
3186 OutlineView::RedrawColumn(BColumn
* column
, float leftEdge
, bool isFirstColumn
)
3188 // TODO: Remove code duplication (private function which takes a view
3189 // pointer, pass "this" in non-double buffered mode)!
3190 // Watch out for sourceRect versus destRect though!
3197 bool tintedLine
= true;
3198 for (RecursiveOutlineIterator
iterator(&fRows
); iterator
.CurrentRow();
3199 line
+= iterator
.CurrentRow()->Height() + 1, iterator
.GoToNext()) {
3201 BRow
* row
= iterator
.CurrentRow();
3202 float rowHeight
= row
->Height();
3203 if (line
> fVisibleRect
.bottom
)
3205 tintedLine
= !tintedLine
;
3207 if (line
+ rowHeight
>= fVisibleRect
.top
) {
3208 #if DOUBLE_BUFFERED_COLUMN_RESIZE
3209 BRect
sourceRect(0, 0, column
->Width(), rowHeight
);
3211 BRect
destRect(leftEdge
, line
, leftEdge
+ column
->Width(),
3214 rgb_color highColor
;
3216 if (row
->fNextSelected
!= 0) {
3218 highColor
= fMasterView
->Color(B_COLOR_EDIT_BACKGROUND
);
3219 lowColor
= fMasterView
->Color(B_COLOR_EDIT_BACKGROUND
);
3221 highColor
= fMasterView
->Color(B_COLOR_SELECTION
);
3222 lowColor
= fMasterView
->Color(B_COLOR_SELECTION
);
3225 highColor
= fMasterView
->Color(B_COLOR_BACKGROUND
);
3226 lowColor
= fMasterView
->Color(B_COLOR_BACKGROUND
);
3229 lowColor
= tint_color(lowColor
, kTintedLineTint
);
3232 #if DOUBLE_BUFFERED_COLUMN_RESIZE
3233 fDrawBuffer
->Lock();
3235 fDrawBufferView
->SetHighColor(highColor
);
3236 fDrawBufferView
->SetLowColor(lowColor
);
3240 fDrawBufferView
->SetFont(&font
);
3241 fDrawBufferView
->FillRect(sourceRect
, B_SOLID_LOW
);
3243 if (isFirstColumn
) {
3244 // If this is the first column, double buffer drawing the latch
3246 destRect
.left
+= iterator
.CurrentLevel() * kOutlineLevelIndent
3247 - fMasterView
->LatchWidth();
3248 sourceRect
.left
+= iterator
.CurrentLevel() * kOutlineLevelIndent
3249 - fMasterView
->LatchWidth();
3251 LatchType pos
= B_NO_LATCH
;
3252 if (row
->HasLatch())
3253 pos
= row
->fIsExpanded
? B_OPEN_LATCH
: B_CLOSED_LATCH
;
3255 BRect
latchRect(sourceRect
);
3256 latchRect
.right
= latchRect
.left
+ fMasterView
->LatchWidth();
3257 fMasterView
->DrawLatch(fDrawBufferView
, latchRect
, pos
, row
);
3260 BField
* field
= row
->GetField(column
->fFieldID
);
3262 BRect
fieldRect(sourceRect
);
3264 fieldRect
.left
+= fMasterView
->LatchWidth();
3266 #if CONSTRAIN_CLIPPING_REGION
3267 BRegion
clipRegion(fieldRect
);
3268 fDrawBufferView
->PushState();
3269 fDrawBufferView
->ConstrainClippingRegion(&clipRegion
);
3271 fDrawBufferView
->SetHighColor(fMasterView
->Color(
3272 row
->fNextSelected
? B_COLOR_SELECTION_TEXT
3274 float baseline
= floor(fieldRect
.top
+ fh
.ascent
3275 + (fieldRect
.Height() + 1 - (fh
.ascent
+fh
.descent
)) / 2);
3276 fDrawBufferView
->MovePenTo(fieldRect
.left
+ 8, baseline
);
3277 column
->DrawField(field
, fieldRect
, fDrawBufferView
);
3278 #if CONSTRAIN_CLIPPING_REGION
3279 fDrawBufferView
->PopState();
3283 if (fFocusRow
== row
&& !fEditMode
&& fMasterView
->IsFocus()
3284 && Window()->IsActive()) {
3285 fDrawBufferView
->SetHighColor(fMasterView
->Color(
3286 B_COLOR_ROW_DIVIDER
));
3287 fDrawBufferView
->StrokeRect(BRect(-1, sourceRect
.top
,
3288 10000.0, sourceRect
.bottom
));
3291 fDrawBufferView
->Sync();
3292 fDrawBuffer
->Unlock();
3293 SetDrawingMode(B_OP_COPY
);
3294 DrawBitmap(fDrawBuffer
, sourceRect
, destRect
);
3298 SetHighColor(highColor
);
3299 SetLowColor(lowColor
);
3300 FillRect(destRect
, B_SOLID_LOW
);
3302 BField
* field
= row
->GetField(column
->fFieldID
);
3304 #if CONSTRAIN_CLIPPING_REGION
3305 BRegion
clipRegion(destRect
);
3307 ConstrainClippingRegion(&clipRegion
);
3309 SetHighColor(fMasterView
->Color(row
->fNextSelected
3310 ? B_COLOR_SELECTION_TEXT
: B_COLOR_TEXT
));
3311 float baseline
= floor(destRect
.top
+ fh
.ascent
3312 + (destRect
.Height() + 1 - (fh
.ascent
+ fh
.descent
)) / 2);
3313 MovePenTo(destRect
.left
+ 8, baseline
);
3314 column
->DrawField(field
, destRect
, this);
3315 #if CONSTRAIN_CLIPPING_REGION
3320 if (fFocusRow
== row
&& !fEditMode
&& fMasterView
->IsFocus()
3321 && Window()->IsActive()) {
3322 SetHighColor(fMasterView
->Color(B_COLOR_ROW_DIVIDER
));
3323 StrokeRect(BRect(0, destRect
.top
, 10000.0, destRect
.bottom
));
3332 OutlineView::Draw(BRect invalidBounds
)
3335 BRegion invalidRegion
;
3336 GetClippingRegion(&invalidRegion
);
3343 bool tintedLine
= true;
3344 int32 numColumns
= fColumns
->CountItems();
3345 for (RecursiveOutlineIterator
iterator(&fRows
); iterator
.CurrentRow();
3346 iterator
.GoToNext()) {
3347 BRow
* row
= iterator
.CurrentRow();
3348 if (line
> invalidBounds
.bottom
)
3351 tintedLine
= !tintedLine
;
3352 float rowHeight
= row
->Height();
3354 if (line
>= invalidBounds
.top
- rowHeight
) {
3355 bool isFirstColumn
= true;
3356 float fieldLeftEdge
= MAX(kLeftMargin
, fMasterView
->LatchWidth());
3358 // setup background color
3360 if (row
->fNextSelected
!= 0) {
3361 if (Window()->IsActive()) {
3363 lowColor
= fMasterView
->Color(B_COLOR_EDIT_BACKGROUND
);
3365 lowColor
= fMasterView
->Color(B_COLOR_SELECTION
);
3368 lowColor
= fMasterView
->Color(B_COLOR_NON_FOCUS_SELECTION
);
3370 lowColor
= fMasterView
->Color(B_COLOR_BACKGROUND
);
3372 lowColor
= tint_color(lowColor
, kTintedLineTint
);
3374 for (int columnIndex
= 0; columnIndex
< numColumns
; columnIndex
++) {
3375 BColumn
* column
= (BColumn
*) fColumns
->ItemAt(columnIndex
);
3376 if (!column
->IsVisible())
3379 if (!isFirstColumn
&& fieldLeftEdge
> invalidBounds
.right
)
3382 if (fieldLeftEdge
+ column
->Width() >= invalidBounds
.left
) {
3383 BRect
fullRect(fieldLeftEdge
, line
,
3384 fieldLeftEdge
+ column
->Width(), line
+ rowHeight
);
3386 bool clippedFirstColumn
= false;
3387 // This happens when a column is indented past the
3388 // beginning of the next column.
3390 SetHighColor(lowColor
);
3392 BRect
destRect(fullRect
);
3393 if (isFirstColumn
) {
3394 fullRect
.left
-= fMasterView
->LatchWidth();
3395 destRect
.left
+= iterator
.CurrentLevel()
3396 * kOutlineLevelIndent
;
3397 if (destRect
.left
>= destRect
.right
) {
3399 FillRect(BRect(0, line
, fieldLeftEdge
3400 + column
->Width(), line
+ rowHeight
));
3401 clippedFirstColumn
= true;
3404 FillRect(BRect(0, line
, MAX(kLeftMargin
,
3405 fMasterView
->LatchWidth()), line
+ row
->Height()));
3410 if (!clippedFirstColumn
3411 && invalidRegion
.Intersects(fullRect
)) {
3413 if (!clippedFirstColumn
) {
3415 FillRect(fullRect
); // Using color set above
3417 // Draw the latch widget if it has one.
3418 if (isFirstColumn
) {
3419 if (row
== fTargetRow
3420 && fCurrentState
== LATCH_CLICKED
) {
3421 // Note that this only occurs if the user is
3422 // holding down a latch while items are added
3423 // in the background.
3426 GetMouse(&pos
, &buttons
);
3427 if (fLatchRect
.Contains(pos
)) {
3428 fMasterView
->DrawLatch(this, fLatchRect
,
3429 B_PRESSED_LATCH
, fTargetRow
);
3431 fMasterView
->DrawLatch(this, fLatchRect
,
3432 row
->fIsExpanded
? B_OPEN_LATCH
3433 : B_CLOSED_LATCH
, fTargetRow
);
3436 LatchType pos
= B_NO_LATCH
;
3437 if (row
->HasLatch())
3438 pos
= row
->fIsExpanded
? B_OPEN_LATCH
3441 fMasterView
->DrawLatch(this,
3443 - fMasterView
->LatchWidth(),
3444 destRect
.top
, destRect
.left
,
3445 destRect
.bottom
), pos
, row
);
3449 SetHighColor(fMasterView
->HighColor());
3450 // The master view just holds the high color for us.
3451 SetLowColor(lowColor
);
3453 BField
* field
= row
->GetField(column
->fFieldID
);
3455 #if CONSTRAIN_CLIPPING_REGION
3456 BRegion
clipRegion(destRect
);
3458 ConstrainClippingRegion(&clipRegion
);
3460 SetHighColor(fMasterView
->Color(
3461 row
->fNextSelected
? B_COLOR_SELECTION_TEXT
3463 float baseline
= floor(destRect
.top
+ fh
.ascent
3464 + (destRect
.Height() + 1
3465 - (fh
.ascent
+fh
.descent
)) / 2);
3466 MovePenTo(destRect
.left
+ 8, baseline
);
3467 column
->DrawField(field
, destRect
, this);
3468 #if CONSTRAIN_CLIPPING_REGION
3475 isFirstColumn
= false;
3476 fieldLeftEdge
+= column
->Width() + 1;
3479 if (fieldLeftEdge
<= invalidBounds
.right
) {
3480 SetHighColor(lowColor
);
3481 FillRect(BRect(fieldLeftEdge
, line
, invalidBounds
.right
,
3486 // indicate the keyboard focus row
3487 if (fFocusRow
== row
&& !fEditMode
&& fMasterView
->IsFocus()
3488 && Window()->IsActive()) {
3489 SetHighColor(fMasterView
->Color(B_COLOR_ROW_DIVIDER
));
3490 StrokeRect(BRect(0, line
, 10000.0, line
+ rowHeight
));
3493 line
+= rowHeight
+ 1;
3496 if (line
<= invalidBounds
.bottom
) {
3497 // fill background below last item
3498 SetHighColor(fMasterView
->Color(B_COLOR_BACKGROUND
));
3499 FillRect(BRect(invalidBounds
.left
, line
, invalidBounds
.right
,
3500 invalidBounds
.bottom
));
3503 // Draw the drop target line
3504 if (fDropHighlightY
!= -1) {
3505 InvertRect(BRect(0, fDropHighlightY
- kDropHighlightLineHeight
/ 2,
3506 1000000, fDropHighlightY
+ kDropHighlightLineHeight
/ 2));
3512 OutlineView::FindRow(float ypos
, int32
* _rowIndent
, float* _top
)
3514 if (_rowIndent
&& _top
) {
3516 for (RecursiveOutlineIterator
iterator(&fRows
); iterator
.CurrentRow();
3517 iterator
.GoToNext()) {
3519 BRow
* row
= iterator
.CurrentRow();
3523 float rowHeight
= row
->Height();
3524 if (ypos
<= line
+ rowHeight
) {
3526 *_rowIndent
= iterator
.CurrentLevel();
3530 line
+= rowHeight
+ 1;
3537 void OutlineView::SetMouseTrackingEnabled(bool enabled
)
3539 fTrackMouse
= enabled
;
3540 if (!enabled
&& fDropHighlightY
!= -1) {
3541 // Erase the old target line
3542 InvertRect(BRect(0, fDropHighlightY
- kDropHighlightLineHeight
/ 2,
3543 1000000, fDropHighlightY
+ kDropHighlightLineHeight
/ 2));
3544 fDropHighlightY
= -1;
3550 // Note that this interaction is not totally safe. If items are added to
3551 // the list in the background, the widget rect will be incorrect, possibly
3552 // resulting in drawing glitches. The code that adds items needs to be a little smarter
3553 // about invalidating state.
3556 OutlineView::MouseDown(BPoint position
)
3559 fMasterView
->MakeFocus(true);
3561 // Check to see if the user is clicking on a widget to open a section
3563 bool reset_click_count
= false;
3566 BRow
* row
= FindRow(position
.y
, &indent
, &rowTop
);
3569 // Update fCurrentField
3570 bool handle_field
= false;
3571 BField
* new_field
= 0;
3573 BColumn
* new_column
= 0;
3576 if (position
.y
>= 0) {
3577 if (position
.x
>= 0) {
3579 for (int32 c
= 0; c
< fMasterView
->CountColumns(); c
++) {
3580 new_column
= fMasterView
->ColumnAt(c
);
3581 if (!new_column
->IsVisible())
3583 if ((MAX(kLeftMargin
, fMasterView
->LatchWidth()) + x
)
3584 + new_column
->Width() >= position
.x
) {
3585 if (new_column
->WantsEvents()) {
3586 new_field
= row
->GetField(c
);
3588 FindRect(new_row
,&new_rect
);
3589 new_rect
.left
= MAX(kLeftMargin
,
3590 fMasterView
->LatchWidth()) + x
;
3591 new_rect
.right
= new_rect
.left
3592 + new_column
->Width() - 1;
3593 handle_field
= true;
3597 x
+= new_column
->Width();
3602 // Handle mouse down
3605 fFieldRect
= new_rect
;
3606 fCurrentColumn
= new_column
;
3607 fCurrentRow
= new_row
;
3608 fCurrentField
= new_field
;
3609 fCurrentCode
= B_INSIDE_VIEW
;
3610 BMessage
* message
= Window()->CurrentMessage();
3612 message
->FindInt32("buttons", &buttons
);
3613 fCurrentColumn
->MouseDown(fMasterView
, fCurrentRow
,
3614 fCurrentField
, fFieldRect
, position
, buttons
);
3620 fTargetRowTop
= rowTop
;
3621 FindVisibleRect(fFocusRow
, &fFocusRowRect
);
3623 float leftWidgetBoundry
= indent
* kOutlineLevelIndent
3624 + MAX(kLeftMargin
, fMasterView
->LatchWidth())
3625 - fMasterView
->LatchWidth();
3626 fLatchRect
.Set(leftWidgetBoundry
, rowTop
, leftWidgetBoundry
3627 + fMasterView
->LatchWidth(), rowTop
+ row
->Height());
3628 if (fLatchRect
.Contains(position
) && row
->HasLatch()) {
3629 fCurrentState
= LATCH_CLICKED
;
3630 if (fTargetRow
->fNextSelected
!= 0)
3631 SetHighColor(fMasterView
->Color(B_COLOR_SELECTION
));
3633 SetHighColor(fMasterView
->Color(B_COLOR_BACKGROUND
));
3635 FillRect(fLatchRect
);
3636 if (fLatchRect
.Contains(position
)) {
3637 fMasterView
->DrawLatch(this, fLatchRect
, B_PRESSED_LATCH
,
3640 fMasterView
->DrawLatch(this, fLatchRect
,
3641 fTargetRow
->fIsExpanded
? B_OPEN_LATCH
3642 : B_CLOSED_LATCH
, row
);
3645 Invalidate(fFocusRowRect
);
3646 fFocusRow
= fTargetRow
;
3647 FindVisibleRect(fFocusRow
, &fFocusRowRect
);
3649 ASSERT(fTargetRow
!= 0);
3651 if ((modifiers() & B_CONTROL_KEY
) == 0)
3654 if ((modifiers() & B_SHIFT_KEY
) != 0 && fFirstSelectedItem
!= 0
3655 && fSelectionMode
== B_MULTIPLE_SELECTION_LIST
) {
3656 SelectRange(fFirstSelectedItem
, fTargetRow
);
3659 if (fTargetRow
->fNextSelected
!= 0) {
3661 fTargetRow
->fNextSelected
->fPrevSelected
3662 = fTargetRow
->fPrevSelected
;
3663 fTargetRow
->fPrevSelected
->fNextSelected
3664 = fTargetRow
->fNextSelected
;
3665 fTargetRow
->fPrevSelected
= 0;
3666 fTargetRow
->fNextSelected
= 0;
3667 fFirstSelectedItem
= NULL
;
3670 if (fSelectionMode
== B_SINGLE_SELECTION_LIST
)
3673 fTargetRow
->fNextSelected
3674 = fSelectionListDummyHead
.fNextSelected
;
3675 fTargetRow
->fPrevSelected
3676 = &fSelectionListDummyHead
;
3677 fTargetRow
->fNextSelected
->fPrevSelected
= fTargetRow
;
3678 fTargetRow
->fPrevSelected
->fNextSelected
= fTargetRow
;
3679 fFirstSelectedItem
= fTargetRow
;
3682 Invalidate(BRect(fVisibleRect
.left
, fTargetRowTop
,
3684 fTargetRowTop
+ fTargetRow
->Height()));
3687 fCurrentState
= ROW_CLICKED
;
3688 if (fLastSelectedItem
!= fTargetRow
)
3689 reset_click_count
= true;
3690 fLastSelectedItem
= fTargetRow
;
3691 fMasterView
->SelectionChanged();
3696 SetMouseEventMask(B_POINTER_EVENTS
, B_LOCK_WINDOW_FOCUS
|
3697 B_NO_POINTER_HISTORY
);
3699 } else if (fFocusRow
!= 0) {
3700 // User clicked in open space, unhighlight focus row.
3701 FindVisibleRect(fFocusRow
, &fFocusRowRect
);
3703 Invalidate(fFocusRowRect
);
3706 // We stash the click counts here because the 'clicks' field
3707 // is not in the CurrentMessage() when MouseUp is called... ;(
3708 if (reset_click_count
)
3711 Window()->CurrentMessage()->FindInt32("clicks", &fClickCount
);
3712 fClickPoint
= position
;
3718 OutlineView::MouseMoved(BPoint position
, uint32
/*transit*/,
3719 const BMessage
* /*dragMessage*/)
3722 // Update fCurrentField
3723 bool handle_field
= false;
3724 BField
* new_field
= 0;
3726 BColumn
* new_column
= 0;
3727 BRect
new_rect(0,0,0,0);
3728 if (position
.y
>=0 ) {
3731 BRow
* row
= FindRow(position
.y
, &indent
, &top
);
3732 if (row
&& position
.x
>=0 ) {
3734 for (int32 c
=0;c
<fMasterView
->CountColumns();c
++) {
3735 new_column
= fMasterView
->ColumnAt(c
);
3736 if (!new_column
->IsVisible())
3738 if ((MAX(kLeftMargin
,
3739 fMasterView
->LatchWidth()) + x
) + new_column
->Width()
3742 if(new_column
->WantsEvents()) {
3743 new_field
= row
->GetField(c
);
3745 FindRect(new_row
,&new_rect
);
3746 new_rect
.left
= MAX(kLeftMargin
,
3747 fMasterView
->LatchWidth()) + x
;
3748 new_rect
.right
= new_rect
.left
3749 + new_column
->Width() - 1;
3750 handle_field
= true;
3754 x
+= new_column
->Width();
3759 // Handle mouse moved
3761 if (new_field
!= fCurrentField
) {
3762 if (fCurrentField
) {
3763 fCurrentColumn
->MouseMoved(fMasterView
, fCurrentRow
,
3764 fCurrentField
, fFieldRect
, position
, 0,
3765 fCurrentCode
= B_EXITED_VIEW
);
3767 fCurrentColumn
= new_column
;
3768 fCurrentRow
= new_row
;
3769 fCurrentField
= new_field
;
3770 fFieldRect
= new_rect
;
3771 if (fCurrentField
) {
3772 fCurrentColumn
->MouseMoved(fMasterView
, fCurrentRow
,
3773 fCurrentField
, fFieldRect
, position
, 0,
3774 fCurrentCode
= B_ENTERED_VIEW
);
3777 if (fCurrentField
) {
3778 fCurrentColumn
->MouseMoved(fMasterView
, fCurrentRow
,
3779 fCurrentField
, fFieldRect
, position
, 0,
3780 fCurrentCode
= B_INSIDE_VIEW
);
3784 if (fCurrentField
) {
3785 fCurrentColumn
->MouseMoved(fMasterView
, fCurrentRow
,
3786 fCurrentField
, fFieldRect
, position
, 0,
3787 fCurrentCode
= B_EXITED_VIEW
);
3794 if (fCurrentField
) {
3795 if (fFieldRect
.Contains(position
)) {
3796 if (fCurrentCode
== B_OUTSIDE_VIEW
3797 || fCurrentCode
== B_EXITED_VIEW
) {
3798 fCurrentColumn
->MouseMoved(fMasterView
, fCurrentRow
,
3799 fCurrentField
, fFieldRect
, position
, 1,
3800 fCurrentCode
= B_ENTERED_VIEW
);
3802 fCurrentColumn
->MouseMoved(fMasterView
, fCurrentRow
,
3803 fCurrentField
, fFieldRect
, position
, 1,
3804 fCurrentCode
= B_INSIDE_VIEW
);
3807 if (fCurrentCode
== B_INSIDE_VIEW
3808 || fCurrentCode
== B_ENTERED_VIEW
) {
3809 fCurrentColumn
->MouseMoved(fMasterView
, fCurrentRow
,
3810 fCurrentField
, fFieldRect
, position
, 1,
3811 fCurrentCode
= B_EXITED_VIEW
);
3813 fCurrentColumn
->MouseMoved(fMasterView
, fCurrentRow
,
3814 fCurrentField
, fFieldRect
, position
, 1,
3815 fCurrentCode
= B_OUTSIDE_VIEW
);
3823 switch (fCurrentState
) {
3825 if (fTargetRow
->fNextSelected
!= 0)
3826 SetHighColor(fMasterView
->Color(B_COLOR_SELECTION
));
3828 SetHighColor(fMasterView
->Color(B_COLOR_BACKGROUND
));
3830 FillRect(fLatchRect
);
3831 if (fLatchRect
.Contains(position
)) {
3832 fMasterView
->DrawLatch(this, fLatchRect
, B_PRESSED_LATCH
,
3835 fMasterView
->DrawLatch(this, fLatchRect
,
3836 fTargetRow
->fIsExpanded
? B_OPEN_LATCH
: B_CLOSED_LATCH
,
3842 if (abs((int)(position
.x
- fClickPoint
.x
)) > kRowDragSensitivity
3843 || abs((int)(position
.y
- fClickPoint
.y
))
3844 > kRowDragSensitivity
) {
3845 fCurrentState
= DRAGGING_ROWS
;
3846 fMasterView
->InitiateDrag(fClickPoint
,
3847 fTargetRow
->fNextSelected
!= 0);
3855 if (fTrackMouse
/*&& message*/) {
3856 if (fVisibleRect
.Contains(position
)) {
3859 BRow
* target
= FindRow(position
.y
, &indent
, &top
);
3861 SetFocusRow(target
, true);
3869 if (fTrackMouse
/*&& message*/) {
3870 // Draw a highlight line...
3871 if (fVisibleRect
.Contains(position
)) {
3874 BRow
* target
= FindRow(position
.y
, &indent
, &top
);
3875 if (target
== fRollOverRow
)
3879 FindRect(fRollOverRow
, &rect
);
3882 fRollOverRow
= target
;
3884 SetFocusRow(fRollOverRow
,false);
3887 SetDrawingMode(B_OP_BLEND
);
3888 SetHighColor(255, 255, 255, 255);
3890 FindRect(fRollOverRow
, &rect
);
3898 FindRect(fRollOverRow
, &rect
);
3900 fRollOverRow
= NULL
;
3911 OutlineView::MouseUp(BPoint position
)
3913 if (fCurrentField
) {
3914 fCurrentColumn
->MouseUp(fMasterView
, fCurrentRow
, fCurrentField
);
3921 switch (fCurrentState
) {
3923 if (fLatchRect
.Contains(position
)) {
3924 fMasterView
->ExpandOrCollapse(fTargetRow
,
3925 !fTargetRow
->fIsExpanded
);
3928 Invalidate(fLatchRect
);
3929 fCurrentState
= INACTIVE
;
3934 && abs((int)fClickPoint
.x
- (int)position
.x
)
3935 < kDoubleClickMoveSensitivity
3936 && abs((int)fClickPoint
.y
- (int)position
.y
)
3937 < kDoubleClickMoveSensitivity
) {
3938 fMasterView
->ItemInvoked();
3940 fCurrentState
= INACTIVE
;
3944 fCurrentState
= INACTIVE
;
3948 if (fDropHighlightY
!= -1) {
3950 fDropHighlightY
- kDropHighlightLineHeight
/ 2,
3951 1000000, fDropHighlightY
+ kDropHighlightLineHeight
/ 2));
3952 // Erase the old target line
3953 fDropHighlightY
= -1;
3960 OutlineView::MessageReceived(BMessage
* message
)
3962 if (message
->WasDropped()) {
3963 fMasterView
->MessageDropped(message
,
3964 ConvertFromScreen(message
->DropPoint()));
3966 BView::MessageReceived(message
);
3972 OutlineView::ChangeFocusRow(bool up
, bool updateSelection
,
3973 bool addToCurrentSelection
)
3977 float newRowPos
= 0;
3978 float verticalScroll
= 0;
3981 // A row currently has the focus, get information about it
3982 newRowPos
= fFocusRowRect
.top
+ (up
? -4 : fFocusRow
->Height() + 4);
3983 if (newRowPos
< fVisibleRect
.top
+ 20)
3984 verticalScroll
= newRowPos
- 20;
3985 else if (newRowPos
> fVisibleRect
.bottom
- 20)
3986 verticalScroll
= newRowPos
- fVisibleRect
.Height() + 20;
3988 newRowPos
= fVisibleRect
.top
+ 2;
3989 // no row is currently focused, set this to the top of the window
3990 // so we will select the first visible item in the list.
3992 BRow
* newRow
= FindRow(newRowPos
, &indent
, &top
);
3995 fFocusRowRect
.right
= 10000;
3996 Invalidate(fFocusRowRect
);
3998 BRow
* oldFocusRow
= fFocusRow
;
4000 fFocusRowRect
.top
= top
;
4001 fFocusRowRect
.left
= 0;
4002 fFocusRowRect
.right
= 10000;
4003 fFocusRowRect
.bottom
= fFocusRowRect
.top
+ fFocusRow
->Height();
4004 Invalidate(fFocusRowRect
);
4006 if (updateSelection
) {
4007 if (!addToCurrentSelection
4008 || fSelectionMode
== B_SINGLE_SELECTION_LIST
) {
4012 // if the focus row isn't selected, add it to the selection
4013 if (fFocusRow
->fNextSelected
== 0) {
4014 fFocusRow
->fNextSelected
4015 = fSelectionListDummyHead
.fNextSelected
;
4016 fFocusRow
->fPrevSelected
= &fSelectionListDummyHead
;
4017 fFocusRow
->fNextSelected
->fPrevSelected
= fFocusRow
;
4018 fFocusRow
->fPrevSelected
->fNextSelected
= fFocusRow
;
4019 } else if (oldFocusRow
!= NULL
4020 && fSelectionListDummyHead
.fNextSelected
== oldFocusRow
4021 && (((IndexOf(oldFocusRow
->fNextSelected
)
4022 < IndexOf(oldFocusRow
)) == up
)
4023 || fFocusRow
== oldFocusRow
->fNextSelected
)) {
4024 // if the focus row is selected, if:
4025 // 1. the previous focus row is last in the selection
4026 // 2a. the next selected row is now the focus row
4027 // 2b. or the next selected row is beyond the focus row
4028 // in the move direction
4029 // then deselect the previous focus row
4030 fSelectionListDummyHead
.fNextSelected
4031 = oldFocusRow
->fNextSelected
;
4032 if (fSelectionListDummyHead
.fNextSelected
!= NULL
) {
4033 fSelectionListDummyHead
.fNextSelected
->fPrevSelected
4034 = &fSelectionListDummyHead
;
4035 oldFocusRow
->fNextSelected
= NULL
;
4037 oldFocusRow
->fPrevSelected
= NULL
;
4040 fLastSelectedItem
= fFocusRow
;
4043 Invalidate(fFocusRowRect
);
4045 if (verticalScroll
!= 0) {
4046 BScrollBar
* vScrollBar
= ScrollBar(B_VERTICAL
);
4048 vScrollBar
->GetRange(&min
, &max
);
4049 if (verticalScroll
< min
)
4050 verticalScroll
= min
;
4051 else if (verticalScroll
> max
)
4052 verticalScroll
= max
;
4054 vScrollBar
->SetValue(verticalScroll
);
4057 if (newRow
&& updateSelection
)
4058 fMasterView
->SelectionChanged();
4063 OutlineView::MoveFocusToVisibleRect()
4066 ChangeFocusRow(true, true, false);
4071 OutlineView::CurrentSelection(BRow
* lastSelected
) const
4074 if (lastSelected
== 0)
4075 row
= fSelectionListDummyHead
.fNextSelected
;
4077 row
= lastSelected
->fNextSelected
;
4080 if (row
== &fSelectionListDummyHead
)
4088 OutlineView::ToggleFocusRowSelection(bool selectRange
)
4093 if (selectRange
&& fSelectionMode
== B_MULTIPLE_SELECTION_LIST
)
4094 SelectRange(fLastSelectedItem
, fFocusRow
);
4096 if (fFocusRow
->fNextSelected
!= 0) {
4098 fFocusRow
->fNextSelected
->fPrevSelected
= fFocusRow
->fPrevSelected
;
4099 fFocusRow
->fPrevSelected
->fNextSelected
= fFocusRow
->fNextSelected
;
4100 fFocusRow
->fPrevSelected
= 0;
4101 fFocusRow
->fNextSelected
= 0;
4104 if (fSelectionMode
== B_SINGLE_SELECTION_LIST
)
4107 fFocusRow
->fNextSelected
= fSelectionListDummyHead
.fNextSelected
;
4108 fFocusRow
->fPrevSelected
= &fSelectionListDummyHead
;
4109 fFocusRow
->fNextSelected
->fPrevSelected
= fFocusRow
;
4110 fFocusRow
->fPrevSelected
->fNextSelected
= fFocusRow
;
4114 fLastSelectedItem
= fFocusRow
;
4115 fMasterView
->SelectionChanged();
4116 Invalidate(fFocusRowRect
);
4121 OutlineView::ToggleFocusRowOpen()
4124 fMasterView
->ExpandOrCollapse(fFocusRow
, !fFocusRow
->fIsExpanded
);
4129 OutlineView::ExpandOrCollapse(BRow
* parentRow
, bool expand
)
4131 // TODO: Could use CopyBits here to speed things up.
4133 if (parentRow
== NULL
)
4136 if (parentRow
->fIsExpanded
== expand
)
4139 parentRow
->fIsExpanded
= expand
;
4142 if (FindRect(parentRow
, &parentRect
)) {
4143 // Determine my new height
4144 float subTreeHeight
= 0.0;
4145 if (parentRow
->fIsExpanded
)
4146 for (RecursiveOutlineIterator
iterator(parentRow
->fChildList
);
4147 iterator
.CurrentRow();
4151 subTreeHeight
+= iterator
.CurrentRow()->Height()+1;
4154 for (RecursiveOutlineIterator
iterator(parentRow
->fChildList
);
4155 iterator
.CurrentRow();
4159 subTreeHeight
-= iterator
.CurrentRow()->Height()+1;
4161 fItemsHeight
+= subTreeHeight
;
4163 // Adjust focus row if necessary.
4164 if (FindRect(fFocusRow
, &fFocusRowRect
) == false) {
4165 // focus row is in a subtree that has collapsed,
4166 // move it up to the parent.
4167 fFocusRow
= parentRow
;
4168 FindRect(fFocusRow
, &fFocusRowRect
);
4171 Invalidate(BRect(0, parentRect
.top
, fVisibleRect
.right
,
4172 fVisibleRect
.bottom
));
4173 FixScrollBar(false);
4178 OutlineView::RemoveRow(BRow
* row
)
4184 bool parentIsVisible
;
4185 FindParent(row
, &parentRow
, &parentIsVisible
);
4186 // NOTE: This could be a root row without a parent, in which case
4187 // it is always visible, though.
4189 // Adjust height for the visible sub-tree that is going to be removed.
4190 float subTreeHeight
= 0.0f
;
4191 if (parentIsVisible
&& (parentRow
== NULL
|| parentRow
->fIsExpanded
)) {
4192 // The row itself is visible at least.
4193 subTreeHeight
= row
->Height() + 1;
4194 if (row
->fIsExpanded
) {
4195 // Adjust for the height of visible sub-items as well.
4196 // (By default, the iterator follows open branches only.)
4197 for (RecursiveOutlineIterator
iterator(row
->fChildList
);
4198 iterator
.CurrentRow(); iterator
.GoToNext())
4199 subTreeHeight
+= iterator
.CurrentRow()->Height() + 1;
4202 if (FindRect(row
, &invalid
)) {
4203 invalid
.bottom
= Bounds().bottom
;
4204 if (invalid
.IsValid())
4205 Invalidate(invalid
);
4209 fItemsHeight
-= subTreeHeight
;
4211 FixScrollBar(false);
4214 if (FindRow(fVisibleRect
.top
, &indent
, &top
) == NULL
&& ScrollBar(B_VERTICAL
) != NULL
) {
4215 // after removing this row, no rows are actually visible any more,
4216 // force a scroll to make them visible again
4217 if (fItemsHeight
> fVisibleRect
.Height())
4218 ScrollBy(0.0, fItemsHeight
- fVisibleRect
.Height() - Bounds().top
);
4220 ScrollBy(0.0, -Bounds().top
);
4222 if (parentRow
!= NULL
) {
4223 parentRow
->fChildList
->RemoveItem(row
);
4224 if (parentRow
->fChildList
->CountItems() == 0) {
4225 delete parentRow
->fChildList
;
4226 parentRow
->fChildList
= 0;
4227 // It was the last child row of the parent, which also means the
4228 // latch disappears.
4229 BRect parentRowRect
;
4230 if (parentIsVisible
&& FindRect(parentRow
, &parentRowRect
))
4231 Invalidate(parentRowRect
);
4234 fRows
.RemoveItem(row
);
4236 // Adjust focus row if necessary.
4237 if (fFocusRow
&& !FindRect(fFocusRow
, &fFocusRowRect
)) {
4238 // focus row is in a subtree that is gone, move it up to the parent.
4239 fFocusRow
= parentRow
;
4241 FindRect(fFocusRow
, &fFocusRowRect
);
4244 // Remove this from the selection if necessary
4245 if (row
->fNextSelected
!= 0) {
4246 row
->fNextSelected
->fPrevSelected
= row
->fPrevSelected
;
4247 row
->fPrevSelected
->fNextSelected
= row
->fNextSelected
;
4248 row
->fPrevSelected
= 0;
4249 row
->fNextSelected
= 0;
4250 fMasterView
->SelectionChanged();
4260 OutlineView::RowList()
4267 OutlineView::UpdateRow(BRow
* row
)
4270 // Determine if this row has changed its sort order
4271 BRow
* parentRow
= NULL
;
4272 bool parentIsVisible
= false;
4273 FindParent(row
, &parentRow
, &parentIsVisible
);
4275 BRowContainer
* list
= (parentRow
== NULL
) ? &fRows
: parentRow
->fChildList
;
4278 int32 rowIndex
= list
->IndexOf(row
);
4279 ASSERT(rowIndex
>= 0);
4280 ASSERT(list
->ItemAt(rowIndex
) == row
);
4282 bool rowMoved
= false;
4283 if (rowIndex
> 0 && CompareRows(list
->ItemAt(rowIndex
- 1), row
) > 0)
4286 if (rowIndex
< list
->CountItems() - 1 && CompareRows(list
->ItemAt(rowIndex
+ 1),
4291 // Sort location of this row has changed.
4292 // Remove and re-add in the right spot
4293 SortList(list
, parentIsVisible
&& (parentRow
== NULL
|| parentRow
->fIsExpanded
));
4294 } else if (parentIsVisible
&& (parentRow
== NULL
|| parentRow
->fIsExpanded
)) {
4296 if (FindVisibleRect(row
, &invalidRect
))
4297 Invalidate(invalidRect
);
4305 OutlineView::AddRow(BRow
* row
, int32 Index
, BRow
* parentRow
)
4310 row
->fParent
= parentRow
;
4312 if (fMasterView
->SortingEnabled() && !fSortColumns
->IsEmpty()) {
4313 // Ignore index here.
4315 if (parentRow
->fChildList
== NULL
)
4316 parentRow
->fChildList
= new BRowContainer
;
4318 AddSorted(parentRow
->fChildList
, row
);
4320 AddSorted(&fRows
, row
);
4322 // Note, a -1 index implies add to end if sorting is not enabled
4324 if (parentRow
->fChildList
== 0)
4325 parentRow
->fChildList
= new BRowContainer
;
4327 if (Index
< 0 || Index
> parentRow
->fChildList
->CountItems())
4328 parentRow
->fChildList
->AddItem(row
);
4330 parentRow
->fChildList
->AddItem(row
, Index
);
4332 if (Index
< 0 || Index
>= fRows
.CountItems())
4335 fRows
.AddItem(row
, Index
);
4339 if (parentRow
== 0 || parentRow
->fIsExpanded
)
4340 fItemsHeight
+= row
->Height() + 1;
4342 FixScrollBar(false);
4345 bool newRowIsInOpenBranch
= FindRect(row
, &newRowRect
);
4347 if (fFocusRow
&& fFocusRowRect
.top
> newRowRect
.bottom
) {
4348 // The focus row has moved.
4349 Invalidate(fFocusRowRect
);
4350 FindRect(fFocusRow
, &fFocusRowRect
);
4351 Invalidate(fFocusRowRect
);
4354 if (newRowIsInOpenBranch
) {
4355 if (fCurrentState
== INACTIVE
) {
4356 if (newRowRect
.bottom
< fVisibleRect
.top
) {
4357 // The new row is totally above the current viewport, move
4358 // everything down and redraw the first line.
4359 BRect
source(fVisibleRect
);
4360 BRect
dest(fVisibleRect
);
4361 source
.bottom
-= row
->Height() + 1;
4362 dest
.top
+= row
->Height() + 1;
4363 CopyBits(source
, dest
);
4364 Invalidate(BRect(fVisibleRect
.left
, fVisibleRect
.top
, fVisibleRect
.right
,
4365 fVisibleRect
.top
+ newRowRect
.Height()));
4366 } else if (newRowRect
.top
< fVisibleRect
.bottom
) {
4367 // New item is somewhere in the current region. Scroll everything
4368 // beneath it down and invalidate just the new row rect.
4369 BRect
source(fVisibleRect
.left
, newRowRect
.top
, fVisibleRect
.right
,
4370 fVisibleRect
.bottom
- newRowRect
.Height());
4372 dest
.OffsetBy(0, newRowRect
.Height() + 1);
4373 CopyBits(source
, dest
);
4374 Invalidate(newRowRect
);
4375 } // otherwise, this is below the currently visible region
4377 // Adding the item may have caused the item that the user is currently
4378 // selected to move. This would cause annoying drawing and interaction
4379 // bugs, as the position of that item is cached. If this happens, resize
4380 // the scroll bar, then scroll back so the selected item is in view.
4382 if (FindRect(fTargetRow
, &targetRect
)) {
4383 float delta
= targetRect
.top
- fTargetRowTop
;
4385 // This causes a jump because ScrollBy will copy a chunk of the view.
4386 // Since the actual contents of the view have been offset, we don't
4387 // want this, we just want to change the virtual origin of the window.
4388 // Constrain the clipping region so everything is clipped out so no
4391 // xxx this currently doesn't work if the scroll bars aren't enabled.
4392 // everything will still move anyway. A minor annoyance.
4393 BRegion emptyRegion
;
4394 ConstrainClippingRegion(&emptyRegion
);
4398 ConstrainClippingRegion(NULL
);
4400 fTargetRowTop
+= delta
;
4401 fClickPoint
.y
+= delta
;
4402 fLatchRect
.OffsetBy(0, delta
);
4408 // If the parent was previously childless, it will need to have a latch
4411 if (parentRow
&& parentRow
->fChildList
->CountItems() == 1
4412 && FindVisibleRect(parentRow
, &parentRect
))
4413 Invalidate(parentRect
);
4418 OutlineView::FixScrollBar(bool scrollToFit
)
4420 BScrollBar
* vScrollBar
= ScrollBar(B_VERTICAL
);
4422 if (fItemsHeight
> fVisibleRect
.Height()) {
4423 float maxScrollBarValue
= fItemsHeight
- fVisibleRect
.Height();
4424 vScrollBar
->SetProportion(fVisibleRect
.Height() / fItemsHeight
);
4426 // If the user is scrolled down too far when making the range smaller, the list
4427 // will jump suddenly, which is undesirable. In this case, don't fix the scroll
4428 // bar here. In ScrollTo, it checks to see if this has occured, and will
4429 // fix the scroll bars sneakily if the user has scrolled up far enough.
4430 if (scrollToFit
|| vScrollBar
->Value() <= maxScrollBarValue
) {
4431 vScrollBar
->SetRange(0.0, maxScrollBarValue
);
4432 vScrollBar
->SetSteps(20.0, fVisibleRect
.Height());
4434 } else if (vScrollBar
->Value() == 0.0 || fItemsHeight
== 0.0)
4435 vScrollBar
->SetRange(0.0, 0.0); // disable scroll bar.
4441 OutlineView::AddSorted(BRowContainer
* list
, BRow
* row
)
4444 // Find general vicinity with binary search.
4446 int32 upper
= list
->CountItems()-1;
4447 while( lower
< upper
) {
4448 int32 middle
= lower
+ (upper
-lower
+1)/2;
4449 int32 cmp
= CompareRows(row
, list
->ItemAt(middle
));
4450 if( cmp
< 0 ) upper
= middle
-1;
4451 else if( cmp
> 0 ) lower
= middle
+1;
4452 else lower
= upper
= middle
;
4455 // At this point, 'upper' and 'lower' at the last found item.
4456 // Arbitrarily use 'upper' and determine the final insertion
4457 // point -- either before or after this item.
4458 if( upper
< 0 ) upper
= 0;
4459 else if( upper
< list
->CountItems() ) {
4460 if( CompareRows(row
, list
->ItemAt(upper
)) > 0 ) upper
++;
4463 if (upper
>= list
->CountItems())
4464 list
->AddItem(row
); // Adding to end.
4466 list
->AddItem(row
, upper
); // Insert
4472 OutlineView::CompareRows(BRow
* row1
, BRow
* row2
)
4474 int32
itemCount (fSortColumns
->CountItems());
4476 for (int32 index
= 0; index
< itemCount
; index
++) {
4477 BColumn
* column
= (BColumn
*) fSortColumns
->ItemAt(index
);
4479 BField
* field1
= (BField
*) row1
->GetField(column
->fFieldID
);
4480 BField
* field2
= (BField
*) row2
->GetField(column
->fFieldID
);
4481 if (field1
&& field2
)
4482 comp
= column
->CompareFields(field1
, field2
);
4484 if (!column
->fSortAscending
)
4496 OutlineView::FrameResized(float width
, float height
)
4498 fVisibleRect
.right
= fVisibleRect
.left
+ width
;
4499 fVisibleRect
.bottom
= fVisibleRect
.top
+ height
;
4501 _inherited::FrameResized(width
, height
);
4506 OutlineView::ScrollTo(BPoint position
)
4508 fVisibleRect
.OffsetTo(position
.x
, position
.y
);
4510 // In FixScrollBar, we might not have been able to change the size of
4511 // the scroll bar because the user was scrolled down too far. Take
4512 // this opportunity to sneak it in if we can.
4513 BScrollBar
* vScrollBar
= ScrollBar(B_VERTICAL
);
4514 float maxScrollBarValue
= fItemsHeight
- fVisibleRect
.Height();
4516 vScrollBar
->GetRange(&min
, &max
);
4517 if (max
!= maxScrollBarValue
&& position
.y
> maxScrollBarValue
)
4520 _inherited::ScrollTo(position
);
4525 OutlineView::VisibleRect() const
4527 return fVisibleRect
;
4532 OutlineView::FindVisibleRect(BRow
* row
, BRect
* _rect
)
4536 for (RecursiveOutlineIterator
iterator(&fRows
); iterator
.CurrentRow();
4537 iterator
.GoToNext()) {
4538 if (line
> fVisibleRect
.bottom
)
4541 if (iterator
.CurrentRow() == row
) {
4542 _rect
->Set(fVisibleRect
.left
, line
, fVisibleRect
.right
,
4543 line
+ row
->Height());
4547 line
+= iterator
.CurrentRow()->Height() + 1;
4555 OutlineView::FindRect(const BRow
* row
, BRect
* _rect
)
4558 for (RecursiveOutlineIterator
iterator(&fRows
); iterator
.CurrentRow();
4559 iterator
.GoToNext()) {
4560 if (iterator
.CurrentRow() == row
) {
4561 _rect
->Set(fVisibleRect
.left
, line
, fVisibleRect
.right
,
4562 line
+ row
->Height());
4566 line
+= iterator
.CurrentRow()->Height() + 1;
4574 OutlineView::ScrollTo(const BRow
* row
)
4577 if (FindRect(row
, &rect
)) {
4578 BRect bounds
= Bounds();
4579 if (rect
.top
< bounds
.top
)
4580 ScrollTo(BPoint(bounds
.left
, rect
.top
));
4581 else if (rect
.bottom
> bounds
.bottom
)
4582 ScrollBy(0, rect
.bottom
- bounds
.bottom
);
4588 OutlineView::DeselectAll()
4590 // Invalidate all selected rows
4592 for (RecursiveOutlineIterator
iterator(&fRows
); iterator
.CurrentRow();
4593 iterator
.GoToNext()) {
4594 if (line
> fVisibleRect
.bottom
)
4597 BRow
* row
= iterator
.CurrentRow();
4598 if (line
+ row
->Height() > fVisibleRect
.top
) {
4599 if (row
->fNextSelected
!= 0)
4600 Invalidate(BRect(fVisibleRect
.left
, line
, fVisibleRect
.right
,
4601 line
+ row
->Height()));
4604 line
+= row
->Height() + 1;
4607 // Set items not selected
4608 while (fSelectionListDummyHead
.fNextSelected
!= &fSelectionListDummyHead
) {
4609 BRow
* row
= fSelectionListDummyHead
.fNextSelected
;
4610 row
->fNextSelected
->fPrevSelected
= row
->fPrevSelected
;
4611 row
->fPrevSelected
->fNextSelected
= row
->fNextSelected
;
4612 row
->fNextSelected
= 0;
4613 row
->fPrevSelected
= 0;
4619 OutlineView::FocusRow() const
4626 OutlineView::SetFocusRow(BRow
* row
, bool Select
)
4630 AddToSelection(row
);
4632 if (fFocusRow
== row
)
4635 Invalidate(fFocusRowRect
); // invalidate previous
4637 fTargetRow
= fFocusRow
= row
;
4639 FindVisibleRect(fFocusRow
, &fFocusRowRect
);
4640 Invalidate(fFocusRowRect
); // invalidate current
4642 fFocusRowRect
.right
= 10000;
4643 fMasterView
->SelectionChanged();
4649 OutlineView::SortList(BRowContainer
* list
, bool isVisible
)
4654 = (BRow
**) BObjectList
<BRow
>::Private(list
).AsBList()->Items();
4655 int32 numItems
= list
->CountItems();
4657 for (h
= 1; h
< numItems
/ 9; h
= 3 * h
+ 1)
4660 for (;h
> 0; h
/= 3) {
4661 for (int step
= h
; step
< numItems
; step
++) {
4662 BRow
* temp
= items
[step
];
4664 for (i
= step
- h
; i
>= 0; i
-= h
) {
4665 if (CompareRows(temp
, items
[i
]) < 0)
4666 items
[i
+ h
] = items
[i
];
4671 items
[i
+ h
] = temp
;
4678 InvalidateCachedPositions();
4679 int lockCount
= Window()->CountLocks();
4680 for (int i
= 0; i
< lockCount
; i
++)
4684 if (!Window()->Lock())
4685 return false; // Window is gone...
4693 OutlineView::DeepSortThreadEntry(void* _outlineView
)
4695 ((OutlineView
*) _outlineView
)->DeepSort();
4701 OutlineView::DeepSort()
4703 struct stack_entry
{
4705 BRowContainer
* list
;
4710 stack
[stackTop
].list
= &fRows
;
4711 stack
[stackTop
].isVisible
= true;
4712 stack
[stackTop
].listIndex
= 0;
4715 if (Window()->Lock() == false)
4718 bool doneSorting
= false;
4719 while (!doneSorting
&& !fSortCancelled
) {
4721 stack_entry
* currentEntry
= &stack
[stackTop
];
4723 // xxx Can make the invalidate area smaller by finding the rect for the
4724 // parent item and using that as the top of the invalid rect.
4726 bool haveLock
= SortList(currentEntry
->list
, currentEntry
->isVisible
);
4728 return ; // window is gone.
4731 InvalidateCachedPositions();
4732 if (fCurrentState
!= INACTIVE
)
4733 fCurrentState
= INACTIVE
; // sorry...
4736 bool foundNextList
= false;
4737 while (!foundNextList
&& !fSortCancelled
) {
4738 for (int32 index
= currentEntry
->listIndex
; index
< currentEntry
->list
->CountItems();
4740 BRow
* parentRow
= currentEntry
->list
->ItemAt(index
);
4741 BRowContainer
* childList
= parentRow
->fChildList
;
4742 if (childList
!= 0) {
4743 currentEntry
->listIndex
= index
+ 1;
4745 ASSERT(stackTop
< kMaxDepth
);
4746 stack
[stackTop
].listIndex
= 0;
4747 stack
[stackTop
].list
= childList
;
4748 stack
[stackTop
].isVisible
= (currentEntry
->isVisible
&& parentRow
->fIsExpanded
);
4749 foundNextList
= true;
4754 if (!foundNextList
) {
4756 if (--stackTop
< 0) {
4761 currentEntry
= &stack
[stackTop
];
4771 OutlineView::StartSorting()
4773 // If this view is not yet attached to a window, don't start a sort thread!
4774 if (Window() == NULL
)
4777 if (fSortThread
!= B_BAD_THREAD_ID
) {
4779 if (get_thread_info(fSortThread
, &tinfo
) == B_OK
) {
4780 // Unlock window so this won't deadlock (sort thread is probably
4781 // waiting to lock window).
4783 int lockCount
= Window()->CountLocks();
4784 for (int i
= 0; i
< lockCount
; i
++)
4787 fSortCancelled
= true;
4789 wait_for_thread(fSortThread
, &status
);
4792 if (!Window()->Lock())
4793 return ; // Window is gone...
4797 fSortCancelled
= false;
4798 fSortThread
= spawn_thread(DeepSortThreadEntry
, "sort_thread", B_NORMAL_PRIORITY
, this);
4799 resume_thread(fSortThread
);
4804 OutlineView::SelectRange(BRow
* start
, BRow
* end
)
4809 if (start
== end
) // start is always selected when this is called
4812 RecursiveOutlineIterator
iterator(&fRows
, false);
4813 while (iterator
.CurrentRow() != 0) {
4814 if (iterator
.CurrentRow() == end
) {
4815 // reverse selection, swap to fix special case
4820 } else if (iterator
.CurrentRow() == start
)
4823 iterator
.GoToNext();
4827 BRow
* row
= iterator
.CurrentRow();
4829 if (row
->fNextSelected
== 0) {
4830 row
->fNextSelected
= fSelectionListDummyHead
.fNextSelected
;
4831 row
->fPrevSelected
= &fSelectionListDummyHead
;
4832 row
->fNextSelected
->fPrevSelected
= row
;
4833 row
->fPrevSelected
->fNextSelected
= row
;
4841 iterator
.GoToNext();
4844 Invalidate(); // xxx make invalidation smaller
4849 OutlineView::FindParent(BRow
* row
, BRow
** outParent
, bool* outParentIsVisible
)
4851 bool result
= false;
4852 if (row
!= NULL
&& outParent
!= NULL
) {
4853 *outParent
= row
->fParent
;
4855 if (outParentIsVisible
!= NULL
) {
4856 // Walk up the parent chain to determine if this row is visible
4857 *outParentIsVisible
= true;
4858 for (BRow
* currentRow
= row
->fParent
; currentRow
!= NULL
;
4859 currentRow
= currentRow
->fParent
) {
4860 if (!currentRow
->fIsExpanded
) {
4861 *outParentIsVisible
= false;
4867 result
= *outParent
!= NULL
;
4875 OutlineView::IndexOf(BRow
* row
)
4878 if (row
->fParent
== 0)
4879 return fRows
.IndexOf(row
);
4881 ASSERT(row
->fParent
->fChildList
);
4882 return row
->fParent
->fChildList
->IndexOf(row
);
4890 OutlineView::InvalidateCachedPositions()
4893 FindRect(fFocusRow
, &fFocusRowRect
);
4898 OutlineView::GetColumnPreferredWidth(BColumn
* column
)
4900 float preferred
= 0.0;
4901 for (RecursiveOutlineIterator
iterator(&fRows
); BRow
* row
=
4902 iterator
.CurrentRow(); iterator
.GoToNext()) {
4903 BField
* field
= row
->GetField(column
->fFieldID
);
4905 float width
= column
->GetPreferredWidth(field
, this)
4906 + iterator
.CurrentLevel() * kOutlineLevelIndent
;
4907 preferred
= max_c(preferred
, width
);
4912 column
->GetColumnName(&name
);
4913 preferred
= max_c(preferred
, StringWidth(name
));
4915 // Constrain to preferred width. This makes the method do a little
4916 // more than asked, but it's for convenience.
4917 if (preferred
< column
->MinWidth())
4918 preferred
= column
->MinWidth();
4919 else if (preferred
> column
->MaxWidth())
4920 preferred
= column
->MaxWidth();
4929 RecursiveOutlineIterator::RecursiveOutlineIterator(BRowContainer
* list
,
4930 bool openBranchesOnly
)
4933 fCurrentListIndex(0),
4934 fCurrentListDepth(0),
4935 fOpenBranchesOnly(openBranchesOnly
)
4937 if (list
== 0 || list
->CountItems() == 0)
4940 fCurrentList
= list
;
4945 RecursiveOutlineIterator::CurrentRow() const
4947 if (fCurrentList
== 0)
4950 return fCurrentList
->ItemAt(fCurrentListIndex
);
4955 RecursiveOutlineIterator::GoToNext()
4957 if (fCurrentList
== 0)
4959 if (fCurrentListIndex
< 0 || fCurrentListIndex
>= fCurrentList
->CountItems()) {
4964 BRow
* currentRow
= fCurrentList
->ItemAt(fCurrentListIndex
);
4966 if (currentRow
->fChildList
&& (currentRow
->fIsExpanded
|| !fOpenBranchesOnly
)
4967 && currentRow
->fChildList
->CountItems() > 0) {
4969 // Put current list on the stack if it needs to be revisited.
4970 if (fCurrentListIndex
< fCurrentList
->CountItems() - 1) {
4971 fStack
[fStackIndex
].fRowSet
= fCurrentList
;
4972 fStack
[fStackIndex
].fIndex
= fCurrentListIndex
+ 1;
4973 fStack
[fStackIndex
].fDepth
= fCurrentListDepth
;
4977 fCurrentList
= currentRow
->fChildList
;
4978 fCurrentListIndex
= 0;
4979 fCurrentListDepth
++;
4980 } else if (fCurrentListIndex
< fCurrentList
->CountItems() - 1)
4981 fCurrentListIndex
++; // next item in current list
4982 else if (--fStackIndex
>= 0) {
4983 fCurrentList
= fStack
[fStackIndex
].fRowSet
;
4984 fCurrentListIndex
= fStack
[fStackIndex
].fIndex
;
4985 fCurrentListDepth
= fStack
[fStackIndex
].fDepth
;
4993 RecursiveOutlineIterator::CurrentLevel() const
4995 return fCurrentListDepth
;