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 "ObjectList.h"
74 #define DOUBLE_BUFFERED_COLUMN_RESIZE 1
75 #define SMART_REDRAW 1
76 #define DRAG_TITLE_OUTLINE 1
77 #define CONSTRAIN_CLIPPING_REGION 1
78 #define LOWER_SCROLLBAR 0
83 static const unsigned char kDownSortArrow8x8
[] = {
84 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff,
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 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
89 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff,
90 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff,
91 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff
94 static const unsigned char kUpSortArrow8x8
[] = {
95 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff,
96 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff,
97 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff,
98 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
99 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 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
105 static const unsigned char kDownSortArrow8x8Invert
[] = {
106 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
107 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0xff,
108 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
109 0xff, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0xff, 0xff,
110 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
111 0xff, 0xff, 0x1f, 0x1f, 0x1f, 0xff, 0xff, 0xff,
112 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
113 0xff, 0xff, 0xff, 0x1f, 0xff, 0xff, 0xff, 0xff
116 static const unsigned char kUpSortArrow8x8Invert
[] = {
117 0xff, 0xff, 0xff, 0x1f, 0xff, 0xff, 0xff, 0xff,
118 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
119 0xff, 0xff, 0x1f, 0x1f, 0x1f, 0xff, 0xff, 0xff,
120 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
121 0xff, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0xff, 0xff,
122 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
123 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0xff,
124 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
127 static const float kTintedLineTint
= 1.04;
129 static const float kMinTitleHeight
= 16.0;
130 static const float kMinRowHeight
= 16.0;
131 static const float kTitleSpacing
= 1.4;
132 static const float kRowSpacing
= 1.4;
133 static const float kLatchWidth
= 15.0;
135 static const int32 kMaxDepth
= 1024;
136 static const float kLeftMargin
= kLatchWidth
;
137 static const float kRightMargin
= 8;
138 static const float kOutlineLevelIndent
= kLatchWidth
;
139 static const float kColumnResizeAreaWidth
= 10.0;
140 static const float kRowDragSensitivity
= 5.0;
141 static const float kDoubleClickMoveSensitivity
= 4.0;
142 static const float kSortIndicatorWidth
= 9.0;
143 static const float kDropHighlightLineHeight
= 2.0;
145 static const uint32 kToggleColumn
= 'BTCL';
147 class BRowContainer
: public BObjectList
<BRow
>
151 class TitleView
: public BView
{
152 typedef BView _inherited
;
154 TitleView(BRect frame
, OutlineView
* outlineView
,
155 BList
* visibleColumns
, BList
* sortColumns
,
156 BColumnListView
* masterView
,
157 uint32 resizingMode
);
158 virtual ~TitleView();
160 void ColumnAdded(BColumn
* column
);
161 void ColumnResized(BColumn
* column
, float oldWidth
);
162 void SetColumnVisible(BColumn
* column
, bool visible
);
164 virtual void Draw(BRect updateRect
);
165 virtual void ScrollTo(BPoint where
);
166 virtual void MessageReceived(BMessage
* message
);
167 virtual void MouseDown(BPoint where
);
168 virtual void MouseMoved(BPoint where
, uint32 transit
,
169 const BMessage
* dragMessage
);
170 virtual void MouseUp(BPoint where
);
171 virtual void FrameResized(float width
, float height
);
173 void MoveColumn(BColumn
* column
, int32 index
);
174 void SetColumnFlags(column_flags flags
);
176 void SetEditMode(bool state
)
177 { fEditMode
= state
; }
179 float MarginWidth() const;
182 void GetTitleRect(BColumn
* column
, BRect
* _rect
);
183 int32
FindColumn(BPoint where
, float* _leftEdge
);
184 void FixScrollBar(bool scrollToFit
);
185 void DragSelectedColumn(BPoint where
);
186 void ResizeSelectedColumn(BPoint where
,
187 bool preferred
= false);
188 void ComputeDragBoundries(BColumn
* column
,
190 void DrawTitle(BView
* view
, BRect frame
,
191 BColumn
* column
, bool depressed
);
193 float _VirtualWidth() const;
195 OutlineView
* fOutlineView
;
198 // float fColumnsWidth;
201 #if DOUBLE_BUFFERED_COLUMN_RESIZE
202 BBitmap
* fDrawBuffer
;
203 BView
* fDrawBufferView
;
210 DRAG_COLUMN_INSIDE_TITLE
,
211 DRAG_COLUMN_OUTSIDE_TITLE
214 BPopUpMenu
* fColumnPop
;
215 BColumnListView
* fMasterView
;
219 // State information for resizing/dragging
220 BColumn
* fSelectedColumn
;
221 BRect fSelectedColumnRect
;
222 bool fResizingFirstColumn
;
223 BPoint fClickPoint
; // offset within cell
224 float fLeftDragBoundry
;
225 float fRightDragBoundry
;
226 BPoint fCurrentDragPosition
;
229 BBitmap
* fUpSortArrow
;
230 BBitmap
* fDownSortArrow
;
232 BCursor
* fResizeCursor
;
233 BCursor
* fMinResizeCursor
;
234 BCursor
* fMaxResizeCursor
;
235 BCursor
* fColumnMoveCursor
;
239 class OutlineView
: public BView
{
240 typedef BView _inherited
;
242 OutlineView(BRect
, BList
* visibleColumns
,
244 BColumnListView
* listView
);
245 virtual ~OutlineView();
247 virtual void Draw(BRect
);
248 const BRect
& VisibleRect() const;
250 void RedrawColumn(BColumn
* column
, float leftEdge
,
253 float GetColumnPreferredWidth(BColumn
* column
);
255 void AddRow(BRow
*, int32 index
, BRow
* TheRow
);
256 BRow
* CurrentSelection(BRow
* lastSelected
) const;
257 void ToggleFocusRowSelection(bool selectRange
);
258 void ToggleFocusRowOpen();
259 void ChangeFocusRow(bool up
, bool updateSelection
,
260 bool addToCurrentSelection
);
261 void MoveFocusToVisibleRect();
262 void ExpandOrCollapse(BRow
* parent
, bool expand
);
263 void RemoveRow(BRow
*);
264 BRowContainer
* RowList();
265 void UpdateRow(BRow
*);
266 bool FindParent(BRow
* row
, BRow
** _parent
,
268 int32
IndexOf(BRow
* row
);
269 void Deselect(BRow
*);
270 void AddToSelection(BRow
*);
272 BRow
* FocusRow() const;
273 void SetFocusRow(BRow
* row
, bool select
);
274 BRow
* FindRow(float ypos
, int32
* _indent
,
276 bool FindRect(const BRow
* row
, BRect
* _rect
);
277 void ScrollTo(const BRow
* row
);
280 void SetSelectionMode(list_view_type type
);
281 list_view_type
SelectionMode() const;
282 void SetMouseTrackingEnabled(bool);
283 void FixScrollBar(bool scrollToFit
);
284 void SetEditMode(bool state
)
285 { fEditMode
= state
; }
287 virtual void FrameResized(float width
, float height
);
288 virtual void ScrollTo(BPoint where
);
289 virtual void MouseDown(BPoint where
);
290 virtual void MouseMoved(BPoint where
, uint32 transit
,
291 const BMessage
* dragMessage
);
292 virtual void MouseUp(BPoint where
);
293 virtual void MessageReceived(BMessage
* message
);
296 bool SortList(BRowContainer
* list
, bool isVisible
);
297 static int32
DeepSortThreadEntry(void* outlineView
);
299 void SelectRange(BRow
* start
, BRow
* end
);
300 int32
CompareRows(BRow
* row1
, BRow
* row2
);
301 void AddSorted(BRowContainer
* list
, BRow
* row
);
302 void RecursiveDeleteRows(BRowContainer
* list
,
304 void InvalidateCachedPositions();
305 bool FindVisibleRect(BRow
* row
, BRect
* _rect
);
313 #if DOUBLE_BUFFERED_COLUMN_RESIZE
314 BBitmap
* fDrawBuffer
;
315 BView
* fDrawBufferView
;
322 BRow fSelectionListDummyHead
;
323 BRow
* fLastSelectedItem
;
324 BRow
* fFirstSelectedItem
;
326 thread_id fSortThread
;
337 CurrentState fCurrentState
;
340 BColumnListView
* fMasterView
;
341 list_view_type fSelectionMode
;
343 BField
* fCurrentField
;
345 BColumn
* fCurrentColumn
;
351 // State information for mouse/keyboard interaction
358 float fDropHighlightY
;
360 friend class RecursiveOutlineIterator
;
364 class RecursiveOutlineIterator
{
366 RecursiveOutlineIterator(
367 BRowContainer
* container
,
368 bool openBranchesOnly
= true);
370 BRow
* CurrentRow() const;
371 int32
CurrentLevel() const;
376 BRowContainer
* fRowSet
;
382 BRowContainer
* fCurrentList
;
383 int32 fCurrentListIndex
;
384 int32 fCurrentListDepth
;
385 bool fOpenBranchesOnly
;
388 } // namespace BPrivate
391 using namespace BPrivate
;
408 BColumn::MouseMoved(BColumnListView
* /*parent*/, BRow
* /*row*/,
409 BField
* /*field*/, BRect
/*field_rect*/, BPoint
/*point*/,
410 uint32
/*buttons*/, int32
/*code*/)
416 BColumn::MouseDown(BColumnListView
* /*parent*/, BRow
* /*row*/,
417 BField
* /*field*/, BRect
/*field_rect*/, BPoint
/*point*/,
424 BColumn::MouseUp(BColumnListView
* /*parent*/, BRow
* /*row*/, BField
* /*field*/)
436 fHeight(std::max(kMinRowHeight
,
437 ceilf(be_plain_font
->Size() * kRowSpacing
))),
446 BRow::BRow(float height
)
462 BField
* field
= (BField
*) fFields
.RemoveItem((int32
)0);
472 BRow::HasLatch() const
474 return fChildList
!= 0;
479 BRow::CountFields() const
481 return fFields
.CountItems();
486 BRow::GetField(int32 index
)
488 return (BField
*)fFields
.ItemAt(index
);
493 BRow::GetField(int32 index
) const
495 return (const BField
*)fFields
.ItemAt(index
);
500 BRow::SetField(BField
* field
, int32 logicalFieldIndex
)
502 if (fFields
.ItemAt(logicalFieldIndex
) != 0)
503 delete (BField
*)fFields
.RemoveItem(logicalFieldIndex
);
506 ValidateField(field
, logicalFieldIndex
);
510 fFields
.AddItem(field
, logicalFieldIndex
);
522 BRow::IsExpanded() const
529 BRow::IsSelected() const
531 return fPrevSelected
!= NULL
;
539 fList
->InvalidateRow(this);
544 BRow::ValidateFields() const
546 for (int32 i
= 0; i
< CountFields(); i
++)
547 ValidateField(GetField(i
), i
);
552 BRow::ValidateField(const BField
* field
, int32 logicalFieldIndex
) const
554 // The Fields may be moved by the user, but the logicalFieldIndexes
555 // do not change, so we need to map them over when checking the
557 BColumn
* column
= NULL
;
558 int32 items
= fList
->CountColumns();
559 for (int32 i
= 0 ; i
< items
; ++i
) {
560 column
= fList
->ColumnAt(i
);
561 if(column
->LogicalFieldNum() == logicalFieldIndex
)
565 if (column
== NULL
) {
566 BString
dbmessage("\n\n\tThe parent BColumnListView does not have "
567 "\n\ta BColumn at the logical field index ");
568 dbmessage
<< logicalFieldIndex
<< ".\n\n";
569 printf(dbmessage
.String());
571 if (!column
->AcceptsField(field
)) {
572 BString
dbmessage("\n\n\tThe BColumn of type ");
573 dbmessage
<< typeid(*column
).name() << "\n\tat logical field index "
574 << logicalFieldIndex
<< "\n\tdoes not support the field type "
575 << typeid(*field
).name() << ".\n\n";
576 debugger(dbmessage
.String());
585 BColumn::BColumn(float width
, float minWidth
, float maxWidth
, alignment align
)
604 BColumn::Width() const
611 BColumn::SetWidth(float width
)
618 BColumn::MinWidth() const
625 BColumn::MaxWidth() const
632 BColumn::DrawTitle(BRect
, BView
*)
638 BColumn::DrawField(BField
*, BRect
, BView
*)
644 BColumn::CompareFields(BField
*, BField
*)
651 BColumn::GetColumnName(BString
* into
) const
658 BColumn::GetPreferredWidth(BField
* field
, BView
* parent
) const
665 BColumn::IsVisible() const
672 BColumn::SetVisible(bool visible
)
674 if (fList
&& (fVisible
!= visible
))
675 fList
->SetColumnVisible(this, visible
);
680 BColumn::ShowHeading() const
687 BColumn::SetShowHeading(bool state
)
689 fShowHeading
= state
;
694 BColumn::Alignment() const
701 BColumn::SetAlignment(alignment align
)
708 BColumn::WantsEvents() const
715 BColumn::SetWantsEvents(bool state
)
717 fWantsEvents
= state
;
722 BColumn::LogicalFieldNum() const
729 BColumn::AcceptsField(const BField
*) const
738 BColumnListView::BColumnListView(BRect rect
, const char* name
,
739 uint32 resizingMode
, uint32 flags
, border_style border
,
740 bool showHorizontalScrollbar
)
742 BView(rect
, name
, resizingMode
,
743 flags
| B_WILL_DRAW
| B_FRAME_EVENTS
| B_FULL_UPDATE_ON_RESIZE
),
745 fSelectionMessage(NULL
),
746 fSortingEnabled(true),
747 fLatchWidth(kLatchWidth
),
748 fBorderStyle(border
),
749 fShowingHorizontalScrollBar(showHorizontalScrollbar
)
755 BColumnListView::BColumnListView(const char* name
, uint32 flags
,
756 border_style border
, bool showHorizontalScrollbar
)
758 BView(name
, flags
| B_WILL_DRAW
| B_FRAME_EVENTS
| B_FULL_UPDATE_ON_RESIZE
),
760 fSelectionMessage(NULL
),
761 fSortingEnabled(true),
762 fLatchWidth(kLatchWidth
),
763 fBorderStyle(border
),
764 fShowingHorizontalScrollBar(showHorizontalScrollbar
)
770 BColumnListView::~BColumnListView()
772 while (BColumn
* column
= (BColumn
*)fColumns
.RemoveItem((int32
)0))
778 BColumnListView::InitiateDrag(BPoint
, bool)
785 BColumnListView::MessageDropped(BMessage
*, BPoint
)
791 BColumnListView::ExpandOrCollapse(BRow
* row
, bool Open
)
793 fOutlineView
->ExpandOrCollapse(row
, Open
);
798 BColumnListView::Invoke(BMessage
* message
)
803 return BInvoker::Invoke(message
);
808 BColumnListView::ItemInvoked()
815 BColumnListView::SetInvocationMessage(BMessage
* message
)
822 BColumnListView::InvocationMessage() const
829 BColumnListView::InvocationCommand() const
836 BColumnListView::FocusRow() const
838 return fOutlineView
->FocusRow();
843 BColumnListView::SetFocusRow(int32 Index
, bool Select
)
845 SetFocusRow(RowAt(Index
), Select
);
850 BColumnListView::SetFocusRow(BRow
* row
, bool Select
)
852 fOutlineView
->SetFocusRow(row
, Select
);
857 BColumnListView::SetMouseTrackingEnabled(bool Enabled
)
859 fOutlineView
->SetMouseTrackingEnabled(Enabled
);
864 BColumnListView::SelectionMode() const
866 return fOutlineView
->SelectionMode();
871 BColumnListView::Deselect(BRow
* row
)
873 fOutlineView
->Deselect(row
);
878 BColumnListView::AddToSelection(BRow
* row
)
880 fOutlineView
->AddToSelection(row
);
885 BColumnListView::DeselectAll()
887 fOutlineView
->DeselectAll();
892 BColumnListView::CurrentSelection(BRow
* lastSelected
) const
894 return fOutlineView
->CurrentSelection(lastSelected
);
899 BColumnListView::SelectionChanged()
901 if (fSelectionMessage
)
902 Invoke(fSelectionMessage
);
907 BColumnListView::SetSelectionMessage(BMessage
* message
)
909 if (fSelectionMessage
== message
)
912 delete fSelectionMessage
;
913 fSelectionMessage
= message
;
918 BColumnListView::SelectionMessage()
920 return fSelectionMessage
;
925 BColumnListView::SelectionCommand() const
927 if (fSelectionMessage
)
928 return fSelectionMessage
->what
;
935 BColumnListView::SetSelectionMode(list_view_type mode
)
937 fOutlineView
->SetSelectionMode(mode
);
942 BColumnListView::SetSortingEnabled(bool enabled
)
944 fSortingEnabled
= enabled
;
945 fSortColumns
.MakeEmpty();
946 fTitleView
->Invalidate();
947 // erase sort indicators
952 BColumnListView::SortingEnabled() const
954 return fSortingEnabled
;
959 BColumnListView::SetSortColumn(BColumn
* column
, bool add
, bool ascending
)
961 if (!SortingEnabled())
965 fSortColumns
.MakeEmpty();
967 if (!fSortColumns
.HasItem(column
))
968 fSortColumns
.AddItem(column
);
970 column
->fSortAscending
= ascending
;
971 fTitleView
->Invalidate();
972 fOutlineView
->StartSorting();
977 BColumnListView::ClearSortColumns()
979 fSortColumns
.MakeEmpty();
980 fTitleView
->Invalidate();
981 // erase sort indicators
986 BColumnListView::AddStatusView(BView
* view
)
988 BRect bounds
= Bounds();
989 float width
= view
->Bounds().Width();
990 if (width
> bounds
.Width() / 2)
991 width
= bounds
.Width() / 2;
995 Window()->BeginViewTransaction();
996 fHorizontalScrollBar
->ResizeBy(-(width
+ 1), 0);
997 fHorizontalScrollBar
->MoveBy((width
+ 1), 0);
1000 BRect
viewRect(bounds
);
1001 viewRect
.right
= width
;
1002 viewRect
.top
= viewRect
.bottom
- B_H_SCROLL_BAR_HEIGHT
;
1003 if (fBorderStyle
== B_PLAIN_BORDER
)
1004 viewRect
.OffsetBy(1, -1);
1005 else if (fBorderStyle
== B_FANCY_BORDER
)
1006 viewRect
.OffsetBy(2, -2);
1008 view
->SetResizingMode(B_FOLLOW_LEFT
| B_FOLLOW_BOTTOM
);
1009 view
->ResizeTo(viewRect
.Width(), viewRect
.Height());
1010 view
->MoveTo(viewRect
.left
, viewRect
.top
);
1011 Window()->EndViewTransaction();
1016 BColumnListView::RemoveStatusView()
1019 float width
= fStatusView
->Bounds().Width();
1020 Window()->BeginViewTransaction();
1021 fStatusView
->RemoveSelf();
1022 fHorizontalScrollBar
->MoveBy(-width
, 0);
1023 fHorizontalScrollBar
->ResizeBy(width
, 0);
1024 Window()->EndViewTransaction();
1027 BView
* view
= fStatusView
;
1034 BColumnListView::AddColumn(BColumn
* column
, int32 logicalFieldIndex
)
1036 ASSERT(column
!= NULL
);
1038 column
->fList
= this;
1039 column
->fFieldID
= logicalFieldIndex
;
1041 // sanity check -- if there is already a field with this ID, remove it.
1042 for (int32 index
= 0; index
< fColumns
.CountItems(); index
++) {
1043 BColumn
* existingColumn
= (BColumn
*) fColumns
.ItemAt(index
);
1044 if (existingColumn
&& existingColumn
->fFieldID
== logicalFieldIndex
) {
1045 RemoveColumn(existingColumn
);
1050 if (column
->Width() < column
->MinWidth())
1051 column
->SetWidth(column
->MinWidth());
1052 else if (column
->Width() > column
->MaxWidth())
1053 column
->SetWidth(column
->MaxWidth());
1055 fColumns
.AddItem((void*) column
);
1056 fTitleView
->ColumnAdded(column
);
1061 BColumnListView::MoveColumn(BColumn
* column
, int32 index
)
1063 ASSERT(column
!= NULL
);
1064 fTitleView
->MoveColumn(column
, index
);
1069 BColumnListView::RemoveColumn(BColumn
* column
)
1071 if (fColumns
.HasItem(column
)) {
1072 SetColumnVisible(column
, false);
1073 if (Window() != NULL
)
1074 Window()->UpdateIfNeeded();
1075 fColumns
.RemoveItem(column
);
1081 BColumnListView::CountColumns() const
1083 return fColumns
.CountItems();
1088 BColumnListView::ColumnAt(int32 field
) const
1090 return (BColumn
*) fColumns
.ItemAt(field
);
1095 BColumnListView::ColumnAt(BPoint point
) const
1097 float left
= MAX(kLeftMargin
, LatchWidth());
1099 for (int i
= 0; BColumn
* column
= (BColumn
*)fColumns
.ItemAt(i
); i
++) {
1100 if (column
== NULL
|| !column
->IsVisible())
1103 float right
= left
+ column
->Width();
1104 if (point
.x
>= left
&& point
.x
<= right
)
1115 BColumnListView::SetColumnVisible(BColumn
* column
, bool visible
)
1117 fTitleView
->SetColumnVisible(column
, visible
);
1122 BColumnListView::SetColumnVisible(int32 index
, bool isVisible
)
1124 BColumn
* column
= ColumnAt(index
);
1126 column
->SetVisible(isVisible
);
1131 BColumnListView::IsColumnVisible(int32 index
) const
1133 BColumn
* column
= ColumnAt(index
);
1135 return column
->IsVisible();
1142 BColumnListView::SetColumnFlags(column_flags flags
)
1144 fTitleView
->SetColumnFlags(flags
);
1149 BColumnListView::ResizeColumnToPreferred(int32 index
)
1151 BColumn
* column
= ColumnAt(index
);
1155 // get the preferred column width
1156 float width
= fOutlineView
->GetColumnPreferredWidth(column
);
1159 float oldWidth
= column
->Width();
1160 column
->SetWidth(width
);
1162 fTitleView
->ColumnResized(column
, oldWidth
);
1163 fOutlineView
->Invalidate();
1168 BColumnListView::ResizeAllColumnsToPreferred()
1170 int32 count
= CountColumns();
1171 for (int32 i
= 0; i
< count
; i
++)
1172 ResizeColumnToPreferred(i
);
1177 BColumnListView::RowAt(int32 Index
, BRow
* parentRow
) const
1180 return fOutlineView
->RowList()->ItemAt(Index
);
1182 return parentRow
->fChildList
? parentRow
->fChildList
->ItemAt(Index
) : NULL
;
1187 BColumnListView::RowAt(int32 Index
, BRow
* parentRow
)
1190 return fOutlineView
->RowList()->ItemAt(Index
);
1192 return parentRow
->fChildList
? parentRow
->fChildList
->ItemAt(Index
) : 0;
1197 BColumnListView::RowAt(BPoint point
) const
1201 return fOutlineView
->FindRow(point
.y
, &indent
, &top
);
1206 BColumnListView::RowAt(BPoint point
)
1210 return fOutlineView
->FindRow(point
.y
, &indent
, &top
);
1215 BColumnListView::GetRowRect(const BRow
* row
, BRect
* outRect
) const
1217 return fOutlineView
->FindRect(row
, outRect
);
1222 BColumnListView::FindParent(BRow
* row
, BRow
** _parent
, bool* _isVisible
) const
1224 return fOutlineView
->FindParent(row
, _parent
, _isVisible
);
1229 BColumnListView::IndexOf(BRow
* row
)
1231 return fOutlineView
->IndexOf(row
);
1236 BColumnListView::CountRows(BRow
* parentRow
) const
1239 return fOutlineView
->RowList()->CountItems();
1240 if (parentRow
->fChildList
)
1241 return parentRow
->fChildList
->CountItems();
1248 BColumnListView::AddRow(BRow
* row
, BRow
* parentRow
)
1250 AddRow(row
, -1, parentRow
);
1255 BColumnListView::AddRow(BRow
* row
, int32 index
, BRow
* parentRow
)
1257 row
->fChildList
= 0;
1259 row
->ValidateFields();
1260 fOutlineView
->AddRow(row
, index
, parentRow
);
1265 BColumnListView::RemoveRow(BRow
* row
)
1267 fOutlineView
->RemoveRow(row
);
1273 BColumnListView::UpdateRow(BRow
* row
)
1275 fOutlineView
->UpdateRow(row
);
1280 BColumnListView::SwapRows(int32 index1
, int32 index2
, BRow
* parentRow1
,
1286 BRowContainer
* container1
= NULL
;
1287 BRowContainer
* container2
= NULL
;
1289 if (parentRow1
== NULL
)
1290 container1
= fOutlineView
->RowList();
1292 container1
= parentRow1
->fChildList
;
1294 if (container1
== NULL
)
1297 if (parentRow2
== NULL
)
1298 container2
= fOutlineView
->RowList();
1300 container2
= parentRow2
->fChildList
;
1302 if (container2
== NULL
)
1305 row1
= container1
->ItemAt(index1
);
1310 row2
= container2
->ItemAt(index2
);
1315 container1
->ReplaceItem(index2
, row1
);
1316 container2
->ReplaceItem(index1
, row2
);
1322 fOutlineView
->FindRect(row1
, &rect1
);
1323 fOutlineView
->FindRect(row2
, &rect2
);
1325 rect
= rect1
| rect2
;
1327 fOutlineView
->Invalidate(rect
);
1334 BColumnListView::ScrollTo(const BRow
* row
)
1336 fOutlineView
->ScrollTo(row
);
1341 BColumnListView::ScrollTo(BPoint point
)
1343 fOutlineView
->ScrollTo(point
);
1348 BColumnListView::Clear()
1350 fOutlineView
->Clear();
1355 BColumnListView::InvalidateRow(BRow
* row
)
1358 GetRowRect(row
, &updateRect
);
1359 fOutlineView
->Invalidate(updateRect
);
1363 // This method is deprecated.
1365 BColumnListView::SetFont(const BFont
* font
, uint32 mask
)
1367 fOutlineView
->SetFont(font
, mask
);
1368 fTitleView
->SetFont(font
, mask
);
1373 BColumnListView::SetFont(ColumnListViewFont font_num
, const BFont
* font
,
1378 fOutlineView
->SetFont(font
, mask
);
1382 fTitleView
->SetFont(font
, mask
);
1393 BColumnListView::GetFont(ColumnListViewFont font_num
, BFont
* font
) const
1397 fOutlineView
->GetFont(font
);
1401 fTitleView
->GetFont(font
);
1412 BColumnListView::SetColor(ColumnListViewColor colorIndex
, const rgb_color color
)
1414 if ((int)colorIndex
< 0) {
1416 colorIndex
= (ColumnListViewColor
)0;
1419 if ((int)colorIndex
>= (int)B_COLOR_TOTAL
) {
1421 colorIndex
= (ColumnListViewColor
)(B_COLOR_TOTAL
- 1);
1424 fColorList
[colorIndex
] = color
;
1425 fCustomColors
= true;
1430 BColumnListView::ResetColors()
1432 fCustomColors
= false;
1439 BColumnListView::Color(ColumnListViewColor colorIndex
) const
1441 if ((int)colorIndex
< 0) {
1443 colorIndex
= (ColumnListViewColor
)0;
1446 if ((int)colorIndex
>= (int)B_COLOR_TOTAL
) {
1448 colorIndex
= (ColumnListViewColor
)(B_COLOR_TOTAL
- 1);
1451 return fColorList
[colorIndex
];
1456 BColumnListView::SetHighColor(rgb_color color
)
1458 BView::SetHighColor(color
);
1459 // fOutlineView->Invalidate();
1460 // Redraw with the new color.
1461 // Note that this will currently cause an infinite loop, refreshing
1462 // over and over. A better solution is needed.
1467 BColumnListView::SetSelectionColor(rgb_color color
)
1469 fColorList
[B_COLOR_SELECTION
] = color
;
1470 fCustomColors
= true;
1475 BColumnListView::SetBackgroundColor(rgb_color color
)
1477 fColorList
[B_COLOR_BACKGROUND
] = color
;
1478 fCustomColors
= true;
1479 fOutlineView
->Invalidate();
1480 // repaint with new color
1485 BColumnListView::SetEditColor(rgb_color color
)
1487 fColorList
[B_COLOR_EDIT_BACKGROUND
] = color
;
1488 fCustomColors
= true;
1493 BColumnListView::SelectionColor() const
1495 return fColorList
[B_COLOR_SELECTION
];
1500 BColumnListView::BackgroundColor() const
1502 return fColorList
[B_COLOR_BACKGROUND
];
1507 BColumnListView::EditColor() const
1509 return fColorList
[B_COLOR_EDIT_BACKGROUND
];
1514 BColumnListView::SuggestTextPosition(const BRow
* row
,
1515 const BColumn
* inColumn
) const
1517 BRect
rect(GetFieldRect(row
, inColumn
));
1520 fOutlineView
->GetFontHeight(&fh
);
1521 float baseline
= floor(rect
.top
+ fh
.ascent
1522 + (rect
.Height() + 1 - (fh
.ascent
+ fh
.descent
)) / 2);
1523 return BPoint(rect
.left
+ 8, baseline
);
1528 BColumnListView::GetFieldRect(const BRow
* row
, const BColumn
* inColumn
) const
1531 GetRowRect(row
, &rect
);
1532 if (inColumn
!= NULL
) {
1533 float leftEdge
= MAX(kLeftMargin
, LatchWidth());
1534 for (int index
= 0; index
< fColumns
.CountItems(); index
++) {
1535 BColumn
* column
= (BColumn
*) fColumns
.ItemAt(index
);
1536 if (column
== NULL
|| !column
->IsVisible())
1539 if (column
== inColumn
) {
1540 rect
.left
= leftEdge
;
1541 rect
.right
= rect
.left
+ column
->Width();
1545 leftEdge
+= column
->Width() + 1;
1554 BColumnListView::SetLatchWidth(float width
)
1556 fLatchWidth
= width
;
1562 BColumnListView::LatchWidth() const
1568 BColumnListView::DrawLatch(BView
* view
, BRect rect
, LatchType position
, BRow
*)
1570 const int32 rectInset
= 4;
1573 int32 sideLen
= rect
.IntegerWidth();
1574 if (sideLen
> rect
.IntegerHeight())
1575 sideLen
= rect
.IntegerHeight();
1578 int32 halfWidth
= rect
.IntegerWidth() / 2;
1579 int32 halfHeight
= rect
.IntegerHeight() / 2;
1580 int32 halfSide
= sideLen
/ 2;
1582 float left
= rect
.left
+ halfWidth
- halfSide
;
1583 float top
= rect
.top
+ halfHeight
- halfSide
;
1585 BRect
itemRect(left
, top
, left
+ sideLen
, top
+ sideLen
);
1587 // Why it is a pixel high? I don't know.
1588 itemRect
.OffsetBy(0, -1);
1590 itemRect
.InsetBy(rectInset
, rectInset
);
1592 // make it an odd number of pixels wide, the latch looks better this way
1593 if ((itemRect
.IntegerWidth() % 2) == 1) {
1594 itemRect
.right
+= 1;
1595 itemRect
.bottom
+= 1;
1598 rgb_color highColor
= view
->HighColor();
1599 view
->SetHighColor(0, 0, 0);
1603 view
->StrokeRect(itemRect
);
1605 BPoint(itemRect
.left
+ 2,
1606 (itemRect
.top
+ itemRect
.bottom
) / 2),
1607 BPoint(itemRect
.right
- 2,
1608 (itemRect
.top
+ itemRect
.bottom
) / 2));
1611 case B_PRESSED_LATCH
:
1612 view
->StrokeRect(itemRect
);
1614 BPoint(itemRect
.left
+ 2,
1615 (itemRect
.top
+ itemRect
.bottom
) / 2),
1616 BPoint(itemRect
.right
- 2,
1617 (itemRect
.top
+ itemRect
.bottom
) / 2));
1619 BPoint((itemRect
.left
+ itemRect
.right
) / 2,
1621 BPoint((itemRect
.left
+ itemRect
.right
) / 2,
1622 itemRect
.bottom
- 2));
1623 view
->InvertRect(itemRect
);
1626 case B_CLOSED_LATCH
:
1627 view
->StrokeRect(itemRect
);
1629 BPoint(itemRect
.left
+ 2,
1630 (itemRect
.top
+ itemRect
.bottom
) / 2),
1631 BPoint(itemRect
.right
- 2,
1632 (itemRect
.top
+ itemRect
.bottom
) / 2));
1634 BPoint((itemRect
.left
+ itemRect
.right
) / 2,
1636 BPoint((itemRect
.left
+ itemRect
.right
) / 2,
1637 itemRect
.bottom
- 2));
1646 view
->SetHighColor(highColor
);
1651 BColumnListView::MakeFocus(bool isFocus
)
1653 if (fBorderStyle
!= B_NO_BORDER
) {
1654 // Redraw focus marks around view
1656 fHorizontalScrollBar
->SetBorderHighlighted(isFocus
);
1657 fVerticalScrollBar
->SetBorderHighlighted(isFocus
);
1660 BView::MakeFocus(isFocus
);
1665 BColumnListView::MessageReceived(BMessage
* message
)
1667 // Propagate mouse wheel messages down to child, so that it can
1668 // scroll. Note we have done so, so we don't go into infinite
1669 // recursion if this comes back up here.
1670 if (message
->what
== B_MOUSE_WHEEL_CHANGED
) {
1672 if (message
->FindBool("be:clvhandled", &handled
) != B_OK
) {
1673 message
->AddBool("be:clvhandled", true);
1674 fOutlineView
->MessageReceived(message
);
1677 } else if (message
->what
== B_COLORS_UPDATED
) {
1678 // Todo: Is it worthwhile to optimize this?
1682 BView::MessageReceived(message
);
1687 BColumnListView::KeyDown(const char* bytes
, int32 numBytes
)
1694 if ((modifiers() & B_SHIFT_KEY
) != 0) {
1695 float minVal
, maxVal
;
1696 fHorizontalScrollBar
->GetRange(&minVal
, &maxVal
);
1697 float smallStep
, largeStep
;
1698 fHorizontalScrollBar
->GetSteps(&smallStep
, &largeStep
);
1699 float oldVal
= fHorizontalScrollBar
->Value();
1700 float newVal
= oldVal
;
1702 if (c
== B_LEFT_ARROW
)
1703 newVal
-= smallStep
;
1704 else if (c
== B_RIGHT_ARROW
)
1705 newVal
+= smallStep
;
1707 if (newVal
< minVal
)
1709 else if (newVal
> maxVal
)
1712 fHorizontalScrollBar
->SetValue(newVal
);
1714 BRow
* focusRow
= fOutlineView
->FocusRow();
1715 if (focusRow
== NULL
)
1718 bool expanded
= focusRow
->IsExpanded();
1719 if ((c
== B_RIGHT_ARROW
&& !expanded
)
1720 || (c
== B_LEFT_ARROW
&& expanded
)) {
1721 fOutlineView
->ToggleFocusRowOpen();
1728 fOutlineView
->ChangeFocusRow(false,
1729 (modifiers() & B_CONTROL_KEY
) == 0,
1730 (modifiers() & B_SHIFT_KEY
) != 0);
1734 fOutlineView
->ChangeFocusRow(true,
1735 (modifiers() & B_CONTROL_KEY
) == 0,
1736 (modifiers() & B_SHIFT_KEY
) != 0);
1742 float minValue
, maxValue
;
1743 fVerticalScrollBar
->GetRange(&minValue
, &maxValue
);
1744 float smallStep
, largeStep
;
1745 fVerticalScrollBar
->GetSteps(&smallStep
, &largeStep
);
1746 float currentValue
= fVerticalScrollBar
->Value();
1747 float newValue
= currentValue
;
1750 newValue
-= largeStep
;
1752 newValue
+= largeStep
;
1754 if (newValue
> maxValue
)
1755 newValue
= maxValue
;
1756 else if (newValue
< minValue
)
1757 newValue
= minValue
;
1759 fVerticalScrollBar
->SetValue(newValue
);
1761 // Option + pgup or pgdn scrolls and changes the selection.
1762 if (modifiers() & B_OPTION_KEY
)
1763 fOutlineView
->MoveFocusToVisibleRect();
1773 fOutlineView
->ToggleFocusRowSelection(
1774 (modifiers() & B_SHIFT_KEY
) != 0);
1778 fOutlineView
->ToggleFocusRowOpen();
1782 BView::KeyDown(bytes
, numBytes
);
1788 BColumnListView::AttachedToWindow()
1790 if (!Messenger().IsValid())
1791 SetTarget(Window());
1793 if (SortingEnabled()) fOutlineView
->StartSorting();
1798 BColumnListView::WindowActivated(bool active
)
1800 fOutlineView
->Invalidate();
1801 // focus and selection appearance changes with focus
1804 // redraw focus marks around view
1805 BView::WindowActivated(active
);
1810 BColumnListView::Draw(BRect updateRect
)
1812 BRect rect
= Bounds();
1815 if (IsFocus() && Window()->IsActive())
1816 flags
|= BControlLook::B_FOCUSED
;
1818 rgb_color base
= ui_color(B_PANEL_BACKGROUND_COLOR
);
1820 BRect verticalScrollBarFrame
;
1821 if (!fVerticalScrollBar
->IsHidden())
1822 verticalScrollBarFrame
= fVerticalScrollBar
->Frame();
1824 BRect horizontalScrollBarFrame
;
1825 if (!fHorizontalScrollBar
->IsHidden())
1826 horizontalScrollBarFrame
= fHorizontalScrollBar
->Frame();
1828 if (fBorderStyle
== B_NO_BORDER
) {
1829 // We still draw the left/top border, but not focused.
1830 // The scrollbars cannot be displayed without frame and
1831 // it looks bad to have no frame only along the left/top
1833 rgb_color borderColor
= tint_color(base
, B_DARKEN_2_TINT
);
1834 SetHighColor(borderColor
);
1835 StrokeLine(BPoint(rect
.left
, rect
.bottom
),
1836 BPoint(rect
.left
, rect
.top
));
1837 StrokeLine(BPoint(rect
.left
+ 1, rect
.top
),
1838 BPoint(rect
.right
, rect
.top
));
1841 be_control_look
->DrawScrollViewFrame(this, rect
, updateRect
,
1842 verticalScrollBarFrame
, horizontalScrollBarFrame
,
1843 base
, fBorderStyle
, flags
);
1845 if (fStatusView
!= NULL
) {
1847 BRegion
region(rect
& fStatusView
->Frame().InsetByCopy(-2, -2));
1848 ConstrainClippingRegion(®ion
);
1849 rect
.bottom
= fStatusView
->Frame().top
- 1;
1850 be_control_look
->DrawScrollViewFrame(this, rect
, updateRect
,
1851 BRect(), BRect(), base
, fBorderStyle
, flags
);
1857 BColumnListView::SaveState(BMessage
* message
)
1859 message
->MakeEmpty();
1861 for (int32 i
= 0; BColumn
* column
= (BColumn
*)fColumns
.ItemAt(i
); i
++) {
1862 message
->AddInt32("ID", column
->fFieldID
);
1863 message
->AddFloat("width", column
->fWidth
);
1864 message
->AddBool("visible", column
->fVisible
);
1867 message
->AddBool("sortingenabled", fSortingEnabled
);
1869 if (fSortingEnabled
) {
1870 for (int32 i
= 0; BColumn
* column
= (BColumn
*)fSortColumns
.ItemAt(i
);
1872 message
->AddInt32("sortID", column
->fFieldID
);
1873 message
->AddBool("sortascending", column
->fSortAscending
);
1880 BColumnListView::LoadState(BMessage
* message
)
1883 for (int i
= 0; message
->FindInt32("ID", i
, &id
) == B_OK
; i
++) {
1884 for (int j
= 0; BColumn
* column
= (BColumn
*)fColumns
.ItemAt(j
); j
++) {
1885 if (column
->fFieldID
== id
) {
1886 // move this column to position 'i' and set its attributes
1887 MoveColumn(column
, i
);
1889 if (message
->FindFloat("width", i
, &width
) == B_OK
)
1890 column
->SetWidth(width
);
1892 if (message
->FindBool("visible", i
, &visible
) == B_OK
)
1893 column
->SetVisible(visible
);
1898 if (message
->FindBool("sortingenabled", &b
) == B_OK
) {
1899 SetSortingEnabled(b
);
1900 for (int k
= 0; message
->FindInt32("sortID", k
, &id
) == B_OK
; k
++) {
1901 for (int j
= 0; BColumn
* column
= (BColumn
*)fColumns
.ItemAt(j
);
1903 if (column
->fFieldID
== id
) {
1904 // add this column to the sort list
1906 if (message
->FindBool("sortascending", k
, &value
) == B_OK
)
1907 SetSortColumn(column
, true, value
);
1916 BColumnListView::SetEditMode(bool state
)
1918 fOutlineView
->SetEditMode(state
);
1919 fTitleView
->SetEditMode(state
);
1924 BColumnListView::Refresh()
1928 fOutlineView
->FixScrollBar (true);
1929 fOutlineView
->Invalidate();
1930 Window()->UpdateIfNeeded();
1937 BColumnListView::MinSize()
1941 size
.height
= std::max(kMinTitleHeight
,
1942 ceilf(be_plain_font
->Size() * kTitleSpacing
))
1943 + 4 * B_H_SCROLL_BAR_HEIGHT
;
1944 if (!fHorizontalScrollBar
->IsHidden())
1945 size
.height
+= fHorizontalScrollBar
->Frame().Height() + 1;
1946 // TODO: Take border size into account
1948 return BLayoutUtils::ComposeSize(ExplicitMinSize(), size
);
1953 BColumnListView::PreferredSize()
1955 BSize size
= MinSize();
1956 size
.height
+= ceilf(be_plain_font
->Size()) * 20;
1958 // return MinSize().width if there are no columns.
1959 int32 count
= CountColumns();
1963 BRect vScrollBarRect
;
1964 BRect hScrollBarRect
;
1965 _GetChildViewRects(Bounds(), titleRect
, outlineRect
, vScrollBarRect
,
1967 // Start with the extra width for border and scrollbars etc.
1968 size
.width
= titleRect
.left
- Bounds().left
;
1969 size
.width
+= Bounds().right
- titleRect
.right
;
1970 // If we want all columns to be visible at their preferred width,
1971 // we also need to add the extra margin width that the TitleView
1972 // uses to compute its _VirtualWidth() for the horizontal scroll bar.
1973 size
.width
+= fTitleView
->MarginWidth();
1974 for (int32 i
= 0; i
< count
; i
++) {
1975 BColumn
* column
= ColumnAt(i
);
1977 size
.width
+= fOutlineView
->GetColumnPreferredWidth(column
);
1981 return BLayoutUtils::ComposeSize(ExplicitPreferredSize(), size
);
1986 BColumnListView::MaxSize()
1988 BSize
size(B_SIZE_UNLIMITED
, B_SIZE_UNLIMITED
);
1989 return BLayoutUtils::ComposeSize(ExplicitMaxSize(), size
);
1994 BColumnListView::LayoutInvalidated(bool descendants
)
2000 BColumnListView::DoLayout()
2002 if ((Flags() & B_SUPPORTS_LAYOUT
) == 0)
2007 BRect vScrollBarRect
;
2008 BRect hScrollBarRect
;
2009 _GetChildViewRects(Bounds(), titleRect
, outlineRect
, vScrollBarRect
,
2012 fTitleView
->MoveTo(titleRect
.LeftTop());
2013 fTitleView
->ResizeTo(titleRect
.Width(), titleRect
.Height());
2015 fOutlineView
->MoveTo(outlineRect
.LeftTop());
2016 fOutlineView
->ResizeTo(outlineRect
.Width(), outlineRect
.Height());
2018 fVerticalScrollBar
->MoveTo(vScrollBarRect
.LeftTop());
2019 fVerticalScrollBar
->ResizeTo(vScrollBarRect
.Width(),
2020 vScrollBarRect
.Height());
2022 if (fStatusView
!= NULL
) {
2023 BSize size
= fStatusView
->MinSize();
2024 if (size
.height
> B_H_SCROLL_BAR_HEIGHT
)
2025 size
.height
= B_H_SCROLL_BAR_HEIGHT
;
2026 if (size
.width
> Bounds().Width() / 2)
2027 size
.width
= floorf(Bounds().Width() / 2);
2029 BPoint
offset(hScrollBarRect
.LeftTop());
2031 if (fBorderStyle
== B_PLAIN_BORDER
) {
2032 offset
+= BPoint(0, 1);
2033 } else if (fBorderStyle
== B_FANCY_BORDER
) {
2034 offset
+= BPoint(-1, 2);
2038 fStatusView
->MoveTo(offset
);
2039 fStatusView
->ResizeTo(size
.width
, size
.height
);
2040 hScrollBarRect
.left
= offset
.x
+ size
.width
+ 1;
2043 fHorizontalScrollBar
->MoveTo(hScrollBarRect
.LeftTop());
2044 fHorizontalScrollBar
->ResizeTo(hScrollBarRect
.Width(),
2045 hScrollBarRect
.Height());
2047 fOutlineView
->FixScrollBar(true);
2052 BColumnListView::_Init()
2054 SetViewColor(B_TRANSPARENT_32_BIT
);
2056 BRect
bounds(Bounds());
2057 if (bounds
.Width() <= 0)
2060 if (bounds
.Height() <= 0)
2061 bounds
.bottom
= 100;
2063 fCustomColors
= false;
2068 BRect vScrollBarRect
;
2069 BRect hScrollBarRect
;
2070 _GetChildViewRects(bounds
, titleRect
, outlineRect
, vScrollBarRect
,
2073 fOutlineView
= new OutlineView(outlineRect
, &fColumns
, &fSortColumns
, this);
2074 AddChild(fOutlineView
);
2077 fTitleView
= new TitleView(titleRect
, fOutlineView
, &fColumns
,
2078 &fSortColumns
, this, B_FOLLOW_LEFT_RIGHT
| B_FOLLOW_TOP
);
2079 AddChild(fTitleView
);
2081 fVerticalScrollBar
= new BScrollBar(vScrollBarRect
, "vertical_scroll_bar",
2082 fOutlineView
, 0.0, bounds
.Height(), B_VERTICAL
);
2083 AddChild(fVerticalScrollBar
);
2085 fHorizontalScrollBar
= new BScrollBar(hScrollBarRect
,
2086 "horizontal_scroll_bar", fTitleView
, 0.0, bounds
.Width(), B_HORIZONTAL
);
2087 AddChild(fHorizontalScrollBar
);
2089 if (!fShowingHorizontalScrollBar
)
2090 fHorizontalScrollBar
->Hide();
2092 fOutlineView
->FixScrollBar(true);
2097 BColumnListView::_UpdateColors()
2102 fColorList
[B_COLOR_BACKGROUND
] = ui_color(B_LIST_BACKGROUND_COLOR
);
2103 fColorList
[B_COLOR_TEXT
] = ui_color(B_LIST_ITEM_TEXT_COLOR
);
2104 fColorList
[B_COLOR_ROW_DIVIDER
] = tint_color(
2105 ui_color(B_LIST_SELECTED_BACKGROUND_COLOR
), B_DARKEN_2_TINT
);
2106 fColorList
[B_COLOR_SELECTION
] = ui_color(B_LIST_SELECTED_BACKGROUND_COLOR
);
2107 fColorList
[B_COLOR_SELECTION_TEXT
] =
2108 ui_color(B_LIST_SELECTED_ITEM_TEXT_COLOR
);
2110 // For non focus selection uses the selection color as BListView
2111 fColorList
[B_COLOR_NON_FOCUS_SELECTION
] =
2112 ui_color(B_LIST_SELECTED_BACKGROUND_COLOR
);
2114 // edit mode doesn't work very well
2115 fColorList
[B_COLOR_EDIT_BACKGROUND
] = tint_color(
2116 ui_color(B_LIST_SELECTED_BACKGROUND_COLOR
), B_DARKEN_1_TINT
);
2117 fColorList
[B_COLOR_EDIT_BACKGROUND
].alpha
= 180;
2120 fColorList
[B_COLOR_EDIT_TEXT
] = ui_color(B_LIST_SELECTED_ITEM_TEXT_COLOR
);
2122 fColorList
[B_COLOR_HEADER_BACKGROUND
] = ui_color(B_PANEL_BACKGROUND_COLOR
);
2123 fColorList
[B_COLOR_HEADER_TEXT
] = ui_color(B_PANEL_TEXT_COLOR
);
2126 fColorList
[B_COLOR_SEPARATOR_LINE
] = ui_color(B_LIST_ITEM_TEXT_COLOR
);
2127 fColorList
[B_COLOR_SEPARATOR_BORDER
] = ui_color(B_LIST_ITEM_TEXT_COLOR
);
2132 BColumnListView::_GetChildViewRects(const BRect
& bounds
, BRect
& titleRect
,
2133 BRect
& outlineRect
, BRect
& vScrollBarRect
, BRect
& hScrollBarRect
)
2136 titleRect
.bottom
= titleRect
.top
+ std::max(kMinTitleHeight
,
2137 ceilf(be_plain_font
->Size() * kTitleSpacing
));
2138 #if !LOWER_SCROLLBAR
2139 titleRect
.right
-= B_V_SCROLL_BAR_WIDTH
;
2142 outlineRect
= bounds
;
2143 outlineRect
.top
= titleRect
.bottom
+ 1.0;
2144 outlineRect
.right
-= B_V_SCROLL_BAR_WIDTH
;
2145 if (fShowingHorizontalScrollBar
)
2146 outlineRect
.bottom
-= B_H_SCROLL_BAR_HEIGHT
;
2148 vScrollBarRect
= bounds
;
2150 vScrollBarRect
.top
+= std::max(kMinTitleHeight
,
2151 ceilf(be_plain_font
->Size() * kTitleSpacing
));
2154 vScrollBarRect
.left
= vScrollBarRect
.right
- B_V_SCROLL_BAR_WIDTH
;
2155 if (fShowingHorizontalScrollBar
)
2156 vScrollBarRect
.bottom
-= B_H_SCROLL_BAR_HEIGHT
;
2158 hScrollBarRect
= bounds
;
2159 hScrollBarRect
.top
= hScrollBarRect
.bottom
- B_H_SCROLL_BAR_HEIGHT
;
2160 hScrollBarRect
.right
-= B_V_SCROLL_BAR_WIDTH
;
2162 // Adjust stuff so the border will fit.
2163 if (fBorderStyle
== B_PLAIN_BORDER
|| fBorderStyle
== B_NO_BORDER
) {
2164 titleRect
.InsetBy(1, 0);
2165 titleRect
.OffsetBy(0, 1);
2166 outlineRect
.InsetBy(1, 1);
2167 } else if (fBorderStyle
== B_FANCY_BORDER
) {
2168 titleRect
.InsetBy(2, 0);
2169 titleRect
.OffsetBy(0, 2);
2170 outlineRect
.InsetBy(2, 2);
2172 vScrollBarRect
.OffsetBy(-1, 0);
2174 vScrollBarRect
.top
+= 2;
2175 vScrollBarRect
.bottom
-= 1;
2177 vScrollBarRect
.InsetBy(0, 1);
2179 hScrollBarRect
.OffsetBy(0, -1);
2180 hScrollBarRect
.InsetBy(1, 0);
2188 TitleView::TitleView(BRect rect
, OutlineView
* horizontalSlave
,
2189 BList
* visibleColumns
, BList
* sortColumns
, BColumnListView
* listView
,
2190 uint32 resizingMode
)
2192 BView(rect
, "title_view", resizingMode
, B_WILL_DRAW
| B_FRAME_EVENTS
),
2193 fOutlineView(horizontalSlave
),
2194 fColumns(visibleColumns
),
2195 fSortColumns(sortColumns
),
2196 // fColumnsWidth(0),
2197 fVisibleRect(rect
.OffsetToCopy(0, 0)),
2198 fCurrentState(INACTIVE
),
2200 fMasterView(listView
),
2202 fColumnFlags(B_ALLOW_COLUMN_MOVE
| B_ALLOW_COLUMN_RESIZE
2203 | B_ALLOW_COLUMN_POPUP
| B_ALLOW_COLUMN_REMOVE
)
2205 SetViewColor(B_TRANSPARENT_COLOR
);
2207 #if DOUBLE_BUFFERED_COLUMN_RESIZE
2208 // xxx this needs to be smart about the size of the backbuffer.
2209 BRect
doubleBufferRect(0, 0, 600, 35);
2210 fDrawBuffer
= new BBitmap(doubleBufferRect
, B_RGB32
, true);
2211 fDrawBufferView
= new BView(doubleBufferRect
, "double_buffer_view",
2212 B_FOLLOW_ALL_SIDES
, 0);
2213 fDrawBuffer
->Lock();
2214 fDrawBuffer
->AddChild(fDrawBufferView
);
2215 fDrawBuffer
->Unlock();
2218 fUpSortArrow
= new BBitmap(BRect(0, 0, 7, 7), B_CMAP8
);
2219 fDownSortArrow
= new BBitmap(BRect(0, 0, 7, 7), B_CMAP8
);
2221 fUpSortArrow
->SetBits((const void*) kUpSortArrow8x8
, 64, 0, B_CMAP8
);
2222 fDownSortArrow
->SetBits((const void*) kDownSortArrow8x8
, 64, 0, B_CMAP8
);
2224 fResizeCursor
= new BCursor(B_CURSOR_ID_RESIZE_EAST_WEST
);
2225 fMinResizeCursor
= new BCursor(B_CURSOR_ID_RESIZE_EAST
);
2226 fMaxResizeCursor
= new BCursor(B_CURSOR_ID_RESIZE_WEST
);
2227 fColumnMoveCursor
= new BCursor(B_CURSOR_ID_MOVE
);
2233 TitleView::~TitleView()
2238 #if DOUBLE_BUFFERED_COLUMN_RESIZE
2241 delete fUpSortArrow
;
2242 delete fDownSortArrow
;
2244 delete fResizeCursor
;
2245 delete fMaxResizeCursor
;
2246 delete fMinResizeCursor
;
2247 delete fColumnMoveCursor
;
2252 TitleView::ColumnAdded(BColumn
* column
)
2254 // fColumnsWidth += column->Width();
2255 FixScrollBar(false);
2261 TitleView::ColumnResized(BColumn
* column
, float oldWidth
)
2263 // fColumnsWidth += column->Width() - oldWidth;
2264 FixScrollBar(false);
2270 TitleView::SetColumnVisible(BColumn
* column
, bool visible
)
2272 if (column
->fVisible
== visible
)
2275 // If setting it visible, do this first so we can find its position
2276 // to invalidate. If hiding it, do it last.
2278 column
->fVisible
= visible
;
2281 GetTitleRect(column
, &titleInvalid
);
2283 // Now really set the visibility
2284 column
->fVisible
= visible
;
2287 // fColumnsWidth += column->Width();
2289 // fColumnsWidth -= column->Width();
2291 BRect
outlineInvalid(fOutlineView
->VisibleRect());
2292 outlineInvalid
.left
= titleInvalid
.left
;
2293 titleInvalid
.right
= outlineInvalid
.right
;
2295 Invalidate(titleInvalid
);
2296 fOutlineView
->Invalidate(outlineInvalid
);
2301 TitleView::GetTitleRect(BColumn
* findColumn
, BRect
* _rect
)
2303 float leftEdge
= MAX(kLeftMargin
, fMasterView
->LatchWidth());
2304 int32 numColumns
= fColumns
->CountItems();
2305 for (int index
= 0; index
< numColumns
; index
++) {
2306 BColumn
* column
= (BColumn
*) fColumns
->ItemAt(index
);
2307 if (!column
->IsVisible())
2310 if (column
== findColumn
) {
2311 _rect
->Set(leftEdge
, 0, leftEdge
+ column
->Width(),
2312 fVisibleRect
.bottom
);
2316 leftEdge
+= column
->Width() + 1;
2324 TitleView::FindColumn(BPoint position
, float* _leftEdge
)
2326 float leftEdge
= MAX(kLeftMargin
, fMasterView
->LatchWidth());
2327 int32 numColumns
= fColumns
->CountItems();
2328 for (int index
= 0; index
< numColumns
; index
++) {
2329 BColumn
* column
= (BColumn
*) fColumns
->ItemAt(index
);
2330 if (!column
->IsVisible())
2333 if (leftEdge
> position
.x
)
2336 if (position
.x
>= leftEdge
2337 && position
.x
<= leftEdge
+ column
->Width()) {
2338 *_leftEdge
= leftEdge
;
2342 leftEdge
+= column
->Width() + 1;
2350 TitleView::FixScrollBar(bool scrollToFit
)
2352 BScrollBar
* hScrollBar
= ScrollBar(B_HORIZONTAL
);
2353 if (hScrollBar
== NULL
)
2356 float virtualWidth
= _VirtualWidth();
2358 if (virtualWidth
> fVisibleRect
.Width()) {
2359 hScrollBar
->SetProportion(fVisibleRect
.Width() / virtualWidth
);
2361 // Perform the little trick if the user is scrolled over too far.
2362 // See OutlineView::FixScrollBar for a more in depth explanation
2363 float maxScrollBarValue
= virtualWidth
- fVisibleRect
.Width();
2364 if (scrollToFit
|| hScrollBar
->Value() <= maxScrollBarValue
) {
2365 hScrollBar
->SetRange(0.0, maxScrollBarValue
);
2366 hScrollBar
->SetSteps(50, fVisibleRect
.Width());
2368 } else if (hScrollBar
->Value() == 0.0) {
2369 // disable scroll bar.
2370 hScrollBar
->SetRange(0.0, 0.0);
2376 TitleView::DragSelectedColumn(BPoint position
)
2378 float invalidLeft
= fSelectedColumnRect
.left
;
2379 float invalidRight
= fSelectedColumnRect
.right
;
2382 int32 columnIndex
= FindColumn(position
, &leftEdge
);
2383 fSelectedColumnRect
.OffsetTo(leftEdge
, 0);
2385 MoveColumn(fSelectedColumn
, columnIndex
);
2387 fSelectedColumn
->fVisible
= true;
2388 ComputeDragBoundries(fSelectedColumn
, position
);
2390 // Redraw the new column position
2391 GetTitleRect(fSelectedColumn
, &fSelectedColumnRect
);
2392 invalidLeft
= MIN(fSelectedColumnRect
.left
, invalidLeft
);
2393 invalidRight
= MAX(fSelectedColumnRect
.right
, invalidRight
);
2395 Invalidate(BRect(invalidLeft
, 0, invalidRight
, fVisibleRect
.bottom
));
2396 fOutlineView
->Invalidate(BRect(invalidLeft
, 0, invalidRight
,
2397 fOutlineView
->VisibleRect().bottom
));
2399 DrawTitle(this, fSelectedColumnRect
, fSelectedColumn
, true);
2404 TitleView::MoveColumn(BColumn
* column
, int32 index
)
2406 fColumns
->RemoveItem((void*) column
);
2409 // Re-add the column at the end of the list.
2410 fColumns
->AddItem((void*) column
);
2412 fColumns
->AddItem((void*) column
, index
);
2418 TitleView::SetColumnFlags(column_flags flags
)
2420 fColumnFlags
= flags
;
2425 TitleView::MarginWidth() const
2427 return MAX(kLeftMargin
, fMasterView
->LatchWidth()) + kRightMargin
;
2432 TitleView::ResizeSelectedColumn(BPoint position
, bool preferred
)
2434 float minWidth
= fSelectedColumn
->MinWidth();
2435 float maxWidth
= fSelectedColumn
->MaxWidth();
2437 float oldWidth
= fSelectedColumn
->Width();
2438 float originalEdge
= fSelectedColumnRect
.left
+ oldWidth
;
2440 float width
= fOutlineView
->GetColumnPreferredWidth(fSelectedColumn
);
2441 fSelectedColumn
->SetWidth(width
);
2442 } else if (position
.x
> fSelectedColumnRect
.left
+ maxWidth
)
2443 fSelectedColumn
->SetWidth(maxWidth
);
2444 else if (position
.x
< fSelectedColumnRect
.left
+ minWidth
)
2445 fSelectedColumn
->SetWidth(minWidth
);
2447 fSelectedColumn
->SetWidth(position
.x
- fSelectedColumnRect
.left
- 1);
2449 float dX
= fSelectedColumnRect
.left
+ fSelectedColumn
->Width()
2452 float columnHeight
= fVisibleRect
.Height();
2453 BRect
originalRect(originalEdge
, 0, 1000000.0, columnHeight
);
2454 BRect
movedRect(originalRect
);
2455 movedRect
.OffsetBy(dX
, 0);
2457 // Update the size of the title column
2458 BRect
sourceRect(0, 0, fSelectedColumn
->Width(), columnHeight
);
2459 BRect
destRect(sourceRect
);
2460 destRect
.OffsetBy(fSelectedColumnRect
.left
, 0);
2462 #if DOUBLE_BUFFERED_COLUMN_RESIZE
2463 fDrawBuffer
->Lock();
2464 DrawTitle(fDrawBufferView
, sourceRect
, fSelectedColumn
, false);
2465 fDrawBufferView
->Sync();
2466 fDrawBuffer
->Unlock();
2468 CopyBits(originalRect
, movedRect
);
2469 DrawBitmap(fDrawBuffer
, sourceRect
, destRect
);
2471 CopyBits(originalRect
, movedRect
);
2472 DrawTitle(this, destRect
, fSelectedColumn
, false);
2475 // Update the body view
2476 BRect slaveSize
= fOutlineView
->VisibleRect();
2477 BRect
slaveSource(originalRect
);
2478 slaveSource
.bottom
= slaveSize
.bottom
;
2479 BRect
slaveDest(movedRect
);
2480 slaveDest
.bottom
= slaveSize
.bottom
;
2481 fOutlineView
->CopyBits(slaveSource
, slaveDest
);
2482 fOutlineView
->RedrawColumn(fSelectedColumn
, fSelectedColumnRect
.left
,
2483 fResizingFirstColumn
);
2485 // fColumnsWidth += dX;
2487 // Update the cursor
2488 if (fSelectedColumn
->Width() == minWidth
)
2489 SetViewCursor(fMinResizeCursor
, true);
2490 else if (fSelectedColumn
->Width() == maxWidth
)
2491 SetViewCursor(fMaxResizeCursor
, true);
2493 SetViewCursor(fResizeCursor
, true);
2495 ColumnResized(fSelectedColumn
, oldWidth
);
2501 TitleView::ComputeDragBoundries(BColumn
* findColumn
, BPoint
)
2503 float previousColumnLeftEdge
= -1000000.0;
2504 float nextColumnRightEdge
= 1000000.0;
2506 bool foundColumn
= false;
2507 float leftEdge
= MAX(kLeftMargin
, fMasterView
->LatchWidth());
2508 int32 numColumns
= fColumns
->CountItems();
2509 for (int index
= 0; index
< numColumns
; index
++) {
2510 BColumn
* column
= (BColumn
*) fColumns
->ItemAt(index
);
2511 if (!column
->IsVisible())
2514 if (column
== findColumn
) {
2520 nextColumnRightEdge
= leftEdge
+ column
->Width();
2523 previousColumnLeftEdge
= leftEdge
;
2525 leftEdge
+= column
->Width() + 1;
2528 float rightEdge
= leftEdge
+ findColumn
->Width();
2530 fLeftDragBoundry
= MIN(previousColumnLeftEdge
+ findColumn
->Width(),
2532 fRightDragBoundry
= MAX(nextColumnRightEdge
, rightEdge
);
2537 TitleView::DrawTitle(BView
* view
, BRect rect
, BColumn
* column
, bool depressed
)
2540 rgb_color borderColor
= mix_color(
2541 fMasterView
->Color(B_COLOR_HEADER_BACKGROUND
),
2542 make_color(0, 0, 0), 128);
2548 float baseline
= floor(drawRect
.top
+ fh
.ascent
2549 + (drawRect
.Height() + 1 - (fh
.ascent
+ fh
.descent
)) / 2);
2551 BRect bgRect
= rect
;
2553 rgb_color base
= ui_color(B_PANEL_BACKGROUND_COLOR
);
2554 view
->SetHighColor(tint_color(base
, B_DARKEN_2_TINT
));
2555 view
->StrokeLine(bgRect
.LeftBottom(), bgRect
.RightBottom());
2561 base
= tint_color(base
, B_DARKEN_1_TINT
);
2563 be_control_look
->DrawButtonBackground(view
, bgRect
, rect
, base
, 0,
2564 BControlLook::B_TOP_BORDER
| BControlLook::B_BOTTOM_BORDER
);
2566 view
->SetHighColor(tint_color(ui_color(B_PANEL_BACKGROUND_COLOR
),
2568 view
->StrokeLine(rect
.RightTop(), rect
.RightBottom());
2570 // If no column given, nothing else to draw.
2574 view
->SetHighColor(fMasterView
->Color(B_COLOR_HEADER_TEXT
));
2578 view
->SetFont(&font
);
2580 int sortIndex
= fSortColumns
->IndexOf(column
);
2581 if (sortIndex
>= 0) {
2582 // Draw sort notation.
2583 BPoint
upperLeft(drawRect
.right
- kSortIndicatorWidth
, baseline
);
2585 if (fSortColumns
->CountItems() > 1) {
2587 sprintf(str
, "%d", sortIndex
+ 1);
2588 const float w
= view
->StringWidth(str
);
2591 view
->SetDrawingMode(B_OP_COPY
);
2592 view
->MovePenTo(BPoint(upperLeft
.x
+ kSortIndicatorWidth
,
2594 view
->DrawString(str
);
2597 float bmh
= fDownSortArrow
->Bounds().Height()+1;
2599 view
->SetDrawingMode(B_OP_OVER
);
2601 if (column
->fSortAscending
) {
2602 BPoint
leftTop(upperLeft
.x
, drawRect
.top
+ (drawRect
.IntegerHeight()
2603 - fDownSortArrow
->Bounds().IntegerHeight()) / 2);
2604 view
->DrawBitmapAsync(fDownSortArrow
, leftTop
);
2606 BPoint
leftTop(upperLeft
.x
, drawRect
.top
+ (drawRect
.IntegerHeight()
2607 - fUpSortArrow
->Bounds().IntegerHeight()) / 2);
2608 view
->DrawBitmapAsync(fUpSortArrow
, leftTop
);
2611 upperLeft
.y
= baseline
- bmh
+ floor((fh
.ascent
+ fh
.descent
- bmh
) / 2);
2612 if (upperLeft
.y
< drawRect
.top
)
2613 upperLeft
.y
= drawRect
.top
;
2615 // Adjust title stuff for sort indicator
2616 drawRect
.right
= upperLeft
.x
- 2;
2619 if (drawRect
.right
> drawRect
.left
) {
2620 #if CONSTRAIN_CLIPPING_REGION
2621 BRegion
clipRegion(drawRect
);
2623 view
->ConstrainClippingRegion(&clipRegion
);
2625 view
->MovePenTo(BPoint(drawRect
.left
+ 8, baseline
));
2626 view
->SetDrawingMode(B_OP_OVER
);
2627 view
->SetHighColor(fMasterView
->Color(B_COLOR_HEADER_TEXT
));
2628 column
->DrawTitle(drawRect
, view
);
2630 #if CONSTRAIN_CLIPPING_REGION
2638 TitleView::_VirtualWidth() const
2640 float width
= MarginWidth();
2642 int32 count
= fColumns
->CountItems();
2643 for (int32 i
= 0; i
< count
; i
++) {
2644 BColumn
* column
= reinterpret_cast<BColumn
*>(fColumns
->ItemAt(i
));
2645 width
+= column
->Width();
2653 TitleView::Draw(BRect invalidRect
)
2655 float columnLeftEdge
= MAX(kLeftMargin
, fMasterView
->LatchWidth());
2656 for (int32 columnIndex
= 0; columnIndex
< fColumns
->CountItems();
2659 BColumn
* column
= (BColumn
*) fColumns
->ItemAt(columnIndex
);
2660 if (!column
->IsVisible())
2663 if (columnLeftEdge
> invalidRect
.right
)
2666 if (columnLeftEdge
+ column
->Width() >= invalidRect
.left
) {
2667 BRect
titleRect(columnLeftEdge
, 0,
2668 columnLeftEdge
+ column
->Width(), fVisibleRect
.Height());
2669 DrawTitle(this, titleRect
, column
,
2670 (fCurrentState
== DRAG_COLUMN_INSIDE_TITLE
2671 && fSelectedColumn
== column
));
2674 columnLeftEdge
+= column
->Width() + 1;
2678 // bevels for right title margin
2679 if (columnLeftEdge
<= invalidRect
.right
) {
2680 BRect
titleRect(columnLeftEdge
, 0, Bounds().right
+ 2,
2681 fVisibleRect
.Height());
2682 DrawTitle(this, titleRect
, NULL
, false);
2685 // bevels for left title margin
2686 if (invalidRect
.left
< MAX(kLeftMargin
, fMasterView
->LatchWidth())) {
2687 BRect
titleRect(0, 0, MAX(kLeftMargin
, fMasterView
->LatchWidth()) - 1,
2688 fVisibleRect
.Height());
2689 DrawTitle(this, titleRect
, NULL
, false);
2692 #if DRAG_TITLE_OUTLINE
2693 // (internal) column drag indicator
2694 if (fCurrentState
== DRAG_COLUMN_INSIDE_TITLE
) {
2695 BRect
dragRect(fSelectedColumnRect
);
2696 dragRect
.OffsetTo(fCurrentDragPosition
.x
- fClickPoint
.x
, 0);
2697 if (dragRect
.Intersects(invalidRect
)) {
2698 SetHighColor(0, 0, 255);
2699 StrokeRect(dragRect
);
2707 TitleView::ScrollTo(BPoint position
)
2709 fOutlineView
->ScrollBy(position
.x
- fVisibleRect
.left
, 0);
2710 fVisibleRect
.OffsetTo(position
.x
, position
.y
);
2712 // Perform the little trick if the user is scrolled over too far.
2713 // See OutlineView::ScrollTo for a more in depth explanation
2714 float maxScrollBarValue
= _VirtualWidth() - fVisibleRect
.Width();
2715 BScrollBar
* hScrollBar
= ScrollBar(B_HORIZONTAL
);
2717 hScrollBar
->GetRange(&min
, &max
);
2718 if (max
!= maxScrollBarValue
&& position
.x
> maxScrollBarValue
)
2721 _inherited::ScrollTo(position
);
2726 TitleView::MessageReceived(BMessage
* message
)
2728 if (message
->what
== kToggleColumn
) {
2730 if (message
->FindInt32("be:field_num", &num
) == B_OK
) {
2731 for (int index
= 0; index
< fColumns
->CountItems(); index
++) {
2732 BColumn
* column
= (BColumn
*) fColumns
->ItemAt(index
);
2736 if (column
->LogicalFieldNum() == num
)
2737 column
->SetVisible(!column
->IsVisible());
2743 BView::MessageReceived(message
);
2748 TitleView::MouseDown(BPoint position
)
2754 Window()->CurrentMessage()->FindInt32("buttons", &buttons
);
2755 if (buttons
== B_SECONDARY_MOUSE_BUTTON
2756 && (fColumnFlags
& B_ALLOW_COLUMN_POPUP
)) {
2757 // Right mouse button -- bring up menu to show/hide columns.
2758 if (fColumnPop
== NULL
)
2759 fColumnPop
= new BPopUpMenu("Columns", false, false);
2761 fColumnPop
->RemoveItems(0, fColumnPop
->CountItems(), true);
2762 BMessenger
me(this);
2763 for (int index
= 0; index
< fColumns
->CountItems(); index
++) {
2764 BColumn
* column
= (BColumn
*) fColumns
->ItemAt(index
);
2769 column
->GetColumnName(&name
);
2770 BMessage
* message
= new BMessage(kToggleColumn
);
2771 message
->AddInt32("be:field_num", column
->LogicalFieldNum());
2772 BMenuItem
* item
= new BMenuItem(name
.String(), message
);
2773 item
->SetMarked(column
->IsVisible());
2774 item
->SetTarget(me
);
2775 fColumnPop
->AddItem(item
);
2778 BPoint screenPosition
= ConvertToScreen(position
);
2779 BRect
sticky(screenPosition
, screenPosition
);
2780 sticky
.InsetBy(-5, -5);
2781 fColumnPop
->Go(ConvertToScreen(position
), true, false, sticky
, true);
2786 fResizingFirstColumn
= true;
2787 float leftEdge
= MAX(kLeftMargin
, fMasterView
->LatchWidth());
2788 for (int index
= 0; index
< fColumns
->CountItems(); index
++) {
2789 BColumn
* column
= (BColumn
*)fColumns
->ItemAt(index
);
2790 if (column
== NULL
|| !column
->IsVisible())
2793 if (leftEdge
> position
.x
+ kColumnResizeAreaWidth
/ 2)
2796 // check for resizing a column
2797 float rightEdge
= leftEdge
+ column
->Width();
2799 if (column
->ShowHeading()) {
2800 if (position
.x
> rightEdge
- kColumnResizeAreaWidth
/ 2
2801 && position
.x
< rightEdge
+ kColumnResizeAreaWidth
/ 2
2802 && column
->MaxWidth() > column
->MinWidth()
2803 && (fColumnFlags
& B_ALLOW_COLUMN_RESIZE
) != 0) {
2806 fSelectedColumn
= column
;
2807 fSelectedColumnRect
.Set(leftEdge
, 0, rightEdge
,
2808 fVisibleRect
.Height());
2809 Window()->CurrentMessage()->FindInt32("clicks", &clicks
);
2810 if (clicks
== 2 || buttons
== B_TERTIARY_MOUSE_BUTTON
) {
2811 ResizeSelectedColumn(position
, true);
2812 fCurrentState
= INACTIVE
;
2815 fCurrentState
= RESIZING_COLUMN
;
2816 fClickPoint
= BPoint(position
.x
- rightEdge
- 1,
2817 position
.y
- fSelectedColumnRect
.top
);
2818 SetMouseEventMask(B_POINTER_EVENTS
,
2819 B_LOCK_WINDOW_FOCUS
| B_NO_POINTER_HISTORY
);
2823 fResizingFirstColumn
= false;
2825 // check for clicking on a column
2826 if (position
.x
> leftEdge
&& position
.x
< rightEdge
) {
2827 fCurrentState
= PRESSING_COLUMN
;
2828 fSelectedColumn
= column
;
2829 fSelectedColumnRect
.Set(leftEdge
, 0, rightEdge
,
2830 fVisibleRect
.Height());
2831 DrawTitle(this, fSelectedColumnRect
, fSelectedColumn
, true);
2832 fClickPoint
= BPoint(position
.x
- fSelectedColumnRect
.left
,
2833 position
.y
- fSelectedColumnRect
.top
);
2834 SetMouseEventMask(B_POINTER_EVENTS
,
2835 B_LOCK_WINDOW_FOCUS
| B_NO_POINTER_HISTORY
);
2839 leftEdge
= rightEdge
+ 1;
2845 TitleView::MouseMoved(BPoint position
, uint32 transit
,
2846 const BMessage
* dragMessage
)
2851 // Handle column manipulation
2852 switch (fCurrentState
) {
2853 case RESIZING_COLUMN
:
2854 ResizeSelectedColumn(position
- BPoint(fClickPoint
.x
, 0));
2857 case PRESSING_COLUMN
: {
2858 if (abs((int32
)(position
.x
- (fClickPoint
.x
2859 + fSelectedColumnRect
.left
))) > kColumnResizeAreaWidth
2860 || abs((int32
)(position
.y
- (fClickPoint
.y
2861 + fSelectedColumnRect
.top
))) > kColumnResizeAreaWidth
) {
2862 // User has moved the mouse more than the tolerable amount,
2864 if (transit
== B_INSIDE_VIEW
|| transit
== B_ENTERED_VIEW
) {
2865 if(fColumnFlags
& B_ALLOW_COLUMN_MOVE
) {
2866 fCurrentState
= DRAG_COLUMN_INSIDE_TITLE
;
2867 ComputeDragBoundries(fSelectedColumn
, position
);
2868 SetViewCursor(fColumnMoveCursor
, true);
2869 #if DRAG_TITLE_OUTLINE
2870 BRect
invalidRect(fSelectedColumnRect
);
2871 invalidRect
.OffsetTo(position
.x
- fClickPoint
.x
, 0);
2872 fCurrentDragPosition
= position
;
2873 Invalidate(invalidRect
);
2877 if(fColumnFlags
& B_ALLOW_COLUMN_REMOVE
) {
2878 // Dragged outside view
2879 fCurrentState
= DRAG_COLUMN_OUTSIDE_TITLE
;
2880 fSelectedColumn
->SetVisible(false);
2881 BRect
dragRect(fSelectedColumnRect
);
2883 // There is a race condition where the mouse may have
2884 // moved by the time we get to handle this message.
2885 // If the user drags a column very quickly, this
2886 // results in the annoying bug where the cursor is
2887 // outside of the rectangle that is being dragged
2888 // around. Call GetMouse with the checkQueue flag set
2889 // to false so we can get the most recent position of
2890 // the mouse. This minimizes this problem (although
2891 // it is currently not possible to completely eliminate
2894 GetMouse(&position
, &buttons
, false);
2895 dragRect
.OffsetTo(position
.x
- fClickPoint
.x
,
2896 position
.y
- dragRect
.Height() / 2);
2897 BeginRectTracking(dragRect
, B_TRACK_WHOLE_RECT
);
2905 case DRAG_COLUMN_INSIDE_TITLE
: {
2906 if (transit
== B_EXITED_VIEW
2907 && (fColumnFlags
& B_ALLOW_COLUMN_REMOVE
)) {
2908 // Dragged outside view
2909 fCurrentState
= DRAG_COLUMN_OUTSIDE_TITLE
;
2910 fSelectedColumn
->SetVisible(false);
2911 BRect
dragRect(fSelectedColumnRect
);
2913 // See explanation above.
2915 GetMouse(&position
, &buttons
, false);
2917 dragRect
.OffsetTo(position
.x
- fClickPoint
.x
,
2918 position
.y
- fClickPoint
.y
);
2919 BeginRectTracking(dragRect
, B_TRACK_WHOLE_RECT
);
2920 } else if (position
.x
< fLeftDragBoundry
2921 || position
.x
> fRightDragBoundry
) {
2922 DragSelectedColumn(position
- BPoint(fClickPoint
.x
, 0));
2925 #if DRAG_TITLE_OUTLINE
2926 // Set up the invalid rect to include the rect for the previous
2927 // position of the drag rect, as well as the new one.
2928 BRect
invalidRect(fSelectedColumnRect
);
2929 invalidRect
.OffsetTo(fCurrentDragPosition
.x
- fClickPoint
.x
, 0);
2930 if (position
.x
< fCurrentDragPosition
.x
)
2931 invalidRect
.left
-= fCurrentDragPosition
.x
- position
.x
;
2933 invalidRect
.right
+= position
.x
- fCurrentDragPosition
.x
;
2935 fCurrentDragPosition
= position
;
2936 Invalidate(invalidRect
);
2941 case DRAG_COLUMN_OUTSIDE_TITLE
:
2942 if (transit
== B_ENTERED_VIEW
) {
2943 // Drag back into view
2945 fCurrentState
= DRAG_COLUMN_INSIDE_TITLE
;
2946 fSelectedColumn
->SetVisible(true);
2947 DragSelectedColumn(position
- BPoint(fClickPoint
.x
, 0));
2953 // Check for cursor changes if we are over the resize area for
2955 BColumn
* resizeColumn
= 0;
2956 float leftEdge
= MAX(kLeftMargin
, fMasterView
->LatchWidth());
2957 for (int index
= 0; index
< fColumns
->CountItems(); index
++) {
2958 BColumn
* column
= (BColumn
*) fColumns
->ItemAt(index
);
2959 if (!column
->IsVisible())
2962 if (leftEdge
> position
.x
+ kColumnResizeAreaWidth
/ 2)
2965 float rightEdge
= leftEdge
+ column
->Width();
2966 if (position
.x
> rightEdge
- kColumnResizeAreaWidth
/ 2
2967 && position
.x
< rightEdge
+ kColumnResizeAreaWidth
/ 2
2968 && column
->MaxWidth() > column
->MinWidth()) {
2969 resizeColumn
= column
;
2973 leftEdge
= rightEdge
+ 1;
2976 // Update the cursor
2978 if (resizeColumn
->Width() == resizeColumn
->MinWidth())
2979 SetViewCursor(fMinResizeCursor
, true);
2980 else if (resizeColumn
->Width() == resizeColumn
->MaxWidth())
2981 SetViewCursor(fMaxResizeCursor
, true);
2983 SetViewCursor(fResizeCursor
, true);
2985 SetViewCursor(B_CURSOR_SYSTEM_DEFAULT
, true);
2992 TitleView::MouseUp(BPoint position
)
2997 switch (fCurrentState
) {
2998 case RESIZING_COLUMN
:
2999 ResizeSelectedColumn(position
- BPoint(fClickPoint
.x
, 0));
3000 fCurrentState
= INACTIVE
;
3001 FixScrollBar(false);
3004 case PRESSING_COLUMN
: {
3005 if (fMasterView
->SortingEnabled()) {
3006 if (fSortColumns
->HasItem(fSelectedColumn
)) {
3007 if ((modifiers() & B_CONTROL_KEY
) == 0
3008 && fSortColumns
->CountItems() > 1) {
3009 fSortColumns
->MakeEmpty();
3010 fSortColumns
->AddItem(fSelectedColumn
);
3013 fSelectedColumn
->fSortAscending
3014 = !fSelectedColumn
->fSortAscending
;
3016 if ((modifiers() & B_CONTROL_KEY
) == 0)
3017 fSortColumns
->MakeEmpty();
3019 fSortColumns
->AddItem(fSelectedColumn
);
3020 fSelectedColumn
->fSortAscending
= true;
3023 fOutlineView
->StartSorting();
3026 fCurrentState
= INACTIVE
;
3031 case DRAG_COLUMN_INSIDE_TITLE
:
3032 fCurrentState
= INACTIVE
;
3034 #if DRAG_TITLE_OUTLINE
3035 Invalidate(); // xxx Can make this smaller
3037 Invalidate(fSelectedColumnRect
);
3039 SetViewCursor(B_CURSOR_SYSTEM_DEFAULT
, true);
3042 case DRAG_COLUMN_OUTSIDE_TITLE
:
3043 fCurrentState
= INACTIVE
;
3045 SetViewCursor(B_CURSOR_SYSTEM_DEFAULT
, true);
3055 TitleView::FrameResized(float width
, float height
)
3057 fVisibleRect
.right
= fVisibleRect
.left
+ width
;
3058 fVisibleRect
.bottom
= fVisibleRect
.top
+ height
;
3063 // #pragma mark - OutlineView
3066 OutlineView::OutlineView(BRect rect
, BList
* visibleColumns
, BList
* sortColumns
,
3067 BColumnListView
* listView
)
3069 BView(rect
, "outline_view", B_FOLLOW_ALL_SIDES
,
3070 B_WILL_DRAW
| B_FRAME_EVENTS
),
3071 fColumns(visibleColumns
),
3072 fSortColumns(sortColumns
),
3074 fVisibleRect(rect
.OffsetToCopy(0, 0)),
3077 fLastSelectedItem(0),
3078 fFirstSelectedItem(0),
3079 fSortThread(B_BAD_THREAD_ID
),
3080 fCurrentState(INACTIVE
),
3081 fMasterView(listView
),
3082 fSelectionMode(B_MULTIPLE_SELECTION_LIST
),
3088 fCurrentCode(B_OUTSIDE_VIEW
),
3094 SetViewColor(B_TRANSPARENT_COLOR
);
3096 #if DOUBLE_BUFFERED_COLUMN_RESIZE
3097 // TODO: This needs to be smart about the size of the buffer.
3098 // Also, the buffer can be shared with the title's buffer.
3099 BRect
doubleBufferRect(0, 0, 600, 35);
3100 fDrawBuffer
= new BBitmap(doubleBufferRect
, B_RGB32
, true);
3101 fDrawBufferView
= new BView(doubleBufferRect
, "double_buffer_view",
3102 B_FOLLOW_ALL_SIDES
, 0);
3103 fDrawBuffer
->Lock();
3104 fDrawBuffer
->AddChild(fDrawBufferView
);
3105 fDrawBuffer
->Unlock();
3109 fSelectionListDummyHead
.fNextSelected
= &fSelectionListDummyHead
;
3110 fSelectionListDummyHead
.fPrevSelected
= &fSelectionListDummyHead
;
3114 OutlineView::~OutlineView()
3116 #if DOUBLE_BUFFERED_COLUMN_RESIZE
3125 OutlineView::Clear()
3128 // Make sure selection list doesn't point to deleted rows!
3129 RecursiveDeleteRows(&fRows
, false);
3137 OutlineView::SetSelectionMode(list_view_type mode
)
3140 fSelectionMode
= mode
;
3145 OutlineView::SelectionMode() const
3147 return fSelectionMode
;
3152 OutlineView::Deselect(BRow
* row
)
3157 if (row
->fNextSelected
!= 0) {
3158 row
->fNextSelected
->fPrevSelected
= row
->fPrevSelected
;
3159 row
->fPrevSelected
->fNextSelected
= row
->fNextSelected
;
3160 row
->fNextSelected
= 0;
3161 row
->fPrevSelected
= 0;
3168 OutlineView::AddToSelection(BRow
* row
)
3173 if (row
->fNextSelected
== 0) {
3174 if (fSelectionMode
== B_SINGLE_SELECTION_LIST
)
3177 row
->fNextSelected
= fSelectionListDummyHead
.fNextSelected
;
3178 row
->fPrevSelected
= &fSelectionListDummyHead
;
3179 row
->fNextSelected
->fPrevSelected
= row
;
3180 row
->fPrevSelected
->fNextSelected
= row
;
3183 if (FindVisibleRect(row
, &invalidRect
))
3184 Invalidate(invalidRect
);
3190 OutlineView::RecursiveDeleteRows(BRowContainer
* list
, bool isOwner
)
3196 BRow
* row
= list
->RemoveItemAt(0L);
3200 if (row
->fChildList
)
3201 RecursiveDeleteRows(row
->fChildList
, true);
3212 OutlineView::RedrawColumn(BColumn
* column
, float leftEdge
, bool isFirstColumn
)
3214 // TODO: Remove code duplication (private function which takes a view
3215 // pointer, pass "this" in non-double buffered mode)!
3216 // Watch out for sourceRect versus destRect though!
3223 bool tintedLine
= true;
3224 for (RecursiveOutlineIterator
iterator(&fRows
); iterator
.CurrentRow();
3225 line
+= iterator
.CurrentRow()->Height() + 1, iterator
.GoToNext()) {
3227 BRow
* row
= iterator
.CurrentRow();
3228 float rowHeight
= row
->Height();
3229 if (line
> fVisibleRect
.bottom
)
3231 tintedLine
= !tintedLine
;
3233 if (line
+ rowHeight
>= fVisibleRect
.top
) {
3234 #if DOUBLE_BUFFERED_COLUMN_RESIZE
3235 BRect
sourceRect(0, 0, column
->Width(), rowHeight
);
3237 BRect
destRect(leftEdge
, line
, leftEdge
+ column
->Width(),
3240 rgb_color highColor
;
3242 if (row
->fNextSelected
!= 0) {
3244 highColor
= fMasterView
->Color(B_COLOR_EDIT_BACKGROUND
);
3245 lowColor
= fMasterView
->Color(B_COLOR_EDIT_BACKGROUND
);
3247 highColor
= fMasterView
->Color(B_COLOR_SELECTION
);
3248 lowColor
= fMasterView
->Color(B_COLOR_SELECTION
);
3251 highColor
= fMasterView
->Color(B_COLOR_BACKGROUND
);
3252 lowColor
= fMasterView
->Color(B_COLOR_BACKGROUND
);
3255 lowColor
= tint_color(lowColor
, kTintedLineTint
);
3258 #if DOUBLE_BUFFERED_COLUMN_RESIZE
3259 fDrawBuffer
->Lock();
3261 fDrawBufferView
->SetHighColor(highColor
);
3262 fDrawBufferView
->SetLowColor(lowColor
);
3266 fDrawBufferView
->SetFont(&font
);
3267 fDrawBufferView
->FillRect(sourceRect
, B_SOLID_LOW
);
3269 if (isFirstColumn
) {
3270 // If this is the first column, double buffer drawing the latch
3272 destRect
.left
+= iterator
.CurrentLevel() * kOutlineLevelIndent
3273 - fMasterView
->LatchWidth();
3274 sourceRect
.left
+= iterator
.CurrentLevel() * kOutlineLevelIndent
3275 - fMasterView
->LatchWidth();
3277 LatchType pos
= B_NO_LATCH
;
3278 if (row
->HasLatch())
3279 pos
= row
->fIsExpanded
? B_OPEN_LATCH
: B_CLOSED_LATCH
;
3281 BRect
latchRect(sourceRect
);
3282 latchRect
.right
= latchRect
.left
+ fMasterView
->LatchWidth();
3283 fMasterView
->DrawLatch(fDrawBufferView
, latchRect
, pos
, row
);
3286 BField
* field
= row
->GetField(column
->fFieldID
);
3288 BRect
fieldRect(sourceRect
);
3290 fieldRect
.left
+= fMasterView
->LatchWidth();
3292 #if CONSTRAIN_CLIPPING_REGION
3293 BRegion
clipRegion(fieldRect
);
3294 fDrawBufferView
->PushState();
3295 fDrawBufferView
->ConstrainClippingRegion(&clipRegion
);
3297 fDrawBufferView
->SetHighColor(fMasterView
->Color(
3298 row
->fNextSelected
? B_COLOR_SELECTION_TEXT
3300 float baseline
= floor(fieldRect
.top
+ fh
.ascent
3301 + (fieldRect
.Height() + 1 - (fh
.ascent
+fh
.descent
)) / 2);
3302 fDrawBufferView
->MovePenTo(fieldRect
.left
+ 8, baseline
);
3303 column
->DrawField(field
, fieldRect
, fDrawBufferView
);
3304 #if CONSTRAIN_CLIPPING_REGION
3305 fDrawBufferView
->PopState();
3309 if (fFocusRow
== row
&& !fEditMode
&& fMasterView
->IsFocus()
3310 && Window()->IsActive()) {
3311 fDrawBufferView
->SetHighColor(fMasterView
->Color(
3312 B_COLOR_ROW_DIVIDER
));
3313 fDrawBufferView
->StrokeRect(BRect(-1, sourceRect
.top
,
3314 10000.0, sourceRect
.bottom
));
3317 fDrawBufferView
->Sync();
3318 fDrawBuffer
->Unlock();
3319 SetDrawingMode(B_OP_COPY
);
3320 DrawBitmap(fDrawBuffer
, sourceRect
, destRect
);
3324 SetHighColor(highColor
);
3325 SetLowColor(lowColor
);
3326 FillRect(destRect
, B_SOLID_LOW
);
3328 BField
* field
= row
->GetField(column
->fFieldID
);
3330 #if CONSTRAIN_CLIPPING_REGION
3331 BRegion
clipRegion(destRect
);
3333 ConstrainClippingRegion(&clipRegion
);
3335 SetHighColor(fMasterView
->Color(row
->fNextSelected
3336 ? B_COLOR_SELECTION_TEXT
: B_COLOR_TEXT
));
3337 float baseline
= floor(destRect
.top
+ fh
.ascent
3338 + (destRect
.Height() + 1 - (fh
.ascent
+ fh
.descent
)) / 2);
3339 MovePenTo(destRect
.left
+ 8, baseline
);
3340 column
->DrawField(field
, destRect
, this);
3341 #if CONSTRAIN_CLIPPING_REGION
3346 if (fFocusRow
== row
&& !fEditMode
&& fMasterView
->IsFocus()
3347 && Window()->IsActive()) {
3348 SetHighColor(fMasterView
->Color(B_COLOR_ROW_DIVIDER
));
3349 StrokeRect(BRect(0, destRect
.top
, 10000.0, destRect
.bottom
));
3358 OutlineView::Draw(BRect invalidBounds
)
3361 BRegion invalidRegion
;
3362 GetClippingRegion(&invalidRegion
);
3369 bool tintedLine
= true;
3370 int32 numColumns
= fColumns
->CountItems();
3371 for (RecursiveOutlineIterator
iterator(&fRows
); iterator
.CurrentRow();
3372 iterator
.GoToNext()) {
3373 BRow
* row
= iterator
.CurrentRow();
3374 if (line
> invalidBounds
.bottom
)
3377 tintedLine
= !tintedLine
;
3378 float rowHeight
= row
->Height();
3380 if (line
>= invalidBounds
.top
- rowHeight
) {
3381 bool isFirstColumn
= true;
3382 float fieldLeftEdge
= MAX(kLeftMargin
, fMasterView
->LatchWidth());
3384 // setup background color
3386 if (row
->fNextSelected
!= 0) {
3387 if (Window()->IsActive()) {
3389 lowColor
= fMasterView
->Color(B_COLOR_EDIT_BACKGROUND
);
3391 lowColor
= fMasterView
->Color(B_COLOR_SELECTION
);
3394 lowColor
= fMasterView
->Color(B_COLOR_NON_FOCUS_SELECTION
);
3396 lowColor
= fMasterView
->Color(B_COLOR_BACKGROUND
);
3398 lowColor
= tint_color(lowColor
, kTintedLineTint
);
3400 for (int columnIndex
= 0; columnIndex
< numColumns
; columnIndex
++) {
3401 BColumn
* column
= (BColumn
*) fColumns
->ItemAt(columnIndex
);
3402 if (!column
->IsVisible())
3405 if (!isFirstColumn
&& fieldLeftEdge
> invalidBounds
.right
)
3408 if (fieldLeftEdge
+ column
->Width() >= invalidBounds
.left
) {
3409 BRect
fullRect(fieldLeftEdge
, line
,
3410 fieldLeftEdge
+ column
->Width(), line
+ rowHeight
);
3412 bool clippedFirstColumn
= false;
3413 // This happens when a column is indented past the
3414 // beginning of the next column.
3416 SetHighColor(lowColor
);
3418 BRect
destRect(fullRect
);
3419 if (isFirstColumn
) {
3420 fullRect
.left
-= fMasterView
->LatchWidth();
3421 destRect
.left
+= iterator
.CurrentLevel()
3422 * kOutlineLevelIndent
;
3423 if (destRect
.left
>= destRect
.right
) {
3425 FillRect(BRect(0, line
, fieldLeftEdge
3426 + column
->Width(), line
+ rowHeight
));
3427 clippedFirstColumn
= true;
3430 FillRect(BRect(0, line
, MAX(kLeftMargin
,
3431 fMasterView
->LatchWidth()), line
+ row
->Height()));
3436 if (!clippedFirstColumn
3437 && invalidRegion
.Intersects(fullRect
)) {
3439 if (!clippedFirstColumn
) {
3441 FillRect(fullRect
); // Using color set above
3443 // Draw the latch widget if it has one.
3444 if (isFirstColumn
) {
3445 if (row
== fTargetRow
3446 && fCurrentState
== LATCH_CLICKED
) {
3447 // Note that this only occurs if the user is
3448 // holding down a latch while items are added
3449 // in the background.
3452 GetMouse(&pos
, &buttons
);
3453 if (fLatchRect
.Contains(pos
)) {
3454 fMasterView
->DrawLatch(this, fLatchRect
,
3455 B_PRESSED_LATCH
, fTargetRow
);
3457 fMasterView
->DrawLatch(this, fLatchRect
,
3458 row
->fIsExpanded
? B_OPEN_LATCH
3459 : B_CLOSED_LATCH
, fTargetRow
);
3462 LatchType pos
= B_NO_LATCH
;
3463 if (row
->HasLatch())
3464 pos
= row
->fIsExpanded
? B_OPEN_LATCH
3467 fMasterView
->DrawLatch(this,
3469 - fMasterView
->LatchWidth(),
3470 destRect
.top
, destRect
.left
,
3471 destRect
.bottom
), pos
, row
);
3475 SetHighColor(fMasterView
->HighColor());
3476 // The master view just holds the high color for us.
3477 SetLowColor(lowColor
);
3479 BField
* field
= row
->GetField(column
->fFieldID
);
3481 #if CONSTRAIN_CLIPPING_REGION
3482 BRegion
clipRegion(destRect
);
3484 ConstrainClippingRegion(&clipRegion
);
3486 SetHighColor(fMasterView
->Color(
3487 row
->fNextSelected
? B_COLOR_SELECTION_TEXT
3489 float baseline
= floor(destRect
.top
+ fh
.ascent
3490 + (destRect
.Height() + 1
3491 - (fh
.ascent
+fh
.descent
)) / 2);
3492 MovePenTo(destRect
.left
+ 8, baseline
);
3493 column
->DrawField(field
, destRect
, this);
3494 #if CONSTRAIN_CLIPPING_REGION
3501 isFirstColumn
= false;
3502 fieldLeftEdge
+= column
->Width() + 1;
3505 if (fieldLeftEdge
<= invalidBounds
.right
) {
3506 SetHighColor(lowColor
);
3507 FillRect(BRect(fieldLeftEdge
, line
, invalidBounds
.right
,
3512 // indicate the keyboard focus row
3513 if (fFocusRow
== row
&& !fEditMode
&& fMasterView
->IsFocus()
3514 && Window()->IsActive()) {
3515 SetHighColor(fMasterView
->Color(B_COLOR_ROW_DIVIDER
));
3516 StrokeRect(BRect(0, line
, 10000.0, line
+ rowHeight
));
3519 line
+= rowHeight
+ 1;
3522 if (line
<= invalidBounds
.bottom
) {
3523 // fill background below last item
3524 SetHighColor(fMasterView
->Color(B_COLOR_BACKGROUND
));
3525 FillRect(BRect(invalidBounds
.left
, line
, invalidBounds
.right
,
3526 invalidBounds
.bottom
));
3529 // Draw the drop target line
3530 if (fDropHighlightY
!= -1) {
3531 InvertRect(BRect(0, fDropHighlightY
- kDropHighlightLineHeight
/ 2,
3532 1000000, fDropHighlightY
+ kDropHighlightLineHeight
/ 2));
3538 OutlineView::FindRow(float ypos
, int32
* _rowIndent
, float* _top
)
3540 if (_rowIndent
&& _top
) {
3542 for (RecursiveOutlineIterator
iterator(&fRows
); iterator
.CurrentRow();
3543 iterator
.GoToNext()) {
3545 BRow
* row
= iterator
.CurrentRow();
3549 float rowHeight
= row
->Height();
3550 if (ypos
<= line
+ rowHeight
) {
3552 *_rowIndent
= iterator
.CurrentLevel();
3556 line
+= rowHeight
+ 1;
3563 void OutlineView::SetMouseTrackingEnabled(bool enabled
)
3565 fTrackMouse
= enabled
;
3566 if (!enabled
&& fDropHighlightY
!= -1) {
3567 // Erase the old target line
3568 InvertRect(BRect(0, fDropHighlightY
- kDropHighlightLineHeight
/ 2,
3569 1000000, fDropHighlightY
+ kDropHighlightLineHeight
/ 2));
3570 fDropHighlightY
= -1;
3576 // Note that this interaction is not totally safe. If items are added to
3577 // the list in the background, the widget rect will be incorrect, possibly
3578 // resulting in drawing glitches. The code that adds items needs to be a little smarter
3579 // about invalidating state.
3582 OutlineView::MouseDown(BPoint position
)
3585 fMasterView
->MakeFocus(true);
3587 // Check to see if the user is clicking on a widget to open a section
3589 bool reset_click_count
= false;
3592 BRow
* row
= FindRow(position
.y
, &indent
, &rowTop
);
3595 // Update fCurrentField
3596 bool handle_field
= false;
3597 BField
* new_field
= 0;
3599 BColumn
* new_column
= 0;
3602 if (position
.y
>= 0) {
3603 if (position
.x
>= 0) {
3605 for (int32 c
= 0; c
< fMasterView
->CountColumns(); c
++) {
3606 new_column
= fMasterView
->ColumnAt(c
);
3607 if (!new_column
->IsVisible())
3609 if ((MAX(kLeftMargin
, fMasterView
->LatchWidth()) + x
)
3610 + new_column
->Width() >= position
.x
) {
3611 if (new_column
->WantsEvents()) {
3612 new_field
= row
->GetField(c
);
3614 FindRect(new_row
,&new_rect
);
3615 new_rect
.left
= MAX(kLeftMargin
,
3616 fMasterView
->LatchWidth()) + x
;
3617 new_rect
.right
= new_rect
.left
3618 + new_column
->Width() - 1;
3619 handle_field
= true;
3623 x
+= new_column
->Width();
3628 // Handle mouse down
3631 fFieldRect
= new_rect
;
3632 fCurrentColumn
= new_column
;
3633 fCurrentRow
= new_row
;
3634 fCurrentField
= new_field
;
3635 fCurrentCode
= B_INSIDE_VIEW
;
3636 BMessage
* message
= Window()->CurrentMessage();
3638 message
->FindInt32("buttons", &buttons
);
3639 fCurrentColumn
->MouseDown(fMasterView
, fCurrentRow
,
3640 fCurrentField
, fFieldRect
, position
, buttons
);
3646 fTargetRowTop
= rowTop
;
3647 FindVisibleRect(fFocusRow
, &fFocusRowRect
);
3649 float leftWidgetBoundry
= indent
* kOutlineLevelIndent
3650 + MAX(kLeftMargin
, fMasterView
->LatchWidth())
3651 - fMasterView
->LatchWidth();
3652 fLatchRect
.Set(leftWidgetBoundry
, rowTop
, leftWidgetBoundry
3653 + fMasterView
->LatchWidth(), rowTop
+ row
->Height());
3654 if (fLatchRect
.Contains(position
) && row
->HasLatch()) {
3655 fCurrentState
= LATCH_CLICKED
;
3656 if (fTargetRow
->fNextSelected
!= 0)
3657 SetHighColor(fMasterView
->Color(B_COLOR_SELECTION
));
3659 SetHighColor(fMasterView
->Color(B_COLOR_BACKGROUND
));
3661 FillRect(fLatchRect
);
3662 if (fLatchRect
.Contains(position
)) {
3663 fMasterView
->DrawLatch(this, fLatchRect
, B_PRESSED_LATCH
,
3666 fMasterView
->DrawLatch(this, fLatchRect
,
3667 fTargetRow
->fIsExpanded
? B_OPEN_LATCH
3668 : B_CLOSED_LATCH
, row
);
3671 Invalidate(fFocusRowRect
);
3672 fFocusRow
= fTargetRow
;
3673 FindVisibleRect(fFocusRow
, &fFocusRowRect
);
3675 ASSERT(fTargetRow
!= 0);
3677 if ((modifiers() & B_CONTROL_KEY
) == 0)
3680 if ((modifiers() & B_SHIFT_KEY
) != 0 && fFirstSelectedItem
!= 0
3681 && fSelectionMode
== B_MULTIPLE_SELECTION_LIST
) {
3682 SelectRange(fFirstSelectedItem
, fTargetRow
);
3685 if (fTargetRow
->fNextSelected
!= 0) {
3687 fTargetRow
->fNextSelected
->fPrevSelected
3688 = fTargetRow
->fPrevSelected
;
3689 fTargetRow
->fPrevSelected
->fNextSelected
3690 = fTargetRow
->fNextSelected
;
3691 fTargetRow
->fPrevSelected
= 0;
3692 fTargetRow
->fNextSelected
= 0;
3693 fFirstSelectedItem
= NULL
;
3696 if (fSelectionMode
== B_SINGLE_SELECTION_LIST
)
3699 fTargetRow
->fNextSelected
3700 = fSelectionListDummyHead
.fNextSelected
;
3701 fTargetRow
->fPrevSelected
3702 = &fSelectionListDummyHead
;
3703 fTargetRow
->fNextSelected
->fPrevSelected
= fTargetRow
;
3704 fTargetRow
->fPrevSelected
->fNextSelected
= fTargetRow
;
3705 fFirstSelectedItem
= fTargetRow
;
3708 Invalidate(BRect(fVisibleRect
.left
, fTargetRowTop
,
3710 fTargetRowTop
+ fTargetRow
->Height()));
3713 fCurrentState
= ROW_CLICKED
;
3714 if (fLastSelectedItem
!= fTargetRow
)
3715 reset_click_count
= true;
3716 fLastSelectedItem
= fTargetRow
;
3717 fMasterView
->SelectionChanged();
3722 SetMouseEventMask(B_POINTER_EVENTS
, B_LOCK_WINDOW_FOCUS
|
3723 B_NO_POINTER_HISTORY
);
3725 } else if (fFocusRow
!= 0) {
3726 // User clicked in open space, unhighlight focus row.
3727 FindVisibleRect(fFocusRow
, &fFocusRowRect
);
3729 Invalidate(fFocusRowRect
);
3732 // We stash the click counts here because the 'clicks' field
3733 // is not in the CurrentMessage() when MouseUp is called... ;(
3734 if (reset_click_count
)
3737 Window()->CurrentMessage()->FindInt32("clicks", &fClickCount
);
3738 fClickPoint
= position
;
3744 OutlineView::MouseMoved(BPoint position
, uint32
/*transit*/,
3745 const BMessage
* /*dragMessage*/)
3748 // Update fCurrentField
3749 bool handle_field
= false;
3750 BField
* new_field
= 0;
3752 BColumn
* new_column
= 0;
3753 BRect
new_rect(0,0,0,0);
3754 if (position
.y
>=0 ) {
3757 BRow
* row
= FindRow(position
.y
, &indent
, &top
);
3758 if (row
&& position
.x
>=0 ) {
3760 for (int32 c
=0;c
<fMasterView
->CountColumns();c
++) {
3761 new_column
= fMasterView
->ColumnAt(c
);
3762 if (!new_column
->IsVisible())
3764 if ((MAX(kLeftMargin
,
3765 fMasterView
->LatchWidth()) + x
) + new_column
->Width()
3768 if(new_column
->WantsEvents()) {
3769 new_field
= row
->GetField(c
);
3771 FindRect(new_row
,&new_rect
);
3772 new_rect
.left
= MAX(kLeftMargin
,
3773 fMasterView
->LatchWidth()) + x
;
3774 new_rect
.right
= new_rect
.left
3775 + new_column
->Width() - 1;
3776 handle_field
= true;
3780 x
+= new_column
->Width();
3785 // Handle mouse moved
3787 if (new_field
!= fCurrentField
) {
3788 if (fCurrentField
) {
3789 fCurrentColumn
->MouseMoved(fMasterView
, fCurrentRow
,
3790 fCurrentField
, fFieldRect
, position
, 0,
3791 fCurrentCode
= B_EXITED_VIEW
);
3793 fCurrentColumn
= new_column
;
3794 fCurrentRow
= new_row
;
3795 fCurrentField
= new_field
;
3796 fFieldRect
= new_rect
;
3797 if (fCurrentField
) {
3798 fCurrentColumn
->MouseMoved(fMasterView
, fCurrentRow
,
3799 fCurrentField
, fFieldRect
, position
, 0,
3800 fCurrentCode
= B_ENTERED_VIEW
);
3803 if (fCurrentField
) {
3804 fCurrentColumn
->MouseMoved(fMasterView
, fCurrentRow
,
3805 fCurrentField
, fFieldRect
, position
, 0,
3806 fCurrentCode
= B_INSIDE_VIEW
);
3810 if (fCurrentField
) {
3811 fCurrentColumn
->MouseMoved(fMasterView
, fCurrentRow
,
3812 fCurrentField
, fFieldRect
, position
, 0,
3813 fCurrentCode
= B_EXITED_VIEW
);
3820 if (fCurrentField
) {
3821 if (fFieldRect
.Contains(position
)) {
3822 if (fCurrentCode
== B_OUTSIDE_VIEW
3823 || fCurrentCode
== B_EXITED_VIEW
) {
3824 fCurrentColumn
->MouseMoved(fMasterView
, fCurrentRow
,
3825 fCurrentField
, fFieldRect
, position
, 1,
3826 fCurrentCode
= B_ENTERED_VIEW
);
3828 fCurrentColumn
->MouseMoved(fMasterView
, fCurrentRow
,
3829 fCurrentField
, fFieldRect
, position
, 1,
3830 fCurrentCode
= B_INSIDE_VIEW
);
3833 if (fCurrentCode
== B_INSIDE_VIEW
3834 || fCurrentCode
== B_ENTERED_VIEW
) {
3835 fCurrentColumn
->MouseMoved(fMasterView
, fCurrentRow
,
3836 fCurrentField
, fFieldRect
, position
, 1,
3837 fCurrentCode
= B_EXITED_VIEW
);
3839 fCurrentColumn
->MouseMoved(fMasterView
, fCurrentRow
,
3840 fCurrentField
, fFieldRect
, position
, 1,
3841 fCurrentCode
= B_OUTSIDE_VIEW
);
3849 switch (fCurrentState
) {
3851 if (fTargetRow
->fNextSelected
!= 0)
3852 SetHighColor(fMasterView
->Color(B_COLOR_SELECTION
));
3854 SetHighColor(fMasterView
->Color(B_COLOR_BACKGROUND
));
3856 FillRect(fLatchRect
);
3857 if (fLatchRect
.Contains(position
)) {
3858 fMasterView
->DrawLatch(this, fLatchRect
, B_PRESSED_LATCH
,
3861 fMasterView
->DrawLatch(this, fLatchRect
,
3862 fTargetRow
->fIsExpanded
? B_OPEN_LATCH
: B_CLOSED_LATCH
,
3868 if (abs((int)(position
.x
- fClickPoint
.x
)) > kRowDragSensitivity
3869 || abs((int)(position
.y
- fClickPoint
.y
))
3870 > kRowDragSensitivity
) {
3871 fCurrentState
= DRAGGING_ROWS
;
3872 fMasterView
->InitiateDrag(fClickPoint
,
3873 fTargetRow
->fNextSelected
!= 0);
3881 if (fTrackMouse
/*&& message*/) {
3882 if (fVisibleRect
.Contains(position
)) {
3885 BRow
* target
= FindRow(position
.y
, &indent
, &top
);
3887 SetFocusRow(target
, true);
3895 if (fTrackMouse
/*&& message*/) {
3896 // Draw a highlight line...
3897 if (fVisibleRect
.Contains(position
)) {
3900 BRow
* target
= FindRow(position
.y
, &indent
, &top
);
3901 if (target
== fRollOverRow
)
3905 FindRect(fRollOverRow
, &rect
);
3908 fRollOverRow
= target
;
3910 SetFocusRow(fRollOverRow
,false);
3913 SetDrawingMode(B_OP_BLEND
);
3914 SetHighColor(255, 255, 255, 255);
3916 FindRect(fRollOverRow
, &rect
);
3924 FindRect(fRollOverRow
, &rect
);
3926 fRollOverRow
= NULL
;
3937 OutlineView::MouseUp(BPoint position
)
3939 if (fCurrentField
) {
3940 fCurrentColumn
->MouseUp(fMasterView
, fCurrentRow
, fCurrentField
);
3947 switch (fCurrentState
) {
3949 if (fLatchRect
.Contains(position
)) {
3950 fMasterView
->ExpandOrCollapse(fTargetRow
,
3951 !fTargetRow
->fIsExpanded
);
3954 Invalidate(fLatchRect
);
3955 fCurrentState
= INACTIVE
;
3960 && abs((int)fClickPoint
.x
- (int)position
.x
)
3961 < kDoubleClickMoveSensitivity
3962 && abs((int)fClickPoint
.y
- (int)position
.y
)
3963 < kDoubleClickMoveSensitivity
) {
3964 fMasterView
->ItemInvoked();
3966 fCurrentState
= INACTIVE
;
3970 fCurrentState
= INACTIVE
;
3974 if (fDropHighlightY
!= -1) {
3976 fDropHighlightY
- kDropHighlightLineHeight
/ 2,
3977 1000000, fDropHighlightY
+ kDropHighlightLineHeight
/ 2));
3978 // Erase the old target line
3979 fDropHighlightY
= -1;
3986 OutlineView::MessageReceived(BMessage
* message
)
3988 if (message
->WasDropped()) {
3989 fMasterView
->MessageDropped(message
,
3990 ConvertFromScreen(message
->DropPoint()));
3992 BView::MessageReceived(message
);
3998 OutlineView::ChangeFocusRow(bool up
, bool updateSelection
,
3999 bool addToCurrentSelection
)
4003 float newRowPos
= 0;
4004 float verticalScroll
= 0;
4007 // A row currently has the focus, get information about it
4008 newRowPos
= fFocusRowRect
.top
+ (up
? -4 : fFocusRow
->Height() + 4);
4009 if (newRowPos
< fVisibleRect
.top
+ 20)
4010 verticalScroll
= newRowPos
- 20;
4011 else if (newRowPos
> fVisibleRect
.bottom
- 20)
4012 verticalScroll
= newRowPos
- fVisibleRect
.Height() + 20;
4014 newRowPos
= fVisibleRect
.top
+ 2;
4015 // no row is currently focused, set this to the top of the window
4016 // so we will select the first visible item in the list.
4018 BRow
* newRow
= FindRow(newRowPos
, &indent
, &top
);
4021 fFocusRowRect
.right
= 10000;
4022 Invalidate(fFocusRowRect
);
4024 BRow
* oldFocusRow
= fFocusRow
;
4026 fFocusRowRect
.top
= top
;
4027 fFocusRowRect
.left
= 0;
4028 fFocusRowRect
.right
= 10000;
4029 fFocusRowRect
.bottom
= fFocusRowRect
.top
+ fFocusRow
->Height();
4030 Invalidate(fFocusRowRect
);
4032 if (updateSelection
) {
4033 if (!addToCurrentSelection
4034 || fSelectionMode
== B_SINGLE_SELECTION_LIST
) {
4038 // if the focus row isn't selected, add it to the selection
4039 if (fFocusRow
->fNextSelected
== 0) {
4040 fFocusRow
->fNextSelected
4041 = fSelectionListDummyHead
.fNextSelected
;
4042 fFocusRow
->fPrevSelected
= &fSelectionListDummyHead
;
4043 fFocusRow
->fNextSelected
->fPrevSelected
= fFocusRow
;
4044 fFocusRow
->fPrevSelected
->fNextSelected
= fFocusRow
;
4045 } else if (oldFocusRow
!= NULL
4046 && fSelectionListDummyHead
.fNextSelected
== oldFocusRow
4047 && (((IndexOf(oldFocusRow
->fNextSelected
)
4048 < IndexOf(oldFocusRow
)) == up
)
4049 || fFocusRow
== oldFocusRow
->fNextSelected
)) {
4050 // if the focus row is selected, if:
4051 // 1. the previous focus row is last in the selection
4052 // 2a. the next selected row is now the focus row
4053 // 2b. or the next selected row is beyond the focus row
4054 // in the move direction
4055 // then deselect the previous focus row
4056 fSelectionListDummyHead
.fNextSelected
4057 = oldFocusRow
->fNextSelected
;
4058 if (fSelectionListDummyHead
.fNextSelected
!= NULL
) {
4059 fSelectionListDummyHead
.fNextSelected
->fPrevSelected
4060 = &fSelectionListDummyHead
;
4061 oldFocusRow
->fNextSelected
= NULL
;
4063 oldFocusRow
->fPrevSelected
= NULL
;
4066 fLastSelectedItem
= fFocusRow
;
4069 Invalidate(fFocusRowRect
);
4071 if (verticalScroll
!= 0) {
4072 BScrollBar
* vScrollBar
= ScrollBar(B_VERTICAL
);
4074 vScrollBar
->GetRange(&min
, &max
);
4075 if (verticalScroll
< min
)
4076 verticalScroll
= min
;
4077 else if (verticalScroll
> max
)
4078 verticalScroll
= max
;
4080 vScrollBar
->SetValue(verticalScroll
);
4083 if (newRow
&& updateSelection
)
4084 fMasterView
->SelectionChanged();
4089 OutlineView::MoveFocusToVisibleRect()
4092 ChangeFocusRow(true, true, false);
4097 OutlineView::CurrentSelection(BRow
* lastSelected
) const
4100 if (lastSelected
== 0)
4101 row
= fSelectionListDummyHead
.fNextSelected
;
4103 row
= lastSelected
->fNextSelected
;
4106 if (row
== &fSelectionListDummyHead
)
4114 OutlineView::ToggleFocusRowSelection(bool selectRange
)
4119 if (selectRange
&& fSelectionMode
== B_MULTIPLE_SELECTION_LIST
)
4120 SelectRange(fLastSelectedItem
, fFocusRow
);
4122 if (fFocusRow
->fNextSelected
!= 0) {
4124 fFocusRow
->fNextSelected
->fPrevSelected
= fFocusRow
->fPrevSelected
;
4125 fFocusRow
->fPrevSelected
->fNextSelected
= fFocusRow
->fNextSelected
;
4126 fFocusRow
->fPrevSelected
= 0;
4127 fFocusRow
->fNextSelected
= 0;
4130 if (fSelectionMode
== B_SINGLE_SELECTION_LIST
)
4133 fFocusRow
->fNextSelected
= fSelectionListDummyHead
.fNextSelected
;
4134 fFocusRow
->fPrevSelected
= &fSelectionListDummyHead
;
4135 fFocusRow
->fNextSelected
->fPrevSelected
= fFocusRow
;
4136 fFocusRow
->fPrevSelected
->fNextSelected
= fFocusRow
;
4140 fLastSelectedItem
= fFocusRow
;
4141 fMasterView
->SelectionChanged();
4142 Invalidate(fFocusRowRect
);
4147 OutlineView::ToggleFocusRowOpen()
4150 fMasterView
->ExpandOrCollapse(fFocusRow
, !fFocusRow
->fIsExpanded
);
4155 OutlineView::ExpandOrCollapse(BRow
* parentRow
, bool expand
)
4157 // TODO: Could use CopyBits here to speed things up.
4159 if (parentRow
== NULL
)
4162 if (parentRow
->fIsExpanded
== expand
)
4165 parentRow
->fIsExpanded
= expand
;
4168 if (FindRect(parentRow
, &parentRect
)) {
4169 // Determine my new height
4170 float subTreeHeight
= 0.0;
4171 if (parentRow
->fIsExpanded
)
4172 for (RecursiveOutlineIterator
iterator(parentRow
->fChildList
);
4173 iterator
.CurrentRow();
4177 subTreeHeight
+= iterator
.CurrentRow()->Height()+1;
4180 for (RecursiveOutlineIterator
iterator(parentRow
->fChildList
);
4181 iterator
.CurrentRow();
4185 subTreeHeight
-= iterator
.CurrentRow()->Height()+1;
4187 fItemsHeight
+= subTreeHeight
;
4189 // Adjust focus row if necessary.
4190 if (FindRect(fFocusRow
, &fFocusRowRect
) == false) {
4191 // focus row is in a subtree that has collapsed,
4192 // move it up to the parent.
4193 fFocusRow
= parentRow
;
4194 FindRect(fFocusRow
, &fFocusRowRect
);
4197 Invalidate(BRect(0, parentRect
.top
, fVisibleRect
.right
,
4198 fVisibleRect
.bottom
));
4199 FixScrollBar(false);
4204 OutlineView::RemoveRow(BRow
* row
)
4210 bool parentIsVisible
;
4211 FindParent(row
, &parentRow
, &parentIsVisible
);
4212 // NOTE: This could be a root row without a parent, in which case
4213 // it is always visible, though.
4215 // Adjust height for the visible sub-tree that is going to be removed.
4216 float subTreeHeight
= 0.0f
;
4217 if (parentIsVisible
&& (parentRow
== NULL
|| parentRow
->fIsExpanded
)) {
4218 // The row itself is visible at least.
4219 subTreeHeight
= row
->Height() + 1;
4220 if (row
->fIsExpanded
) {
4221 // Adjust for the height of visible sub-items as well.
4222 // (By default, the iterator follows open branches only.)
4223 for (RecursiveOutlineIterator
iterator(row
->fChildList
);
4224 iterator
.CurrentRow(); iterator
.GoToNext())
4225 subTreeHeight
+= iterator
.CurrentRow()->Height() + 1;
4228 if (FindRect(row
, &invalid
)) {
4229 invalid
.bottom
= Bounds().bottom
;
4230 if (invalid
.IsValid())
4231 Invalidate(invalid
);
4235 fItemsHeight
-= subTreeHeight
;
4237 FixScrollBar(false);
4240 if (FindRow(fVisibleRect
.top
, &indent
, &top
) == NULL
&& ScrollBar(B_VERTICAL
) != NULL
) {
4241 // after removing this row, no rows are actually visible any more,
4242 // force a scroll to make them visible again
4243 if (fItemsHeight
> fVisibleRect
.Height())
4244 ScrollBy(0.0, fItemsHeight
- fVisibleRect
.Height() - Bounds().top
);
4246 ScrollBy(0.0, -Bounds().top
);
4248 if (parentRow
!= NULL
) {
4249 parentRow
->fChildList
->RemoveItem(row
);
4250 if (parentRow
->fChildList
->CountItems() == 0) {
4251 delete parentRow
->fChildList
;
4252 parentRow
->fChildList
= 0;
4253 // It was the last child row of the parent, which also means the
4254 // latch disappears.
4255 BRect parentRowRect
;
4256 if (parentIsVisible
&& FindRect(parentRow
, &parentRowRect
))
4257 Invalidate(parentRowRect
);
4260 fRows
.RemoveItem(row
);
4262 // Adjust focus row if necessary.
4263 if (fFocusRow
&& !FindRect(fFocusRow
, &fFocusRowRect
)) {
4264 // focus row is in a subtree that is gone, move it up to the parent.
4265 fFocusRow
= parentRow
;
4267 FindRect(fFocusRow
, &fFocusRowRect
);
4270 // Remove this from the selection if necessary
4271 if (row
->fNextSelected
!= 0) {
4272 row
->fNextSelected
->fPrevSelected
= row
->fPrevSelected
;
4273 row
->fPrevSelected
->fNextSelected
= row
->fNextSelected
;
4274 row
->fPrevSelected
= 0;
4275 row
->fNextSelected
= 0;
4276 fMasterView
->SelectionChanged();
4286 OutlineView::RowList()
4293 OutlineView::UpdateRow(BRow
* row
)
4296 // Determine if this row has changed its sort order
4297 BRow
* parentRow
= NULL
;
4298 bool parentIsVisible
= false;
4299 FindParent(row
, &parentRow
, &parentIsVisible
);
4301 BRowContainer
* list
= (parentRow
== NULL
) ? &fRows
: parentRow
->fChildList
;
4304 int32 rowIndex
= list
->IndexOf(row
);
4305 ASSERT(rowIndex
>= 0);
4306 ASSERT(list
->ItemAt(rowIndex
) == row
);
4308 bool rowMoved
= false;
4309 if (rowIndex
> 0 && CompareRows(list
->ItemAt(rowIndex
- 1), row
) > 0)
4312 if (rowIndex
< list
->CountItems() - 1 && CompareRows(list
->ItemAt(rowIndex
+ 1),
4317 // Sort location of this row has changed.
4318 // Remove and re-add in the right spot
4319 SortList(list
, parentIsVisible
&& (parentRow
== NULL
|| parentRow
->fIsExpanded
));
4320 } else if (parentIsVisible
&& (parentRow
== NULL
|| parentRow
->fIsExpanded
)) {
4322 if (FindVisibleRect(row
, &invalidRect
))
4323 Invalidate(invalidRect
);
4331 OutlineView::AddRow(BRow
* row
, int32 Index
, BRow
* parentRow
)
4336 row
->fParent
= parentRow
;
4338 if (fMasterView
->SortingEnabled() && !fSortColumns
->IsEmpty()) {
4339 // Ignore index here.
4341 if (parentRow
->fChildList
== NULL
)
4342 parentRow
->fChildList
= new BRowContainer
;
4344 AddSorted(parentRow
->fChildList
, row
);
4346 AddSorted(&fRows
, row
);
4348 // Note, a -1 index implies add to end if sorting is not enabled
4350 if (parentRow
->fChildList
== 0)
4351 parentRow
->fChildList
= new BRowContainer
;
4353 if (Index
< 0 || Index
> parentRow
->fChildList
->CountItems())
4354 parentRow
->fChildList
->AddItem(row
);
4356 parentRow
->fChildList
->AddItem(row
, Index
);
4358 if (Index
< 0 || Index
>= fRows
.CountItems())
4361 fRows
.AddItem(row
, Index
);
4365 if (parentRow
== 0 || parentRow
->fIsExpanded
)
4366 fItemsHeight
+= row
->Height() + 1;
4368 FixScrollBar(false);
4371 bool newRowIsInOpenBranch
= FindRect(row
, &newRowRect
);
4373 if (fFocusRow
&& fFocusRowRect
.top
> newRowRect
.bottom
) {
4374 // The focus row has moved.
4375 Invalidate(fFocusRowRect
);
4376 FindRect(fFocusRow
, &fFocusRowRect
);
4377 Invalidate(fFocusRowRect
);
4380 if (newRowIsInOpenBranch
) {
4381 if (fCurrentState
== INACTIVE
) {
4382 if (newRowRect
.bottom
< fVisibleRect
.top
) {
4383 // The new row is totally above the current viewport, move
4384 // everything down and redraw the first line.
4385 BRect
source(fVisibleRect
);
4386 BRect
dest(fVisibleRect
);
4387 source
.bottom
-= row
->Height() + 1;
4388 dest
.top
+= row
->Height() + 1;
4389 CopyBits(source
, dest
);
4390 Invalidate(BRect(fVisibleRect
.left
, fVisibleRect
.top
, fVisibleRect
.right
,
4391 fVisibleRect
.top
+ newRowRect
.Height()));
4392 } else if (newRowRect
.top
< fVisibleRect
.bottom
) {
4393 // New item is somewhere in the current region. Scroll everything
4394 // beneath it down and invalidate just the new row rect.
4395 BRect
source(fVisibleRect
.left
, newRowRect
.top
, fVisibleRect
.right
,
4396 fVisibleRect
.bottom
- newRowRect
.Height());
4398 dest
.OffsetBy(0, newRowRect
.Height() + 1);
4399 CopyBits(source
, dest
);
4400 Invalidate(newRowRect
);
4401 } // otherwise, this is below the currently visible region
4403 // Adding the item may have caused the item that the user is currently
4404 // selected to move. This would cause annoying drawing and interaction
4405 // bugs, as the position of that item is cached. If this happens, resize
4406 // the scroll bar, then scroll back so the selected item is in view.
4408 if (FindRect(fTargetRow
, &targetRect
)) {
4409 float delta
= targetRect
.top
- fTargetRowTop
;
4411 // This causes a jump because ScrollBy will copy a chunk of the view.
4412 // Since the actual contents of the view have been offset, we don't
4413 // want this, we just want to change the virtual origin of the window.
4414 // Constrain the clipping region so everything is clipped out so no
4417 // xxx this currently doesn't work if the scroll bars aren't enabled.
4418 // everything will still move anyway. A minor annoyance.
4419 BRegion emptyRegion
;
4420 ConstrainClippingRegion(&emptyRegion
);
4424 ConstrainClippingRegion(NULL
);
4426 fTargetRowTop
+= delta
;
4427 fClickPoint
.y
+= delta
;
4428 fLatchRect
.OffsetBy(0, delta
);
4434 // If the parent was previously childless, it will need to have a latch
4437 if (parentRow
&& parentRow
->fChildList
->CountItems() == 1
4438 && FindVisibleRect(parentRow
, &parentRect
))
4439 Invalidate(parentRect
);
4444 OutlineView::FixScrollBar(bool scrollToFit
)
4446 BScrollBar
* vScrollBar
= ScrollBar(B_VERTICAL
);
4448 if (fItemsHeight
> fVisibleRect
.Height()) {
4449 float maxScrollBarValue
= fItemsHeight
- fVisibleRect
.Height();
4450 vScrollBar
->SetProportion(fVisibleRect
.Height() / fItemsHeight
);
4452 // If the user is scrolled down too far when making the range smaller, the list
4453 // will jump suddenly, which is undesirable. In this case, don't fix the scroll
4454 // bar here. In ScrollTo, it checks to see if this has occured, and will
4455 // fix the scroll bars sneakily if the user has scrolled up far enough.
4456 if (scrollToFit
|| vScrollBar
->Value() <= maxScrollBarValue
) {
4457 vScrollBar
->SetRange(0.0, maxScrollBarValue
);
4458 vScrollBar
->SetSteps(20.0, fVisibleRect
.Height());
4460 } else if (vScrollBar
->Value() == 0.0 || fItemsHeight
== 0.0)
4461 vScrollBar
->SetRange(0.0, 0.0); // disable scroll bar.
4467 OutlineView::AddSorted(BRowContainer
* list
, BRow
* row
)
4470 // Find general vicinity with binary search.
4472 int32 upper
= list
->CountItems()-1;
4473 while( lower
< upper
) {
4474 int32 middle
= lower
+ (upper
-lower
+1)/2;
4475 int32 cmp
= CompareRows(row
, list
->ItemAt(middle
));
4476 if( cmp
< 0 ) upper
= middle
-1;
4477 else if( cmp
> 0 ) lower
= middle
+1;
4478 else lower
= upper
= middle
;
4481 // At this point, 'upper' and 'lower' at the last found item.
4482 // Arbitrarily use 'upper' and determine the final insertion
4483 // point -- either before or after this item.
4484 if( upper
< 0 ) upper
= 0;
4485 else if( upper
< list
->CountItems() ) {
4486 if( CompareRows(row
, list
->ItemAt(upper
)) > 0 ) upper
++;
4489 if (upper
>= list
->CountItems())
4490 list
->AddItem(row
); // Adding to end.
4492 list
->AddItem(row
, upper
); // Insert
4498 OutlineView::CompareRows(BRow
* row1
, BRow
* row2
)
4500 int32
itemCount (fSortColumns
->CountItems());
4502 for (int32 index
= 0; index
< itemCount
; index
++) {
4503 BColumn
* column
= (BColumn
*) fSortColumns
->ItemAt(index
);
4505 BField
* field1
= (BField
*) row1
->GetField(column
->fFieldID
);
4506 BField
* field2
= (BField
*) row2
->GetField(column
->fFieldID
);
4507 if (field1
&& field2
)
4508 comp
= column
->CompareFields(field1
, field2
);
4510 if (!column
->fSortAscending
)
4522 OutlineView::FrameResized(float width
, float height
)
4524 fVisibleRect
.right
= fVisibleRect
.left
+ width
;
4525 fVisibleRect
.bottom
= fVisibleRect
.top
+ height
;
4527 _inherited::FrameResized(width
, height
);
4532 OutlineView::ScrollTo(BPoint position
)
4534 fVisibleRect
.OffsetTo(position
.x
, position
.y
);
4536 // In FixScrollBar, we might not have been able to change the size of
4537 // the scroll bar because the user was scrolled down too far. Take
4538 // this opportunity to sneak it in if we can.
4539 BScrollBar
* vScrollBar
= ScrollBar(B_VERTICAL
);
4540 float maxScrollBarValue
= fItemsHeight
- fVisibleRect
.Height();
4542 vScrollBar
->GetRange(&min
, &max
);
4543 if (max
!= maxScrollBarValue
&& position
.y
> maxScrollBarValue
)
4546 _inherited::ScrollTo(position
);
4551 OutlineView::VisibleRect() const
4553 return fVisibleRect
;
4558 OutlineView::FindVisibleRect(BRow
* row
, BRect
* _rect
)
4562 for (RecursiveOutlineIterator
iterator(&fRows
); iterator
.CurrentRow();
4563 iterator
.GoToNext()) {
4564 if (line
> fVisibleRect
.bottom
)
4567 if (iterator
.CurrentRow() == row
) {
4568 _rect
->Set(fVisibleRect
.left
, line
, fVisibleRect
.right
,
4569 line
+ row
->Height());
4573 line
+= iterator
.CurrentRow()->Height() + 1;
4581 OutlineView::FindRect(const BRow
* row
, BRect
* _rect
)
4584 for (RecursiveOutlineIterator
iterator(&fRows
); iterator
.CurrentRow();
4585 iterator
.GoToNext()) {
4586 if (iterator
.CurrentRow() == row
) {
4587 _rect
->Set(fVisibleRect
.left
, line
, fVisibleRect
.right
,
4588 line
+ row
->Height());
4592 line
+= iterator
.CurrentRow()->Height() + 1;
4600 OutlineView::ScrollTo(const BRow
* row
)
4603 if (FindRect(row
, &rect
)) {
4604 BRect bounds
= Bounds();
4605 if (rect
.top
< bounds
.top
)
4606 ScrollTo(BPoint(bounds
.left
, rect
.top
));
4607 else if (rect
.bottom
> bounds
.bottom
)
4608 ScrollBy(0, rect
.bottom
- bounds
.bottom
);
4614 OutlineView::DeselectAll()
4616 // Invalidate all selected rows
4618 for (RecursiveOutlineIterator
iterator(&fRows
); iterator
.CurrentRow();
4619 iterator
.GoToNext()) {
4620 if (line
> fVisibleRect
.bottom
)
4623 BRow
* row
= iterator
.CurrentRow();
4624 if (line
+ row
->Height() > fVisibleRect
.top
) {
4625 if (row
->fNextSelected
!= 0)
4626 Invalidate(BRect(fVisibleRect
.left
, line
, fVisibleRect
.right
,
4627 line
+ row
->Height()));
4630 line
+= row
->Height() + 1;
4633 // Set items not selected
4634 while (fSelectionListDummyHead
.fNextSelected
!= &fSelectionListDummyHead
) {
4635 BRow
* row
= fSelectionListDummyHead
.fNextSelected
;
4636 row
->fNextSelected
->fPrevSelected
= row
->fPrevSelected
;
4637 row
->fPrevSelected
->fNextSelected
= row
->fNextSelected
;
4638 row
->fNextSelected
= 0;
4639 row
->fPrevSelected
= 0;
4645 OutlineView::FocusRow() const
4652 OutlineView::SetFocusRow(BRow
* row
, bool Select
)
4656 AddToSelection(row
);
4658 if (fFocusRow
== row
)
4661 Invalidate(fFocusRowRect
); // invalidate previous
4663 fTargetRow
= fFocusRow
= row
;
4665 FindVisibleRect(fFocusRow
, &fFocusRowRect
);
4666 Invalidate(fFocusRowRect
); // invalidate current
4668 fFocusRowRect
.right
= 10000;
4669 fMasterView
->SelectionChanged();
4675 OutlineView::SortList(BRowContainer
* list
, bool isVisible
)
4680 = (BRow
**) BObjectList
<BRow
>::Private(list
).AsBList()->Items();
4681 int32 numItems
= list
->CountItems();
4683 for (h
= 1; h
< numItems
/ 9; h
= 3 * h
+ 1)
4686 for (;h
> 0; h
/= 3) {
4687 for (int step
= h
; step
< numItems
; step
++) {
4688 BRow
* temp
= items
[step
];
4690 for (i
= step
- h
; i
>= 0; i
-= h
) {
4691 if (CompareRows(temp
, items
[i
]) < 0)
4692 items
[i
+ h
] = items
[i
];
4697 items
[i
+ h
] = temp
;
4704 InvalidateCachedPositions();
4705 int lockCount
= Window()->CountLocks();
4706 for (int i
= 0; i
< lockCount
; i
++)
4710 if (!Window()->Lock())
4711 return false; // Window is gone...
4719 OutlineView::DeepSortThreadEntry(void* _outlineView
)
4721 ((OutlineView
*) _outlineView
)->DeepSort();
4727 OutlineView::DeepSort()
4729 struct stack_entry
{
4731 BRowContainer
* list
;
4736 stack
[stackTop
].list
= &fRows
;
4737 stack
[stackTop
].isVisible
= true;
4738 stack
[stackTop
].listIndex
= 0;
4741 if (Window()->Lock() == false)
4744 bool doneSorting
= false;
4745 while (!doneSorting
&& !fSortCancelled
) {
4747 stack_entry
* currentEntry
= &stack
[stackTop
];
4749 // xxx Can make the invalidate area smaller by finding the rect for the
4750 // parent item and using that as the top of the invalid rect.
4752 bool haveLock
= SortList(currentEntry
->list
, currentEntry
->isVisible
);
4754 return ; // window is gone.
4757 InvalidateCachedPositions();
4758 if (fCurrentState
!= INACTIVE
)
4759 fCurrentState
= INACTIVE
; // sorry...
4762 bool foundNextList
= false;
4763 while (!foundNextList
&& !fSortCancelled
) {
4764 for (int32 index
= currentEntry
->listIndex
; index
< currentEntry
->list
->CountItems();
4766 BRow
* parentRow
= currentEntry
->list
->ItemAt(index
);
4767 BRowContainer
* childList
= parentRow
->fChildList
;
4768 if (childList
!= 0) {
4769 currentEntry
->listIndex
= index
+ 1;
4771 ASSERT(stackTop
< kMaxDepth
);
4772 stack
[stackTop
].listIndex
= 0;
4773 stack
[stackTop
].list
= childList
;
4774 stack
[stackTop
].isVisible
= (currentEntry
->isVisible
&& parentRow
->fIsExpanded
);
4775 foundNextList
= true;
4780 if (!foundNextList
) {
4782 if (--stackTop
< 0) {
4787 currentEntry
= &stack
[stackTop
];
4797 OutlineView::StartSorting()
4799 // If this view is not yet attached to a window, don't start a sort thread!
4800 if (Window() == NULL
)
4803 if (fSortThread
!= B_BAD_THREAD_ID
) {
4805 if (get_thread_info(fSortThread
, &tinfo
) == B_OK
) {
4806 // Unlock window so this won't deadlock (sort thread is probably
4807 // waiting to lock window).
4809 int lockCount
= Window()->CountLocks();
4810 for (int i
= 0; i
< lockCount
; i
++)
4813 fSortCancelled
= true;
4815 wait_for_thread(fSortThread
, &status
);
4818 if (!Window()->Lock())
4819 return ; // Window is gone...
4823 fSortCancelled
= false;
4824 fSortThread
= spawn_thread(DeepSortThreadEntry
, "sort_thread", B_NORMAL_PRIORITY
, this);
4825 resume_thread(fSortThread
);
4830 OutlineView::SelectRange(BRow
* start
, BRow
* end
)
4835 if (start
== end
) // start is always selected when this is called
4838 RecursiveOutlineIterator
iterator(&fRows
, false);
4839 while (iterator
.CurrentRow() != 0) {
4840 if (iterator
.CurrentRow() == end
) {
4841 // reverse selection, swap to fix special case
4846 } else if (iterator
.CurrentRow() == start
)
4849 iterator
.GoToNext();
4853 BRow
* row
= iterator
.CurrentRow();
4855 if (row
->fNextSelected
== 0) {
4856 row
->fNextSelected
= fSelectionListDummyHead
.fNextSelected
;
4857 row
->fPrevSelected
= &fSelectionListDummyHead
;
4858 row
->fNextSelected
->fPrevSelected
= row
;
4859 row
->fPrevSelected
->fNextSelected
= row
;
4867 iterator
.GoToNext();
4870 Invalidate(); // xxx make invalidation smaller
4875 OutlineView::FindParent(BRow
* row
, BRow
** outParent
, bool* outParentIsVisible
)
4877 bool result
= false;
4878 if (row
!= NULL
&& outParent
!= NULL
) {
4879 *outParent
= row
->fParent
;
4881 if (outParentIsVisible
!= NULL
) {
4882 // Walk up the parent chain to determine if this row is visible
4883 *outParentIsVisible
= true;
4884 for (BRow
* currentRow
= row
->fParent
; currentRow
!= NULL
;
4885 currentRow
= currentRow
->fParent
) {
4886 if (!currentRow
->fIsExpanded
) {
4887 *outParentIsVisible
= false;
4893 result
= *outParent
!= NULL
;
4901 OutlineView::IndexOf(BRow
* row
)
4904 if (row
->fParent
== 0)
4905 return fRows
.IndexOf(row
);
4907 ASSERT(row
->fParent
->fChildList
);
4908 return row
->fParent
->fChildList
->IndexOf(row
);
4916 OutlineView::InvalidateCachedPositions()
4919 FindRect(fFocusRow
, &fFocusRowRect
);
4924 OutlineView::GetColumnPreferredWidth(BColumn
* column
)
4926 float preferred
= 0.0;
4927 for (RecursiveOutlineIterator
iterator(&fRows
); BRow
* row
=
4928 iterator
.CurrentRow(); iterator
.GoToNext()) {
4929 BField
* field
= row
->GetField(column
->fFieldID
);
4931 float width
= column
->GetPreferredWidth(field
, this)
4932 + iterator
.CurrentLevel() * kOutlineLevelIndent
;
4933 preferred
= max_c(preferred
, width
);
4938 column
->GetColumnName(&name
);
4939 preferred
= max_c(preferred
, StringWidth(name
));
4941 // Constrain to preferred width. This makes the method do a little
4942 // more than asked, but it's for convenience.
4943 if (preferred
< column
->MinWidth())
4944 preferred
= column
->MinWidth();
4945 else if (preferred
> column
->MaxWidth())
4946 preferred
= column
->MaxWidth();
4955 RecursiveOutlineIterator::RecursiveOutlineIterator(BRowContainer
* list
,
4956 bool openBranchesOnly
)
4959 fCurrentListIndex(0),
4960 fCurrentListDepth(0),
4961 fOpenBranchesOnly(openBranchesOnly
)
4963 if (list
== 0 || list
->CountItems() == 0)
4966 fCurrentList
= list
;
4971 RecursiveOutlineIterator::CurrentRow() const
4973 if (fCurrentList
== 0)
4976 return fCurrentList
->ItemAt(fCurrentListIndex
);
4981 RecursiveOutlineIterator::GoToNext()
4983 if (fCurrentList
== 0)
4985 if (fCurrentListIndex
< 0 || fCurrentListIndex
>= fCurrentList
->CountItems()) {
4990 BRow
* currentRow
= fCurrentList
->ItemAt(fCurrentListIndex
);
4992 if (currentRow
->fChildList
&& (currentRow
->fIsExpanded
|| !fOpenBranchesOnly
)
4993 && currentRow
->fChildList
->CountItems() > 0) {
4995 // Put current list on the stack if it needs to be revisited.
4996 if (fCurrentListIndex
< fCurrentList
->CountItems() - 1) {
4997 fStack
[fStackIndex
].fRowSet
= fCurrentList
;
4998 fStack
[fStackIndex
].fIndex
= fCurrentListIndex
+ 1;
4999 fStack
[fStackIndex
].fDepth
= fCurrentListDepth
;
5003 fCurrentList
= currentRow
->fChildList
;
5004 fCurrentListIndex
= 0;
5005 fCurrentListDepth
++;
5006 } else if (fCurrentListIndex
< fCurrentList
->CountItems() - 1)
5007 fCurrentListIndex
++; // next item in current list
5008 else if (--fStackIndex
>= 0) {
5009 fCurrentList
= fStack
[fStackIndex
].fRowSet
;
5010 fCurrentListIndex
= fStack
[fStackIndex
].fIndex
;
5011 fCurrentListDepth
= fStack
[fStackIndex
].fDepth
;
5019 RecursiveOutlineIterator::CurrentLevel() const
5021 return fCurrentListDepth
;