xref: /haiku/src/kits/interface/ColumnListView.cpp (revision d3d8b26997fac34a84981e6d2b649521de2cc45a)
1 /*
2 Open Tracker License
3 
4 Terms and Conditions
5 
6 Copyright (c) 1991-2000, Be Incorporated. All rights reserved.
7 
8 Permission is hereby granted, free of charge, to any person obtaining a copy of
9 this software and associated documentation files (the "Software"), to deal in
10 the Software without restriction, including without limitation the rights to
11 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
12 of the Software, and to permit persons to whom the Software is furnished to do
13 so, subject to the following conditions:
14 
15 The above copyright notice and this permission notice applies to all licensees
16 and shall be included in all copies or substantial portions of the Software.
17 
18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF TITLE, MERCHANTABILITY,
20 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21 BE INCORPORATED BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
22 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION
23 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 
25 Except as contained in this notice, the name of Be Incorporated shall not be
26 used in advertising or otherwise to promote the sale, use or other dealings in
27 this Software without prior written authorization from Be Incorporated.
28 
29 Tracker(TM), Be(R), BeOS(R), and BeIA(TM) are trademarks or registered trademarks
30 of Be Incorporated in the United States and other countries. Other brand product
31 names are registered trademarks or trademarks of their respective holders.
32 All rights reserved.
33 */
34 
35 /*******************************************************************************
36 /
37 /	File:			ColumnListView.cpp
38 /
39 /   Description:    Experimental multi-column list view.
40 /
41 /	Copyright 2000+, Be Incorporated, All Rights Reserved
42 /					 By Jeff Bush
43 /
44 *******************************************************************************/
45 
46 #include <typeinfo>
47 
48 #include <stdio.h>
49 #include <stdlib.h>
50 
51 #include <Application.h>
52 #include <Bitmap.h>
53 #if _INCLUDES_CLASS_CURSOR
54 #include <Cursor.h>
55 #endif
56 #include <Debug.h>
57 #include <GraphicsDefs.h>
58 #include <MenuItem.h>
59 #include <PopUpMenu.h>
60 #include <Region.h>
61 #include <ScrollBar.h>
62 #include <String.h>
63 #include <Window.h>
64 
65 #include "ObjectList.h"
66 #include "ColumnListView.h"
67 #include "ColorTools.h"
68 /*
69 #ifndef _ARCHIVE_DEFS_H
70 #include <archive_defs.h>
71 #endif
72 */
73 #define DOUBLE_BUFFERED_COLUMN_RESIZE 1
74 #define SMART_REDRAW 1
75 #define DRAG_TITLE_OUTLINE 1
76 #define CONSTRAIN_CLIPPING_REGION 1
77 #define LOWER_SCROLLBAR 1
78 
79 namespace BPrivate {
80 
81 static const unsigned char kResizeCursorData[] = {
82 	16, 1, 8, 8,
83 	0, 0, 1, 0x80, 1, 0x80, 1, 0x80, 9, 0x90, 0x19, 0x98, 0x39, 0x09c, 0x79, 0x9e,
84 	0x79, 0x9e, 0x39, 0x9c, 0x19, 0x98, 0x9, 0x90, 1, 0x80, 1, 0x80, 1, 0x80, 0, 0,
85 	3, 0xc0, 3, 0xc0, 3, 0xc0, 0xf, 0xf0, 0x1f, 0xf8, 0x3f, 0xfa, 0x7f, 0xfe, 0xff, 0xff,
86 	0xff, 0xff, 0x7f, 0xfe, 0x3f, 0xfa, 0x1f, 0xf8, 0xf, 0xf0, 3, 0xc0, 3, 0xc0, 3, 0xc0
87 };
88 
89 static const unsigned char kMaxResizeCursorData[] = {
90 	16, 1, 8, 8,
91 	0, 0, 1, 0x80, 1, 0x80, 1, 0x80, 9, 0x80, 0x19, 0x80, 0x39, 0x80, 0x79, 0x80,
92 	0x79, 0x80, 0x39, 0x80, 0x19, 0x80, 0x9, 0x80, 1, 0x80, 1, 0x80, 1, 0x80, 0, 0,
93 	3, 0xc0, 3, 0xc0, 3, 0xc0, 0xf, 0xc0, 0x1f, 0xc0, 0x3f, 0xc0, 0x7f, 0xc0, 0xff, 0xc0,
94 	0xff, 0xc0, 0x7f, 0xc0, 0x3f, 0xc0, 0x1f, 0xc0, 0xf, 0xc0, 3, 0xc0, 3, 0xc0, 3, 0xc0
95 };
96 
97 static const unsigned char kMinResizeCursorData[] = {
98 	16, 1, 8, 8,
99 	0, 0, 1, 0x80, 1, 0x80, 1, 0x80, 1, 0x90, 1, 0x98, 1, 0x09c, 1, 0x9e,
100 	1, 0x9e, 1, 0x9c, 1, 0x98, 1, 0x90, 1, 0x80, 1, 0x80, 1, 0x80, 0, 0,
101 	3, 0xc0, 3, 0xc0, 3, 0xc0, 3, 0xf0, 3, 0xf8, 3, 0xfa, 3, 0xfe, 3, 0xff,
102 	3, 0xff, 3, 0xfe, 3, 0xfa, 3, 0xf8, 3, 0xf0, 3, 0xc0, 3, 0xc0, 3, 0xc0
103 };
104 
105 static const unsigned char kColumnMoveCursorData[] = {
106 	16, 1, 8, 8,
107 	1, 0x80, 3, 0xc0, 7, 0xe0, 0, 0, 0, 0, 0x20, 4, 0x61, 0x86, 0xe3, 0xc7,
108 	0xe3, 0xc7, 0x61, 0x86, 0x20, 4, 0, 0, 0, 0, 7, 0xe0, 3, 0xc0, 1, 0x80,
109 	1, 0x80, 3, 0xc0, 7, 0xe0, 0, 0, 0, 0, 0x20, 4, 0x61, 0x86, 0xe3, 0xc7,
110 	0xe3, 0xc7, 0x61, 0x86, 0x20, 4, 0, 0, 0, 0, 7, 0xe0, 3, 0xc0, 1, 0x80
111 };
112 
113 static const unsigned char kUpSortArrow8x8[] = {
114 	0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff,
115 	0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff,
116 	0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff,
117 	0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff,
118 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
119 	0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff,
120 	0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff,
121 	0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff
122 };
123 
124 static const unsigned char kDownSortArrow8x8[] = {
125 	0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff,
126 	0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff,
127 	0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff,
128 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
129 	0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff,
130 	0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff,
131 	0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff,
132 	0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff
133 };
134 
135 static const unsigned char kUpSortArrow8x8Invert[] = {
136 	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
137 	0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0xff,
138 	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
139 	0xff, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0xff, 0xff,
140 	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
141 	0xff, 0xff, 0x1f, 0x1f, 0x1f, 0xff, 0xff, 0xff,
142 	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
143 	0xff, 0xff, 0xff, 0x1f, 0xff, 0xff, 0xff, 0xff
144 };
145 
146 static const unsigned char kDownSortArrow8x8Invert[] = {
147 	0xff, 0xff, 0xff, 0x1f, 0xff, 0xff, 0xff, 0xff,
148 	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
149 	0xff, 0xff, 0x1f, 0x1f, 0x1f, 0xff, 0xff, 0xff,
150 	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
151 	0xff, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0xff, 0xff,
152 	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
153 	0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0xff,
154 	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
155 };
156 
157 /*
158 static const rgb_color kTitleColor = {215, 215, 215, 255};
159 static const rgb_color kTitleTextColor = { 0, 0, 0, 255 };
160 static const rgb_color kDefaultBackgroundColor = {236, 236, 236, 255};
161 static const rgb_color kRowDividerColor = {148, 148, 148, 255};
162 static const rgb_color kDefaultSelectionColor = {255, 255, 255, 255};
163 static const rgb_color kDefaultEditColor = {180, 180, 180, 180};
164 static const rgb_color kNonFocusSelectionColor = {220, 220, 220, 255};
165 */
166 
167 static const float kTitleHeight = 17.0;
168 static const float kLatchWidth = 15.0;
169 
170 
171 static const rgb_color kColor[B_COLOR_TOTAL] =
172 {
173     {236, 236, 236, 255},           // B_COLOR_BACKGROUND
174     {  0,   0,   0, 255},           // B_COLOR_TEXT
175     {148, 148, 148, 255},           // B_COLOR_ROW_DIVIDER
176     {255, 255, 255, 255},           // B_COLOR_SELECTION
177     {  0,   0,   0, 255},           // B_COLOR_SELECTION_TEXT
178     {220, 220, 220, 255},           // B_COLOR_NON_FOCUS_SELECTION
179     {180, 180, 180, 180},           // B_COLOR_EDIT_BACKGROUND
180     {  0,   0,   0, 255},           // B_COLOR_EDIT_TEXT
181     {215, 215, 215, 255},           // B_COLOR_HEADER_BACKGROUND
182     {  0,   0,   0, 255},           // B_COLOR_HEADER_TEXT
183     {  0,   0,   0, 255},           // B_COLOR_SEPARATOR_LINE
184     {  0,   0,   0, 255},           // B_COLOR_SEPARATOR_BORDER
185 };
186 
187 static const int32 kMaxDepth = 1024;
188 static const float kLeftMargin = kLatchWidth;
189 static const float kRightMargin = kLatchWidth;
190 static const float kBottomMargin = kLatchWidth;
191 static const float kOutlineLevelIndent = kLatchWidth;
192 static const float kColumnResizeAreaWidth = 10.0;
193 static const float kRowDragSensitivity = 5.0;
194 static const float kDoubleClickMoveSensitivity = 4.0;
195 static const float kSortIndicatorWidth = 9.0;
196 static const float kDropHighlightLineHeight = 2.0;
197 
198 static const uint32 kToggleColumn = 'BTCL';
199 
200 class BRowContainer : public BObjectList<BRow>
201 {
202 };
203 
204 class TitleView : public BView {
205 public:
206 	TitleView(BRect, OutlineView*, BList *visibleColumns, BList *sortColumns,
207 		BColumnListView *masterView, uint32 resizingMode);
208 	~TitleView();
209 	void ColumnAdded(BColumn*);
210 	void SetColumnVisible(BColumn*, bool);
211 
212 	virtual void Draw(BRect rect);
213 	virtual void ScrollTo(BPoint);
214 	virtual void MessageReceived(BMessage*);
215 	virtual void MouseDown(BPoint);
216 	virtual void MouseMoved(BPoint, uint32, const BMessage*);
217 	virtual void MouseUp(BPoint position);
218 	virtual void FrameResized(float width, float height);
219 
220 	void MoveColumn(BColumn *column, int32 index);
221 	void SetColumnFlags(column_flags flags);
222 
223 	void SetEditMode(bool state) { fEditMode = state; }
224 
225 private:
226 	void GetTitleRect(BColumn*, BRect *out_rect);
227 	int32 FindColumn(BPoint, float *out_leftEdge);
228 	void FixScrollBar(bool scrollToFit);
229 	void DragSelectedColumn(BPoint);
230 	void ResizeSelectedColumn(BPoint);
231 	void ComputeDragBoundries(BColumn*, BPoint);
232 	void DrawTitle(BView*, BRect, BColumn*, bool depressed);
233 
234 	OutlineView *fOutlineView;
235 	BList *fColumns;
236 	BList *fSortColumns;
237 	float fColumnsWidth;
238 	BRect fVisibleRect;
239 
240 #if DOUBLE_BUFFERED_COLUMN_RESIZE
241 	BBitmap *fDrawBuffer;
242 	BView *fDrawBufferView;
243 #endif
244 
245 	enum {
246 		INACTIVE,
247 		RESIZING_COLUMN,
248 		PRESSING_COLUMN,
249 		DRAG_COLUMN_INSIDE_TITLE,
250 		DRAG_COLUMN_OUTSIDE_TITLE
251 	} fCurrentState;
252 
253 	BPopUpMenu *fColumnPop;
254 	BColumnListView *fMasterView;
255 	bool fEditMode;
256 	int32 fColumnFlags;
257 
258 	// State information for resizing/dragging
259 	BColumn *fSelectedColumn;
260 	BRect fSelectedColumnRect;
261 	bool fResizingFirstColumn;
262 	BPoint fClickPoint;			// offset within cell
263 	float fLeftDragBoundry;
264 	float fRightDragBoundry;
265 	BPoint fCurrentDragPosition;
266 
267 
268 	BBitmap *fUpSortArrow;
269 	BBitmap *fDownSortArrow;
270 #if _INCLUDES_CLASS_CURSOR
271 	BCursor *fResizeCursor;
272 	BCursor *fMinResizeCursor;
273 	BCursor *fMaxResizeCursor;
274 	BCursor *fColumnMoveCursor;
275 #endif
276 
277 
278 	typedef BView _inherited;
279 };
280 
281 class OutlineView : public BView {
282 public:
283 	OutlineView(BRect, BList *visibleColumns, BList *sortColumns, BColumnListView *listView);
284 	~OutlineView();
285 
286 	virtual void				Draw(BRect);
287 	const 	BRect&				VisibleRect() const;
288 
289 			void				RedrawColumn(BColumn *column, float leftEdge, bool isFirstColumn);
290 			void 				StartSorting();
291 
292 			void				AddRow(BRow*, int32 index, BRow *TheRow);
293 			BRow*				CurrentSelection(BRow *lastSelected) const;
294 			void 				ToggleFocusRowSelection(bool selectRange);
295 			void 				ToggleFocusRowOpen();
296 			void 				ChangeFocusRow(bool up, bool updateSelection, bool addToCurrentSelection);
297 			void 				MoveFocusToVisibleRect();
298 			void 				ExpandOrCollapse(BRow *parent, bool expand);
299 			void 				RemoveRow(BRow*);
300 			BRowContainer*		RowList();
301 			void				UpdateRow(BRow*);
302 			bool				FindParent(BRow *row, BRow **out_parent, bool *out_isVisible);
303 			int32				IndexOf(BRow *row);
304 			void				Deselect(BRow*);
305 			void				AddToSelection(BRow*);
306 			void				DeselectAll();
307 			BRow*				FocusRow() const;
308 			void				SetFocusRow(BRow *row, bool select);
309 			BRow*				FindRow(float ypos, int32 *out_indent, float *out_top);
310 			bool				FindRect(const BRow *row, BRect *out_rect);
311 			void				ScrollTo(const BRow* Row);
312 
313 			void				Clear();
314 			void				SetSelectionMode(list_view_type);
315 			list_view_type		SelectionMode() const;
316 			void				SetMouseTrackingEnabled(bool);
317 			void				FixScrollBar(bool scrollToFit);
318 			void				SetEditMode(bool state) { fEditMode = state; }
319 
320 	virtual void				FrameResized(float width, float height);
321 	virtual void				ScrollTo(BPoint pt);
322 	virtual void				MouseDown(BPoint);
323 	virtual void				MouseMoved(BPoint, uint32, const BMessage*);
324 	virtual void				MouseUp(BPoint);
325 	virtual void				MessageReceived(BMessage*);
326 
327 private:
328 			bool				SortList(BRowContainer *list, bool isVisible);
329 	static	int32				DeepSortThreadEntry(void *outlineView);
330 			void				DeepSort();
331 			void				SelectRange(BRow *start, BRow *end);
332 			int32				CompareRows(BRow *row1, BRow *row2);
333 			void				AddSorted(BRowContainer *list, BRow *row);
334 			void				RecursiveDeleteRows(BRowContainer *list, bool owner);
335 			void				InvalidateCachedPositions();
336 			bool				FindVisibleRect(BRow *row, BRect *out_rect);
337 
338 	BList*			fColumns;
339 	BList*			fSortColumns;
340 	float			fItemsHeight;
341 	BRowContainer	fRows;
342 	BRect			fVisibleRect;
343 
344 #if DOUBLE_BUFFERED_COLUMN_RESIZE
345 	BBitmap*		fDrawBuffer;
346 	BView*			fDrawBufferView;
347 #endif
348 
349 	BRow*			fFocusRow;
350 	BRect			fFocusRowRect;
351 	BRow*			fRollOverRow;
352 
353 	BRow			fSelectionListDummyHead;
354 	BRow*			fLastSelectedItem;
355 	BRow*			fFirstSelectedItem;
356 
357 	thread_id		fSortThread;
358 	int32			fNumSorted;
359 	bool			fSortCancelled;
360 
361 	enum CurrentState
362 	{
363 		INACTIVE,
364 		LATCH_CLICKED,
365 		ROW_CLICKED,
366 		DRAGGING_ROWS
367 	};
368 
369 	CurrentState fCurrentState;
370 
371 
372 	BColumnListView*	fMasterView;
373 	list_view_type		fSelectionMode;
374 	bool				fTrackMouse;
375 	BField*				fCurrentField;
376 	BRow*				fCurrentRow;
377 	BColumn*			fCurrentColumn;
378 	bool				fMouseDown;
379 	BRect				fFieldRect;
380 	int32				fCurrentCode;
381 	bool				fEditMode;
382 
383 	// State information for mouse/keyboard interaction
384 	BPoint fClickPoint;
385 	bool fDragging;
386 	int32 fClickCount;
387 	BRow *fTargetRow;
388 	float fTargetRowTop;
389 	BRect fLatchRect;
390 	float fDropHighlightY;
391 
392 	friend class RecursiveOutlineIterator;
393 	typedef BView _inherited;
394 };
395 
396 class RecursiveOutlineIterator {
397 public:
398 	RecursiveOutlineIterator(BRowContainer*, bool openBranchesOnly = true);
399 	BRow *CurrentRow() const;
400 	int32 CurrentLevel() const;
401 	void GoToNext();
402 
403 private:
404 	struct {
405 		BRowContainer *fRowSet;
406 		int32 fIndex;
407 		int32 fDepth;
408 	} fStack[kMaxDepth];
409 
410 	int32 fStackIndex;
411 	BRowContainer *fCurrentList;
412 	int32 fCurrentListIndex;
413 	int32 fCurrentListDepth;
414 	bool fOpenBranchesOnly;
415 };
416 
417 }	// namespace BPrivate
418 
419 using namespace BPrivate;
420 
421 BField::BField()
422 {
423 }
424 
425 BField::~BField()
426 {
427 }
428 
429 // #pragma mark -
430 
431 void BColumn::MouseMoved(BColumnListView */*parent*/, BRow */*row*/, BField */*field*/,
432 						BRect /*field_rect*/, BPoint/*point*/, uint32 /*buttons*/, int32 /*code*/)
433 {
434 }
435 
436 void BColumn::MouseDown( BColumnListView */*parent*/, BRow */*row*/, BField */*field*/,
437 						BRect /*field_rect*/, BPoint /*point*/, uint32 /*buttons*/)
438 {
439 }
440 
441 void BColumn::MouseUp(BColumnListView */*parent*/, BRow */*row*/, BField */*field*/)
442 {
443 }
444 
445 // #pragma mark -
446 
447 BRow::BRow(float height)
448 	:	fChildList(NULL),
449 		fIsExpanded(false),
450 		fHeight(height),
451 		fNextSelected(NULL),
452 		fPrevSelected(NULL),
453 		fParent(NULL),
454 		fList(NULL)
455 {
456 }
457 
458 BRow::~BRow()
459 {
460 	while (true) {
461 		BField *field = (BField*) fFields.RemoveItem(0L);
462 		if (field == 0)
463 			break;
464 
465 		delete field;
466 	}
467 }
468 
469 bool BRow::HasLatch() const
470 {
471 	return fChildList != 0;
472 }
473 
474 int32 BRow::CountFields() const
475 {
476 	return fFields.CountItems();
477 }
478 
479 BField* BRow::GetField(int32 index)
480 {
481 	return (BField*) fFields.ItemAt(index);
482 }
483 
484 const BField* BRow::GetField(int32 index) const
485 {
486 	return (const BField*) fFields.ItemAt(index);
487 }
488 
489 void BRow::SetField(BField *field, int32 logicalFieldIndex)
490 {
491 	if (fFields.ItemAt(logicalFieldIndex) != 0)
492 		delete (BField*) fFields.RemoveItem(logicalFieldIndex);
493 
494 	if( NULL != fList ) {
495 		ValidateField(field, logicalFieldIndex);
496 		BRect inv;
497 		fList->GetRowRect(this, &inv);
498 		fList->Invalidate(inv);
499 	}
500 
501 	fFields.AddItem(field, logicalFieldIndex);
502 }
503 
504 float BRow::Height() const
505 {
506 	return fHeight;
507 }
508 
509 bool BRow::IsExpanded() const
510 {
511 	return fIsExpanded;
512 }
513 
514 void
515 BRow::ValidateFields() const
516 {
517 	for( int32 i = 0; i < CountFields(); i++ )
518 	{
519 		ValidateField(GetField(i), i);
520 	}
521 }
522 
523 void
524 BRow::ValidateField(const BField *field, int32 logicalFieldIndex) const
525 {
526 	// The Fields may be moved by the user, but the logicalFieldIndexes
527 	// do not change, so we need to map them over when checking the
528 	// Field types.
529 	BColumn* col = NULL;
530 	int32 items = fList->CountColumns();
531 	for( int32 i = 0 ; i < items; ++i )
532 	{
533 		col = fList->ColumnAt(i);
534 		if( col->LogicalFieldNum() == logicalFieldIndex )
535 			break;
536 	}
537 
538 	if( NULL == col )
539 	{
540 		BString dbmessage("\n\n\tThe parent BColumnListView does not have "
541 		                  "\n\ta BColumn at the logical field index ");
542 		dbmessage << logicalFieldIndex << ".\n\n";
543 		printf(dbmessage.String());
544 	}
545 	else
546 	{
547 		if( false == col->AcceptsField(field) )
548 		{
549 			BString dbmessage("\n\n\tThe BColumn of type ");
550 			dbmessage << typeid(*col).name() << "\n\tat logical field index "
551 			          << logicalFieldIndex << "\n\tdoes not support the field type "
552 			          << typeid(*field).name() << ".\n\n";
553 			debugger(dbmessage.String());
554 		}
555 	}
556 }
557 
558 
559 // #pragma mark -
560 
561 BColumn::BColumn(float width, float minWidth, float maxWidth, alignment align)
562 	:	fWidth(width),
563 		fMinWidth(minWidth),
564 		fMaxWidth(maxWidth),
565 		fVisible(true),
566 		fList(0),
567 		fShowHeading(true),
568 		fAlignment(align)
569 {
570 }
571 
572 BColumn::~BColumn()
573 {
574 }
575 
576 float BColumn::Width() const
577 {
578 	return fWidth;
579 }
580 
581 void BColumn::SetWidth(float width)
582 {
583 	fWidth = width;
584 }
585 
586 float BColumn::MinWidth() const
587 {
588 	return fMinWidth;
589 }
590 
591 float BColumn::MaxWidth() const
592 {
593 	return fMaxWidth;
594 }
595 
596 void BColumn::DrawTitle(BRect, BView*)
597 {
598 }
599 
600 void BColumn::DrawField(BField*, BRect, BView*)
601 {
602 }
603 
604 int BColumn::CompareFields(BField *, BField *)
605 {
606 	return 0;
607 }
608 
609 void BColumn::GetColumnName(BString* into) const
610 {
611 	*into = "(Unnamed)";
612 }
613 
614 bool BColumn::IsVisible() const
615 {
616 	return fVisible;
617 }
618 
619 void BColumn::SetVisible(bool visible)
620 {
621 	if (fList && (fVisible != visible))
622 		fList->SetColumnVisible(this, visible);
623 }
624 
625 bool BColumn::ShowHeading() const
626 {
627 	return fShowHeading;
628 }
629 
630 void BColumn::SetShowHeading(bool state)
631 {
632 	fShowHeading = state;
633 }
634 
635 alignment BColumn::Alignment() const
636 {
637 	return fAlignment;
638 }
639 
640 void BColumn::SetAlignment(alignment align)
641 {
642 	fAlignment = align;
643 }
644 
645 bool BColumn::WantsEvents() const
646 {
647 	return fWantsEvents;
648 }
649 
650 void BColumn::SetWantsEvents(bool state)
651 {
652 	fWantsEvents = state;
653 }
654 
655 int32 BColumn::LogicalFieldNum() const
656 {
657 	return fFieldID;
658 }
659 
660 bool
661 BColumn::AcceptsField(const BField*) const
662 {
663 	return true;
664 }
665 
666 
667 // #pragma mark -
668 
669 BColumnListView::BColumnListView(BRect rect, const char *name, uint32 resizingMode,
670 	uint32 drawFlags, border_style border, bool showHorizontalScrollbar)
671 	:	BView(rect, name, resizingMode, drawFlags | B_WILL_DRAW | B_FRAME_EVENTS | B_FULL_UPDATE_ON_RESIZE),
672 		fStatusView(0),
673 		fSelectionMessage(0),
674 		fSortingEnabled(true),
675 		fLatchWidth(kLatchWidth),
676 		fBorderStyle(border)
677 {
678 	SetViewColor(B_TRANSPARENT_32_BIT);
679 
680 	BRect bounds(rect);
681 	bounds.OffsetTo(0, 0);
682 
683 	for (int i = 0; i < (int)B_COLOR_TOTAL; i++)
684 	  fColorList[i] = kColor[i];
685 
686 	BRect titleRect(bounds);
687 	titleRect.bottom = titleRect.top + kTitleHeight;
688 #if !LOWER_SCROLLBAR
689 	titleRect.right -= B_V_SCROLL_BAR_WIDTH + 1;
690 #endif
691 
692 	BRect outlineRect(bounds);
693 	outlineRect.top = titleRect.bottom + 1.0;
694 	outlineRect.right -= B_V_SCROLL_BAR_WIDTH + 1;
695 	if(showHorizontalScrollbar)
696 		outlineRect.bottom -= B_H_SCROLL_BAR_HEIGHT + 1;
697 
698 	BRect vScrollBarRect(bounds);
699 #if LOWER_SCROLLBAR
700 	vScrollBarRect.top += kTitleHeight;
701 #endif
702 
703 	vScrollBarRect.left = vScrollBarRect.right - B_V_SCROLL_BAR_WIDTH;
704 	if(showHorizontalScrollbar)
705 		vScrollBarRect.bottom -= B_H_SCROLL_BAR_HEIGHT;
706 
707 	BRect hScrollBarRect(bounds);
708 	hScrollBarRect.top = hScrollBarRect.bottom - B_H_SCROLL_BAR_HEIGHT;
709 	hScrollBarRect.right -= B_V_SCROLL_BAR_WIDTH;
710 
711 	// Adjust stuff so the border will fit.
712 	if (fBorderStyle == B_PLAIN_BORDER) {
713 		titleRect.InsetBy(1, 0);
714 		titleRect.top++;
715 		outlineRect.InsetBy(1, 0);
716 		outlineRect.bottom--;
717 
718 		vScrollBarRect.OffsetBy(-1, 0);
719 		vScrollBarRect.InsetBy(0, 1);
720 		hScrollBarRect.OffsetBy(0, -1);
721 		hScrollBarRect.InsetBy(1, 0);
722 	} else if (fBorderStyle == B_FANCY_BORDER) {
723 		titleRect.InsetBy(2, 0);
724 		titleRect.top += 2;
725 		outlineRect.InsetBy(2, 0);
726 		outlineRect.bottom -= 2;
727 
728 		vScrollBarRect.OffsetBy(-2, 0);
729 		vScrollBarRect.InsetBy(0, 2);
730 		hScrollBarRect.OffsetBy(0, -2);
731 		hScrollBarRect.InsetBy(2, 0);
732 	}
733 
734 	fOutlineView = new OutlineView(outlineRect, &fColumns, &fSortColumns, this);
735 	AddChild(fOutlineView);
736 
737 
738 	// Adapt to correct resizing mode
739 	uint32 fParentFlags = resizingMode;
740 	// Always follow LEFT_RIGHT
741 	uint32 fTitleFlags = B_FOLLOW_LEFT_RIGHT;
742 
743 	if ((fParentFlags & B_FOLLOW_TOP) && (fParentFlags & ~B_FOLLOW_BOTTOM)) {
744 		fTitleFlags |= B_FOLLOW_TOP;
745 	}
746 	else if ((fParentFlags & B_FOLLOW_BOTTOM) && (fParentFlags & ~B_FOLLOW_TOP)) {
747 		fTitleFlags |= B_FOLLOW_BOTTOM;
748 	}
749 
750 	fTitleView = new TitleView(titleRect, fOutlineView, &fColumns, &fSortColumns, this, fTitleFlags);
751 
752 
753 	AddChild(fTitleView);
754 	fVerticalScrollBar = new BScrollBar(vScrollBarRect, "vertical_scroll_bar",
755 		fOutlineView, 0.0, bounds.Height(), B_VERTICAL);
756 	AddChild(fVerticalScrollBar);
757 	fHorizontalScrollBar = new BScrollBar(hScrollBarRect, "horizontal_scroll_bar",
758 		fTitleView, 0.0, bounds.Width(), B_HORIZONTAL);
759 	AddChild(fHorizontalScrollBar);
760 	if(!showHorizontalScrollbar)
761 		fHorizontalScrollBar->Hide();
762 	fOutlineView->FixScrollBar(true);
763 }
764 
765 BColumnListView::~BColumnListView()
766 {
767 	while (true) {
768 		BColumn *column = (BColumn*) fColumns.RemoveItem(0L);
769 		if (column == 0)
770 			break;
771 
772 		delete column;
773 	}
774 }
775 
776 bool BColumnListView::InitiateDrag(BPoint, bool)
777 {
778 	return false;
779 }
780 
781 void BColumnListView::MessageDropped(BMessage*, BPoint)
782 {
783 }
784 
785 void BColumnListView::ExpandOrCollapse(BRow* Row, bool Open)
786 {
787 	fOutlineView->ExpandOrCollapse(Row, Open);
788 }
789 
790 status_t BColumnListView::Invoke(BMessage *message)
791 {
792 	if (message == 0)
793 		message = Message();
794 
795 	return BInvoker::Invoke(message);
796 }
797 
798 void BColumnListView::ItemInvoked()
799 {
800 	Invoke();
801 }
802 
803 void BColumnListView::SetInvocationMessage(BMessage *message)
804 {
805 	SetMessage(message);
806 }
807 
808 BMessage* BColumnListView::InvocationMessage() const
809 {
810 	return Message();
811 }
812 
813 uint32 BColumnListView::InvocationCommand() const
814 {
815 	return Command();
816 }
817 
818 BRow* BColumnListView::FocusRow() const
819 {
820 	return fOutlineView->FocusRow();
821 }
822 
823 void BColumnListView::SetFocusRow(int32 Index, bool Select)
824 {
825 	SetFocusRow(RowAt(Index), Select);
826 }
827 
828 void BColumnListView::SetFocusRow(BRow* Row, bool Select)
829 {
830 	fOutlineView->SetFocusRow(Row, Select);
831 }
832 
833 void BColumnListView::SetMouseTrackingEnabled(bool Enabled)
834 {
835 	fOutlineView->SetMouseTrackingEnabled(Enabled);
836 }
837 
838 list_view_type BColumnListView::SelectionMode() const
839 {
840 	return fOutlineView->SelectionMode();
841 }
842 
843 void BColumnListView::Deselect(BRow *row)
844 {
845 	fOutlineView->Deselect(row);
846 }
847 
848 void BColumnListView::AddToSelection(BRow *row)
849 {
850 	fOutlineView->AddToSelection(row);
851 }
852 
853 void BColumnListView::DeselectAll()
854 {
855 	fOutlineView->DeselectAll();
856 }
857 
858 BRow* BColumnListView::CurrentSelection(BRow *lastSelected) const
859 {
860 	return fOutlineView->CurrentSelection(lastSelected);
861 }
862 
863 void BColumnListView::SelectionChanged()
864 {
865 	if (fSelectionMessage)
866 		Invoke(fSelectionMessage);
867 }
868 
869 void BColumnListView::SetSelectionMessage(BMessage *message)
870 {
871 	if (fSelectionMessage == message)
872 		return;
873 
874 	delete fSelectionMessage;
875 	fSelectionMessage = message;
876 }
877 
878 BMessage* BColumnListView::SelectionMessage()
879 {
880 	return fSelectionMessage;
881 }
882 
883 uint32 BColumnListView::SelectionCommand() const
884 {
885 	if (fSelectionMessage)
886 		return fSelectionMessage->what;
887 
888 	return 0;
889 }
890 
891 void BColumnListView::SetSelectionMode(list_view_type mode)
892 {
893 	fOutlineView->SetSelectionMode(mode);
894 }
895 
896 void BColumnListView::SetSortingEnabled(bool enabled)
897 {
898 	fSortingEnabled = enabled;
899 	fSortColumns.MakeEmpty();
900 	fTitleView->Invalidate();	// Erase sort indicators
901 }
902 
903 bool BColumnListView::SortingEnabled() const
904 {
905 	return fSortingEnabled;
906 }
907 
908 void BColumnListView::SetSortColumn(BColumn *column, bool add, bool ascending)
909 {
910 	if (!SortingEnabled())
911 		return;
912 
913 	if (!add)
914 		fSortColumns.MakeEmpty();
915 
916 	if (!fSortColumns.HasItem(column))
917 		fSortColumns.AddItem(column);
918 
919 	column->fSortAscending = ascending;
920 	fTitleView->Invalidate();
921 	fOutlineView->StartSorting();
922 }
923 
924 void BColumnListView::ClearSortColumns()
925 {
926 	fSortColumns.MakeEmpty();
927 	fTitleView->Invalidate();	// Erase sort indicators
928 }
929 
930 void BColumnListView::AddStatusView(BView *view)
931 {
932 	BRect bounds = Bounds();
933 	float width = view->Bounds().Width();
934 	if (width > bounds.Width() / 2)
935 		width = bounds.Width() / 2;
936 
937 	fStatusView = view;
938 
939 	Window()->BeginViewTransaction();
940 	fHorizontalScrollBar->ResizeBy(-(width + 1), 0);
941 	fHorizontalScrollBar->MoveBy((width + 1), 0);
942 	AddChild(view);
943 
944 	BRect viewRect(bounds);
945 	viewRect.right = width;
946 	viewRect.top = viewRect.bottom - B_H_SCROLL_BAR_HEIGHT;
947 	if (fBorderStyle == B_PLAIN_BORDER)
948 		viewRect.OffsetBy(1, -1);
949 	else if (fBorderStyle == B_FANCY_BORDER)
950 		viewRect.OffsetBy(2, -2);
951 
952 	view->SetResizingMode(B_FOLLOW_LEFT | B_FOLLOW_BOTTOM);
953 	view->ResizeTo(viewRect.Width(), viewRect.Height());
954 	view->MoveTo(viewRect.left, viewRect.top);
955 	Window()->EndViewTransaction();
956 }
957 
958 BView* BColumnListView::RemoveStatusView()
959 {
960 	if (fStatusView) {
961 		float width = fStatusView->Bounds().Width();
962 		Window()->BeginViewTransaction();
963 		fStatusView->RemoveSelf();
964 		fHorizontalScrollBar->MoveBy(-width, 0);
965 		fHorizontalScrollBar->ResizeBy(width, 0);
966 		Window()->EndViewTransaction();
967 	}
968 
969 	BView *view = fStatusView;
970 	fStatusView = 0;
971 	return view;
972 }
973 void BColumnListView::AddColumn(BColumn *column, int32 logicalFieldIndex)
974 {
975 	ASSERT(column != 0);
976 
977 	column->fList = this;
978 	column->fFieldID = logicalFieldIndex;
979 
980 	// sanity check.  If there is already a field with this ID, remove it.
981 	for (int32 index = 0; index < fColumns.CountItems(); index++) {
982 		BColumn *existingColumn = (BColumn*) fColumns.ItemAt(index);
983 		if (existingColumn && existingColumn->fFieldID == logicalFieldIndex) {
984 			RemoveColumn(existingColumn);
985 			break;
986 		}
987 	}
988 
989 	if (column->Width() < column->MinWidth())
990 		column->SetWidth(column->MinWidth());
991 	else if (column->Width() > column->MaxWidth())
992 		column->SetWidth(column->MaxWidth());
993 
994 	fColumns.AddItem((void*) column);
995 	fTitleView->ColumnAdded(column);
996 }
997 
998 void BColumnListView::MoveColumn(BColumn *column, int32 index)
999 {
1000 	ASSERT(column != 0);
1001 	fTitleView->MoveColumn(column, index);
1002 }
1003 
1004 void BColumnListView::RemoveColumn(BColumn *column)
1005 {
1006 	if (fColumns.HasItem(column)) {
1007 		SetColumnVisible(column, false);
1008 		Window()->UpdateIfNeeded();
1009 		fColumns.RemoveItem(column);
1010 	}
1011 }
1012 
1013 int32 BColumnListView::CountColumns() const
1014 {
1015 	return fColumns.CountItems();
1016 }
1017 
1018 BColumn* BColumnListView::ColumnAt(int32 field) const
1019 {
1020 	return (BColumn*) fColumns.ItemAt(field);
1021 }
1022 
1023 void BColumnListView::SetColumnVisible(BColumn *column, bool visible)
1024 {
1025 	fTitleView->SetColumnVisible(column, visible);
1026 }
1027 
1028 void BColumnListView::SetColumnVisible(int32 index, bool isVisible)
1029 {
1030 	BColumn *column = ColumnAt(index);
1031 	if (column)
1032 		column->SetVisible(isVisible);
1033 }
1034 
1035 bool BColumnListView::IsColumnVisible(int32 index) const
1036 {
1037 	BColumn *column = ColumnAt(index);
1038 	if (column)
1039 		return column->IsVisible();
1040 
1041 	return false;
1042 }
1043 
1044 void BColumnListView::SetColumnFlags(column_flags flags)
1045 {
1046 	fTitleView->SetColumnFlags(flags);
1047 }
1048 
1049 const BRow* BColumnListView::RowAt(int32 Index, BRow* ParentRow) const
1050 {
1051 	if (ParentRow == 0)
1052 		return fOutlineView->RowList()->ItemAt(Index);
1053 
1054 	return ParentRow->fChildList ? ParentRow->fChildList->ItemAt(Index) : NULL;
1055 }
1056 
1057 BRow* BColumnListView::RowAt(int32 Index, BRow* ParentRow)
1058 {
1059 	if (ParentRow == 0)
1060 		return fOutlineView->RowList()->ItemAt(Index);
1061 
1062 	return ParentRow->fChildList ? ParentRow->fChildList->ItemAt(Index) : 0;
1063 }
1064 
1065 const BRow* BColumnListView::RowAt(BPoint point) const
1066 {
1067 	float top;
1068 	int32 indent;
1069 	return fOutlineView->FindRow(point.y, &indent, &top);
1070 }
1071 
1072 BRow* BColumnListView::RowAt(BPoint point)
1073 {
1074 	float top;
1075 	int32 indent;
1076 	return fOutlineView->FindRow(point.y, &indent, &top);
1077 }
1078 
1079 bool BColumnListView::GetRowRect(const BRow *row, BRect *outRect) const
1080 {
1081 	return fOutlineView->FindRect(row, outRect);
1082 }
1083 
1084 bool BColumnListView::FindParent(BRow *row, BRow **out_parent, bool *out_isVisible) const
1085 {
1086 	return fOutlineView->FindParent(row, out_parent, out_isVisible);
1087 }
1088 
1089 int32 BColumnListView::IndexOf(BRow *row)
1090 {
1091 	return fOutlineView->IndexOf(row);
1092 }
1093 
1094 int32 BColumnListView::CountRows(BRow* ParentRow) const
1095 {
1096 	if (ParentRow == 0)
1097 		return fOutlineView->RowList()->CountItems();
1098 	if (ParentRow->fChildList)
1099 		return ParentRow->fChildList->CountItems();
1100 	else
1101 		return 0;
1102 }
1103 
1104 void BColumnListView::AddRow(BRow *row, BRow* ParentRow)
1105 {
1106 	AddRow(row, -1, ParentRow);
1107 }
1108 
1109 void BColumnListView::AddRow(BRow *row, int32 index, BRow* ParentRow)
1110 {
1111 	row->fChildList = 0;
1112 	row->fList = this;
1113 	row->ValidateFields();
1114 	fOutlineView->AddRow(row, index, ParentRow);
1115 }
1116 
1117 void BColumnListView::RemoveRow(BRow *row)
1118 {
1119 	fOutlineView->RemoveRow(row);
1120 	row->fList = NULL;
1121 }
1122 
1123 void BColumnListView::UpdateRow(BRow *row)
1124 {
1125 	fOutlineView->UpdateRow(row);
1126 }
1127 
1128 void BColumnListView::ScrollTo(const BRow* Row)
1129 {
1130 	fOutlineView->ScrollTo(Row);
1131 }
1132 
1133 void BColumnListView::Clear()
1134 {
1135 	fOutlineView->Clear();
1136 }
1137 
1138 void BColumnListView::SetFont(const BFont *font, uint32 mask)
1139 {
1140 	// This method is deprecated.
1141 	fOutlineView->SetFont(font, mask);
1142 	fTitleView->SetFont(font, mask);
1143 }
1144 
1145 void BColumnListView::SetFont(ColumnListViewFont font_num, const BFont* font, uint32 mask)
1146 {
1147 	switch (font_num) {
1148 		case B_FONT_ROW:
1149 			fOutlineView->SetFont(font, mask);
1150 			break;
1151 
1152 		case B_FONT_HEADER:
1153 			fTitleView->SetFont(font, mask);
1154 			break;
1155 
1156 		default:
1157 			ASSERT(false);
1158 			break;
1159 	};
1160 }
1161 
1162 void BColumnListView::GetFont(ColumnListViewFont font_num, BFont* font) const
1163 {
1164 	switch (font_num) {
1165 		case B_FONT_ROW:
1166 			fOutlineView->GetFont(font);
1167 			break;
1168 
1169 		case B_FONT_HEADER:
1170 			fTitleView->GetFont(font);
1171 			break;
1172 
1173 		default:
1174 			ASSERT(false);
1175 			break;
1176 	};
1177 }
1178 
1179 void BColumnListView::SetColor(ColumnListViewColor color_num, const rgb_color color)
1180 {
1181 	if ((int)color_num < 0)
1182 	{
1183 		ASSERT(false);
1184 		color_num = (ColumnListViewColor) 0;
1185 	}
1186 
1187 	if ((int)color_num >= (int)B_COLOR_TOTAL)
1188 	{
1189 		ASSERT(false);
1190 		color_num = (ColumnListViewColor) (B_COLOR_TOTAL - 1);
1191 	}
1192 
1193 	fColorList[color_num] = color;
1194 }
1195 
1196 rgb_color BColumnListView::Color(ColumnListViewColor color_num) const
1197 {
1198 	if ((int)color_num < 0)
1199 	{
1200 		ASSERT(false);
1201 		color_num = (ColumnListViewColor) 0;
1202 	}
1203 
1204 	if ((int)color_num >= (int)B_COLOR_TOTAL)
1205 	{
1206 		ASSERT(false);
1207 		color_num = (ColumnListViewColor) (B_COLOR_TOTAL - 1);
1208 	}
1209 
1210 	return fColorList[color_num];
1211 }
1212 
1213 void BColumnListView::SetHighColor(rgb_color color)
1214 {
1215 	BView::SetHighColor(color);
1216 //	fOutlineView->Invalidate();	// Redraw things with the new color
1217 								// Note that this will currently cause
1218 								// an infinite loop, refreshing over and over.
1219 								// A better solution is needed.
1220 }
1221 
1222 void BColumnListView::SetSelectionColor(rgb_color color)
1223 {
1224 	fColorList[B_COLOR_SELECTION] = color;
1225 }
1226 
1227 void BColumnListView::SetBackgroundColor(rgb_color color)
1228 {
1229 	fColorList[B_COLOR_BACKGROUND] = color;
1230 	fOutlineView->Invalidate();	// Repaint with new color
1231 }
1232 
1233 void BColumnListView::SetEditColor(rgb_color color)
1234 {
1235 	fColorList[B_COLOR_EDIT_BACKGROUND] = color;
1236 }
1237 
1238 const rgb_color BColumnListView::SelectionColor() const
1239 {
1240 	return fColorList[B_COLOR_SELECTION];
1241 }
1242 
1243 const rgb_color BColumnListView::BackgroundColor() const
1244 {
1245 	return fColorList[B_COLOR_BACKGROUND];
1246 }
1247 
1248 const rgb_color BColumnListView::EditColor() const
1249 {
1250 	return fColorList[B_COLOR_EDIT_BACKGROUND];
1251 }
1252 
1253 BPoint BColumnListView::SuggestTextPosition(const BRow* row, const BColumn* inColumn) const
1254 {
1255 	BRect rect;
1256 	GetRowRect(row, &rect);
1257 	if (inColumn) {
1258 		float leftEdge = MAX(kLeftMargin, LatchWidth());
1259 		for (int index = 0; index < fColumns.CountItems(); index++) {
1260 			BColumn *column = (BColumn*) fColumns.ItemAt(index);
1261 			if (!column->IsVisible())
1262 				continue;
1263 
1264 			if (column == inColumn) {
1265 				rect.left = leftEdge;
1266 				rect.right = rect.left + column->Width();
1267 				break;
1268 			}
1269 
1270 			leftEdge += column->Width() + 1;
1271 		}
1272 	}
1273 
1274 	font_height fh;
1275 	fOutlineView->GetFontHeight(&fh);
1276 	float baseline = floor(rect.top + fh.ascent
1277 							+ (rect.Height()+1-(fh.ascent+fh.descent))/2);
1278 	return BPoint(rect.left + 8, baseline);
1279 }
1280 
1281 void BColumnListView::SetLatchWidth(float width)
1282 {
1283 	fLatchWidth = width;
1284 	Invalidate();
1285 }
1286 
1287 float BColumnListView::LatchWidth() const
1288 {
1289 	return fLatchWidth;
1290 }
1291 
1292 void BColumnListView::DrawLatch(BView *view, BRect rect, LatchType position, BRow *)
1293 {
1294 	const int32 rectInset = 4;
1295 
1296 	view->SetHighColor(0, 0, 0);
1297 
1298 	// Make Square
1299 	int32 sideLen = rect.IntegerWidth();
1300 	if( sideLen > rect.IntegerHeight() )
1301 	{
1302 		sideLen = rect.IntegerHeight();
1303 	}
1304 
1305 	// Make Center
1306 	int32 halfWidth  = rect.IntegerWidth() / 2;
1307 	int32 halfHeight = rect.IntegerHeight() / 2;
1308 	int32 halfSide   = sideLen / 2;
1309 
1310 	float left = rect.left + halfWidth  - halfSide;
1311 	float top  = rect.top  + halfHeight - halfSide;
1312 
1313 	BRect itemRect(left, top, left + sideLen, top + sideLen);
1314 
1315 	// Why it is a pixel high? I don't know.
1316 	itemRect.OffsetBy(0, -1);
1317 
1318 	itemRect.InsetBy(rectInset, rectInset);
1319 
1320 	// Make it an odd number of pixels wide, the latch looks better this way
1321 	if (1 == (itemRect.IntegerWidth() % 2))
1322 	{
1323 		itemRect.right  += 1;
1324 		itemRect.bottom += 1;
1325 	}
1326 
1327 	switch (position) {
1328 		case B_OPEN_LATCH:
1329 			view->StrokeRect(itemRect);
1330 			view->StrokeLine(BPoint(itemRect.left + 2, (itemRect.top + itemRect.bottom) / 2),
1331 				BPoint(itemRect.right - 2, (itemRect.top + itemRect.bottom) / 2));
1332 			break;
1333 
1334 		case B_PRESSED_LATCH:
1335 			view->StrokeRect(itemRect);
1336 			view->StrokeLine(BPoint(itemRect.left + 2, (itemRect.top + itemRect.bottom) / 2),
1337 				BPoint(itemRect.right - 2, (itemRect.top + itemRect.bottom) / 2));
1338 			view->StrokeLine(BPoint((itemRect.left + itemRect.right) / 2, itemRect.top +  2),
1339 				BPoint((itemRect.left + itemRect.right) / 2, itemRect.bottom - 2));
1340 			view->InvertRect(itemRect);
1341 			break;
1342 
1343 		case B_CLOSED_LATCH:
1344 			view->StrokeRect(itemRect);
1345 			view->StrokeLine(BPoint(itemRect.left + 2, (itemRect.top + itemRect.bottom) / 2),
1346 				BPoint(itemRect.right - 2, (itemRect.top + itemRect.bottom) / 2));
1347 			view->StrokeLine(BPoint((itemRect.left + itemRect.right) / 2, itemRect.top +  2),
1348 				BPoint((itemRect.left + itemRect.right) / 2, itemRect.bottom - 2));
1349 			break;
1350 
1351 		case B_NO_LATCH:
1352 			// No drawing
1353 			break;
1354 	}
1355 }
1356 
1357 void BColumnListView::MakeFocus(bool isFocus)
1358 {
1359 	Invalidate();	// Redraw focus marks around view
1360 	BView::MakeFocus(isFocus);
1361 }
1362 
1363 void BColumnListView::MessageReceived(BMessage *message)
1364 {
1365 	// Propagate mouse wheel messages down to child, so that it can
1366 	// scroll.  Note we have done so, so we don't go into infinite
1367 	// recursion if this comes back up here.
1368 	if (message->what == B_MOUSE_WHEEL_CHANGED) {
1369 		bool handled;
1370 		if (message->FindBool("be:clvhandled", &handled) != B_OK) {
1371 			message->AddBool("be:clvhandled", true);
1372 			fOutlineView->MessageReceived(message);
1373 			return;
1374 		}
1375 	}
1376 	BView::MessageReceived(message);
1377 }
1378 
1379 void BColumnListView::KeyDown(const char *bytes, int32 numBytes)
1380 {
1381 	char c = bytes[0];
1382 	switch (c) {
1383 		case B_RIGHT_ARROW:
1384 		case B_LEFT_ARROW: {
1385 			float  minVal, maxVal;
1386 			fHorizontalScrollBar->GetRange(&minVal, &maxVal);
1387 			float smallStep, largeStep;
1388 			fHorizontalScrollBar->GetSteps(&smallStep, &largeStep);
1389 			float oldVal = fHorizontalScrollBar->Value();
1390 			float newVal = oldVal;
1391 
1392 			if (c == B_LEFT_ARROW)
1393 				newVal -= smallStep;
1394 			else if (c == B_RIGHT_ARROW)
1395 				newVal += smallStep;
1396 
1397 			if (newVal < minVal)
1398 				newVal = minVal;
1399 			else if (newVal > maxVal)
1400 				newVal = maxVal;
1401 
1402 			fHorizontalScrollBar->SetValue(newVal);
1403 			break;
1404 		}
1405 
1406 		case B_DOWN_ARROW:
1407 			fOutlineView->ChangeFocusRow(false, (modifiers() & B_CONTROL_KEY) == 0,
1408 				(modifiers() & B_SHIFT_KEY) != 0);
1409 			break;
1410 
1411 		case B_UP_ARROW:
1412 			fOutlineView->ChangeFocusRow(true, (modifiers() & B_CONTROL_KEY) == 0,
1413 				(modifiers() & B_SHIFT_KEY) != 0);
1414 			break;
1415 
1416 		case B_PAGE_UP:
1417 		case B_PAGE_DOWN: {
1418 			float minValue, maxValue;
1419 			fVerticalScrollBar->GetRange(&minValue, &maxValue);
1420 			float smallStep, largeStep;
1421 			fVerticalScrollBar->GetSteps(&smallStep, &largeStep);
1422 			float currentValue = fVerticalScrollBar->Value();
1423 			float newValue = currentValue;
1424 
1425 			if (c == B_PAGE_UP)
1426 				newValue -= largeStep;
1427 			else
1428 				newValue += largeStep;
1429 
1430 			if (newValue > maxValue)
1431 				newValue = maxValue;
1432 			else if (newValue < minValue)
1433 				newValue = minValue;
1434 
1435 			fVerticalScrollBar->SetValue(newValue);
1436 
1437 			// Option + pgup or pgdn scrolls and changes the selection.
1438 			if (modifiers() & B_OPTION_KEY)
1439 				fOutlineView->MoveFocusToVisibleRect();
1440 
1441 			break;
1442 		}
1443 
1444 		case B_ENTER:
1445 			Invoke();
1446 			break;
1447 
1448 		case B_SPACE:
1449 			fOutlineView->ToggleFocusRowSelection((modifiers() & B_SHIFT_KEY) != 0);
1450 			break;
1451 
1452 		case '+':
1453 			fOutlineView->ToggleFocusRowOpen();
1454 			break;
1455 
1456 		default:
1457 			BView::KeyDown(bytes, numBytes);
1458 	}
1459 }
1460 
1461 void BColumnListView::AttachedToWindow()
1462 {
1463 	if (!Messenger().IsValid())
1464 		SetTarget(Window());
1465 
1466 	if (SortingEnabled()) fOutlineView->StartSorting();
1467 }
1468 
1469 void BColumnListView::WindowActivated(bool active)
1470 {
1471 	fOutlineView->Invalidate();
1472 		// Focus and selection appearance changes with focus
1473 
1474 	Invalidate(); 	// Redraw focus marks around view
1475 	BView::WindowActivated(active);
1476 }
1477 
1478 void BColumnListView::Draw(BRect)
1479 {
1480 	BRect rect = Bounds();
1481 	PushState();
1482 
1483 	BRect cornerRect(rect.right - B_V_SCROLL_BAR_WIDTH, rect.bottom - B_H_SCROLL_BAR_HEIGHT,
1484 		rect.right, rect.bottom);
1485 	if (fBorderStyle == B_PLAIN_BORDER) {
1486 		BView::SetHighColor(0, 0, 0);
1487 		StrokeRect(rect);
1488 		cornerRect.OffsetBy(-1, -1);
1489 	} else if (fBorderStyle == B_FANCY_BORDER) {
1490 		bool isFocus = IsFocus() && Window()->IsActive();
1491 
1492 		if (isFocus)
1493 			BView::SetHighColor(0, 0, 190);	// Need to find focus color programatically
1494 		else
1495 			BView::SetHighColor(255, 255, 255);
1496 
1497 		StrokeRect(rect);
1498 		if (!isFocus)
1499 			BView::SetHighColor(184, 184, 184);
1500 		else
1501 			BView::SetHighColor(152, 152, 152);
1502 
1503 		rect.InsetBy(1,1);
1504 		StrokeRect(rect);
1505 		cornerRect.OffsetBy(-2, -2);
1506 	}
1507 
1508 	BView::SetHighColor(215, 215, 215); // fills lower right rect between scroll bars
1509 	FillRect(cornerRect);
1510 	PopState();
1511 }
1512 
1513 void BColumnListView::SaveState(BMessage *msg)
1514 {
1515 	msg->MakeEmpty();
1516 
1517 	// Damn compiler issuing l43m incorrect warnings.
1518 	int i;
1519 	for (i = 0; ;i++)
1520 	{
1521 		BColumn *col = (BColumn*) fColumns.ItemAt(i);
1522 		if(!col)
1523 			break;
1524 		msg->AddInt32("ID",col->fFieldID);
1525 		msg->AddFloat("width",col->fWidth);
1526 		msg->AddBool("visible",col->fVisible);
1527 	}
1528 
1529 	msg->AddBool("sortingenabled",fSortingEnabled);
1530 
1531 	if(fSortingEnabled)
1532 	{
1533 		for (i = 0; ;i++)
1534 		{
1535 			BColumn *col = (BColumn*) fSortColumns.ItemAt(i);
1536 			if(!col)
1537 				break;
1538 			msg->AddInt32("sortID",col->fFieldID);
1539 			msg->AddBool("sortascending",col->fSortAscending);
1540 		}
1541 	}
1542 }
1543 
1544 void BColumnListView::LoadState(BMessage *msg)
1545 {
1546 	for(int i=0;;i++)
1547 	{
1548 		int32 ID;
1549 		if(B_OK!=msg->FindInt32("ID",i,&ID))
1550 			break;
1551 		for(int j=0;;j++)
1552 		{
1553 			BColumn *col = (BColumn*) fColumns.ItemAt(j);
1554 			if(!col)
1555 				break;
1556 			if(col->fFieldID==ID)
1557 			{
1558 				// move this column to position 'i' and set its attributes
1559 				MoveColumn(col,i);
1560 				float f;
1561 				if(B_OK==msg->FindFloat("width",i,&f))
1562 					col->SetWidth(f);
1563 				bool b;
1564 				if(B_OK==msg->FindBool("visible",i,&b))
1565 					col->SetVisible(b);
1566 			}
1567 		}
1568 	}
1569 	bool b;
1570 	if(B_OK==msg->FindBool("sortingenabled",&b))
1571 	{
1572 		SetSortingEnabled(b);
1573 		// Damn compiler issuing l43m incorrect warnings.
1574 		for(int k=0;;k++)
1575 		{
1576 			int32 ID;
1577 			if(B_OK!=msg->FindInt32("sortID", k, &ID))
1578 				break;
1579 			for(int j=0;;j++)
1580 			{
1581 				BColumn *col = (BColumn*) fColumns.ItemAt(j);
1582 				if(!col)
1583 					break;
1584 				if(col->fFieldID==ID)
1585 				{
1586 					// add this column to the sort list
1587 					bool val;
1588 					if(B_OK==msg->FindBool("sortascending", k, &val))
1589 						SetSortColumn(col, true, val);
1590 				}
1591 			}
1592 		}
1593 	}
1594 }
1595 
1596 void BColumnListView::SetEditMode(bool state)
1597 {
1598 	fOutlineView->SetEditMode(state);
1599 	fTitleView->SetEditMode(state);
1600 }
1601 
1602 void BColumnListView::Refresh()
1603 {
1604 	if(LockLooper())
1605 	{
1606 		Invalidate();
1607 		fOutlineView->FixScrollBar (true);
1608 		fOutlineView->Invalidate();
1609 		Window()->UpdateIfNeeded();
1610 		UnlockLooper();
1611 	}
1612 }
1613 
1614 // #pragma mark -
1615 
1616 
1617 TitleView::TitleView(BRect rect, OutlineView *horizontalSlave, BList *visibleColumns,
1618 		BList *sortColumns, BColumnListView *listView, uint32 resizingMode)
1619 	:	BView(rect, "title_view", resizingMode, B_WILL_DRAW | B_FRAME_EVENTS),
1620 		fOutlineView(horizontalSlave),
1621 		fColumns(visibleColumns),
1622 		fSortColumns(sortColumns),
1623 		fColumnsWidth(0),
1624 		fVisibleRect(rect.OffsetToCopy(0, 0)),
1625 		fCurrentState(INACTIVE),
1626 		fColumnPop(NULL),
1627 		fMasterView(listView),
1628 		fEditMode(false),
1629 		fColumnFlags(B_ALLOW_COLUMN_MOVE|B_ALLOW_COLUMN_RESIZE|B_ALLOW_COLUMN_POPUP|B_ALLOW_COLUMN_REMOVE)
1630 {
1631 	SetViewColor(B_TRANSPARENT_32_BIT);
1632 
1633 #if DOUBLE_BUFFERED_COLUMN_RESIZE
1634 	// xxx this needs to be smart about the size of the backbuffer.
1635 	BRect doubleBufferRect(0, 0, 600, 35);
1636 	fDrawBuffer = new BBitmap(doubleBufferRect, B_RGB32, true);
1637 	fDrawBufferView = new BView(doubleBufferRect, "double_buffer_view",
1638 		B_FOLLOW_ALL_SIDES, 0);
1639 	fDrawBuffer->Lock();
1640 	fDrawBuffer->AddChild(fDrawBufferView);
1641 	fDrawBuffer->Unlock();
1642 #endif
1643 
1644 	fUpSortArrow = new BBitmap(BRect(0, 0, 7, 7), B_CMAP8);
1645 	fDownSortArrow = new BBitmap(BRect(0, 0, 7, 7), B_CMAP8);
1646 
1647 	fUpSortArrow->SetBits((const void*) kUpSortArrow8x8, 64, 0, B_CMAP8);
1648 	fDownSortArrow->SetBits((const void*) kDownSortArrow8x8, 64, 0, B_CMAP8);
1649 
1650 #if _INCLUDES_CLASS_CURSOR
1651 	fResizeCursor = new BCursor(kResizeCursorData);
1652 	fMinResizeCursor = new BCursor(kMinResizeCursorData);
1653 	fMaxResizeCursor = new BCursor(kMaxResizeCursorData);
1654 	fColumnMoveCursor = new BCursor(kColumnMoveCursorData);
1655 #endif
1656 	FixScrollBar(true);
1657 }
1658 
1659 TitleView::~TitleView()
1660 {
1661 	delete fColumnPop;
1662 	fColumnPop = NULL;
1663 
1664 	fDrawBuffer->Lock();
1665 	fDrawBufferView->RemoveSelf();
1666 	fDrawBuffer->Unlock();
1667 	delete fDrawBufferView;
1668 	delete fDrawBuffer;
1669 	delete fUpSortArrow;
1670 	delete fDownSortArrow;
1671 
1672 #if _INCLUDES_CLASS_CURSOR
1673 	delete fResizeCursor;
1674 	delete fMaxResizeCursor;
1675 	delete fMinResizeCursor;
1676 	delete fColumnMoveCursor;
1677 #endif
1678 }
1679 
1680 void TitleView::ColumnAdded(BColumn *column)
1681 {
1682 	fColumnsWidth += column->Width() + 1;
1683 	FixScrollBar(false);
1684 	Invalidate();
1685 }
1686 // Could use a CopyBits here.
1687 void TitleView::SetColumnVisible(BColumn *column, bool visible)
1688 {
1689 	if (column->fVisible == visible)
1690 		return;
1691 
1692 	// If setting it visible, do this first so we can find its position
1693 	// to invalidate.  If hiding it, do it last.
1694 	if (visible)
1695 		column->fVisible = visible;
1696 
1697 	BRect titleInvalid;
1698 	GetTitleRect(column, &titleInvalid);
1699 
1700 	// Now really set the visibility
1701 	column->fVisible = visible;
1702 
1703 	if (visible)
1704 		fColumnsWidth += column->Width();
1705 	else
1706 		fColumnsWidth -= column->Width();
1707 
1708 	BRect outlineInvalid(fOutlineView->VisibleRect());
1709 	outlineInvalid.left = titleInvalid.left;
1710 	titleInvalid.right = outlineInvalid.right;
1711 
1712 	Invalidate(titleInvalid);
1713 	fOutlineView->Invalidate(outlineInvalid);
1714 }
1715 
1716 void TitleView::GetTitleRect(BColumn *findColumn, BRect *out_rect)
1717 {
1718 	float leftEdge = MAX(kLeftMargin, fMasterView->LatchWidth());
1719 	int32 numColumns = fColumns->CountItems();
1720 	for (int index = 0; index < numColumns; index++) {
1721 		BColumn *column = (BColumn*) fColumns->ItemAt(index);
1722 		if (!column->IsVisible())
1723 			continue;
1724 
1725 		if (column == findColumn) {
1726 			out_rect->Set(leftEdge, 0, leftEdge + column->Width(), fVisibleRect.bottom);
1727 			return;
1728 		}
1729 
1730 		leftEdge += column->Width() + 1;
1731 	}
1732 
1733 	TRESPASS();
1734 }
1735 
1736 int32 TitleView::FindColumn(BPoint position, float *out_leftEdge)
1737 {
1738 	float leftEdge = MAX(kLeftMargin, fMasterView->LatchWidth());
1739 	int32 numColumns = fColumns->CountItems();
1740 	for (int index = 0; index < numColumns; index++) {
1741 		BColumn *column = (BColumn*) fColumns->ItemAt(index);
1742 		if (!column->IsVisible())
1743 			continue;
1744 
1745 		if (leftEdge > position.x)
1746 			break;
1747 
1748 		if (position.x >= leftEdge && position.x <= leftEdge + column->Width()) {
1749 			*out_leftEdge = leftEdge;
1750 			return index;
1751 		}
1752 
1753 		leftEdge += column->Width() + 1;
1754 	}
1755 
1756 	return 0;
1757 }
1758 
1759 void TitleView::FixScrollBar(bool scrollToFit)
1760 {
1761 	BScrollBar *hScrollBar = ScrollBar(B_HORIZONTAL);
1762 	if (hScrollBar) {
1763 		float virtualWidth = fColumnsWidth + MAX(kLeftMargin, fMasterView->LatchWidth()) +
1764 			kRightMargin * 2;
1765 
1766 		if (virtualWidth > fVisibleRect.Width()) {
1767 			hScrollBar->SetProportion(fVisibleRect.Width() / virtualWidth);
1768 
1769 			// Perform the little trick if the user is scrolled over too far.
1770 			// See OutlineView::FixScrollBar for a more in depth explanation
1771 			float maxScrollBarValue = virtualWidth - fVisibleRect.Width();
1772 			if (scrollToFit || hScrollBar->Value() <= maxScrollBarValue) {
1773 				hScrollBar->SetRange(0.0, maxScrollBarValue);
1774 				hScrollBar->SetSteps(50, fVisibleRect.Width());
1775 			}
1776 		} else if (hScrollBar->Value() == 0.0)
1777 			hScrollBar->SetRange(0.0, 0.0);	// disable scroll bar.
1778 	}
1779 }
1780 
1781 void TitleView::DragSelectedColumn(BPoint position)
1782 {
1783 	float invalidLeft = fSelectedColumnRect.left;
1784 	float invalidRight = fSelectedColumnRect.right;
1785 
1786 	float leftEdge;
1787 	int32 columnIndex = FindColumn(position, &leftEdge);
1788 	fSelectedColumnRect.OffsetTo(leftEdge, 0);
1789 
1790 	MoveColumn(fSelectedColumn, columnIndex);
1791 
1792 	fSelectedColumn->fVisible = true;
1793 	ComputeDragBoundries(fSelectedColumn, position);
1794 
1795 	// Redraw the new column position
1796 	GetTitleRect(fSelectedColumn, &fSelectedColumnRect);
1797 	invalidLeft = MIN(fSelectedColumnRect.left, invalidLeft);
1798 	invalidRight = MAX(fSelectedColumnRect.right, invalidRight);
1799 
1800 	Invalidate(BRect(invalidLeft, 0, invalidRight, fVisibleRect.bottom));
1801 	fOutlineView->Invalidate(BRect(invalidLeft, 0, invalidRight,
1802 		fOutlineView->VisibleRect().bottom));
1803 
1804 	DrawTitle(this, fSelectedColumnRect, fSelectedColumn, true);
1805 }
1806 
1807 void TitleView::MoveColumn(BColumn *column, int32 index)
1808 {
1809 	fColumns->RemoveItem((void*) column);
1810 
1811 	if (-1 == index)
1812 	{
1813 		// Re-add the column at the end of the list.
1814 		fColumns->AddItem((void*) column);
1815 	}
1816 	else
1817 	{
1818 		fColumns->AddItem((void*) column, index);
1819 	}
1820 }
1821 
1822 void TitleView::SetColumnFlags(column_flags flags)
1823 {
1824 	fColumnFlags = flags;
1825 }
1826 
1827 
1828 void TitleView::ResizeSelectedColumn(BPoint position)
1829 {
1830 	float minWidth = fSelectedColumn->MinWidth();
1831 	float maxWidth = fSelectedColumn->MaxWidth();
1832 
1833 	float originalEdge = fSelectedColumnRect.left + fSelectedColumn->Width();
1834 	if (position.x > fSelectedColumnRect.left + maxWidth)
1835 		fSelectedColumn->SetWidth(maxWidth);
1836 	else if (position.x < fSelectedColumnRect.left + minWidth)
1837 		fSelectedColumn->SetWidth(minWidth);
1838 	else
1839 		fSelectedColumn->SetWidth(position.x - fSelectedColumnRect.left - 1);
1840 
1841 	float dX = fSelectedColumnRect.left + fSelectedColumn->Width() - originalEdge;
1842 	if (dX != 0) {
1843 		BRect originalRect(originalEdge, 0, 1000000.0, fVisibleRect.Height());
1844 		BRect movedRect(originalRect);
1845 		movedRect.OffsetBy(dX, 0);
1846 
1847 		// Update the size of the title column
1848 		BRect sourceRect(0, 0, fSelectedColumn->Width(), fVisibleRect.Height());
1849 		BRect destRect(sourceRect);
1850 		destRect.OffsetBy(fSelectedColumnRect.left, 0);
1851 
1852 #if DOUBLE_BUFFERED_COLUMN_RESIZE
1853 		fDrawBuffer->Lock();
1854 		fDrawBufferView->SetHighColor(fMasterView->Color(B_COLOR_HEADER_BACKGROUND));
1855 		fDrawBufferView->FillRect(sourceRect);
1856 		DrawTitle(fDrawBufferView, sourceRect, fSelectedColumn, false);
1857 		fDrawBufferView->Sync();
1858 		fDrawBuffer->Unlock();
1859 
1860 		CopyBits(originalRect, movedRect);
1861 		DrawBitmap(fDrawBuffer, sourceRect, destRect);
1862 #else
1863 		CopyBits(originalRect, movedRect);
1864 		SetHighColor(fMasterView->Color(B_COLOR_HEADER_BACKGROUND));
1865 		FillRect(destRect);
1866 		DrawTitle(this, destRect, fSelectedColumn, false);
1867 #endif
1868 
1869 		// Update the body view
1870 		BRect slaveSize = fOutlineView->VisibleRect();
1871 		BRect slaveSource(originalRect);
1872 		slaveSource.bottom = slaveSize.bottom;
1873 		BRect slaveDest(movedRect);
1874 		slaveDest.bottom = slaveSize.bottom;
1875 		fOutlineView->CopyBits(slaveSource, slaveDest);
1876 		fOutlineView->RedrawColumn(fSelectedColumn, fSelectedColumnRect.left,
1877 			fResizingFirstColumn);
1878 
1879 		fColumnsWidth += dX;
1880 
1881 		// Update the cursor
1882 #if _INCLUDES_CLASS_CURSOR
1883 		if (fSelectedColumn->Width() == minWidth)
1884 			SetViewCursor(fMinResizeCursor, false);
1885 		else if (fSelectedColumn->Width() == maxWidth)
1886 			SetViewCursor(fMaxResizeCursor, false);
1887 		else
1888 			SetViewCursor(fResizeCursor, false);
1889 #endif
1890 	}
1891 }
1892 
1893 void TitleView::ComputeDragBoundries(BColumn *findColumn, BPoint )
1894 {
1895 	float previousColumnLeftEdge = -1000000.0;
1896 	float nextColumnRightEdge = 1000000.0;
1897 
1898 	bool foundColumn = false;
1899 	float leftEdge = MAX(kLeftMargin, fMasterView->LatchWidth());
1900 	int32 numColumns = fColumns->CountItems();
1901 	for (int index = 0; index < numColumns; index++) {
1902 		BColumn *column = (BColumn*) fColumns->ItemAt(index);
1903 		if (!column->IsVisible())
1904 			continue;
1905 
1906 		if (column == findColumn) {
1907 			foundColumn = true;
1908 			continue;
1909 		}
1910 
1911 		if (foundColumn) {
1912 			nextColumnRightEdge = leftEdge + column->Width();
1913 			break;
1914 		} else
1915 			previousColumnLeftEdge = leftEdge;
1916 
1917 		leftEdge += column->Width() + 1;
1918 	}
1919 
1920 	float rightEdge = leftEdge + findColumn->Width();
1921 
1922 	fLeftDragBoundry = MIN(previousColumnLeftEdge + findColumn->Width(), leftEdge);
1923 	fRightDragBoundry = MAX(nextColumnRightEdge, rightEdge);
1924 }
1925 
1926 void TitleView::DrawTitle(BView *view, BRect rect, BColumn *column, bool depressed)
1927 {
1928 	BRect drawRect;
1929 	rgb_color borderColor = mix_color(fMasterView->Color(B_COLOR_HEADER_BACKGROUND), make_color(0, 0, 0), 128);
1930 	rgb_color backgroundColor;
1931 
1932 	rgb_color bevelHigh;
1933 	rgb_color bevelLow;
1934 	// Want exterior borders to overlap.
1935 	rect.right += 1;
1936 	drawRect = rect;
1937 	drawRect.InsetBy(2, 2);
1938 	if (depressed) {
1939 		backgroundColor = mix_color(fMasterView->Color(B_COLOR_HEADER_BACKGROUND), make_color(0, 0, 0), 64);
1940 		bevelHigh = mix_color(backgroundColor, make_color(0, 0, 0), 64);
1941 		bevelLow = mix_color(backgroundColor, make_color(255, 255, 255), 128);
1942 		drawRect.left++;
1943 		drawRect.top++;
1944 	} else {
1945 		backgroundColor = fMasterView->Color(B_COLOR_HEADER_BACKGROUND);
1946 		bevelHigh = mix_color(backgroundColor, make_color(255, 255, 255), 192);
1947 		bevelLow = mix_color(backgroundColor, make_color(0, 0, 0), 64);
1948 		drawRect.bottom--;
1949 		drawRect.right--;
1950 	}
1951 
1952 	view->SetHighColor(borderColor);
1953 	view->StrokeRect(rect);
1954 	view->BeginLineArray(4);
1955 	view->AddLine(BPoint(rect.left+1, rect.top+1), BPoint(rect.right-1, rect.top+1), bevelHigh);
1956 	view->AddLine(BPoint(rect.left+1, rect.top+1), BPoint(rect.left+1, rect.bottom-1), bevelHigh);
1957 	view->AddLine(BPoint(rect.right-1, rect.top+1), BPoint(rect.right-1, rect.bottom-1), bevelLow);
1958 	view->AddLine(BPoint(rect.left+2, rect.bottom-1), BPoint(rect.right-1, rect.bottom-1), bevelLow);
1959 	view->EndLineArray();
1960 
1961 	font_height fh;
1962 	GetFontHeight(&fh);
1963 
1964 	float baseline = floor(drawRect.top + fh.ascent
1965 							+ (drawRect.Height()+1-(fh.ascent+fh.descent))/2);
1966 
1967 	view->SetHighColor(backgroundColor);
1968 	view->SetLowColor(backgroundColor);
1969 
1970 	view->FillRect(rect.InsetByCopy(2, 2));
1971 
1972 
1973 	// If no column given, nothing else to draw.
1974 	if (!column)
1975 		return;
1976 
1977 	view->SetHighColor(fMasterView->Color(B_COLOR_HEADER_TEXT));
1978 
1979 	BFont font;
1980 	GetFont(&font);
1981 	view->SetFont(&font);
1982 
1983 	int sortIndex = fSortColumns->IndexOf(column);
1984 	if (sortIndex >= 0) {
1985 		// Draw sort notation.
1986 		BPoint upperLeft(drawRect.right-kSortIndicatorWidth, baseline);
1987 
1988 		if (fSortColumns->CountItems() > 1) {
1989 			char str[256];
1990 			sprintf(str, "%d", sortIndex + 1);
1991 			const float w = view->StringWidth(str);
1992 			upperLeft.x -= w;
1993 
1994 			view->SetDrawingMode(B_OP_COPY);
1995 			view->MovePenTo(BPoint(upperLeft.x + kSortIndicatorWidth, baseline));
1996 			view->DrawString(str);
1997 		}
1998 
1999 		float bmh = fDownSortArrow->Bounds().Height()+1;
2000 
2001 		view->SetDrawingMode(B_OP_OVER);
2002 
2003 		if (column->fSortAscending) {
2004 			BPoint leftTop(upperLeft.x, drawRect.top + (drawRect.IntegerHeight()-fDownSortArrow->Bounds().IntegerHeight())/2);
2005 			view->DrawBitmapAsync(fDownSortArrow, leftTop);
2006 		} else {
2007 			BPoint leftTop(upperLeft.x, drawRect.top + (drawRect.IntegerHeight()-fUpSortArrow->Bounds().IntegerHeight())/2);
2008 			view->DrawBitmapAsync(fUpSortArrow, leftTop);
2009 		}
2010 
2011 		upperLeft.y = baseline-bmh+floor((fh.ascent+fh.descent-bmh)/2);
2012 		if (upperLeft.y < drawRect.top) upperLeft.y = drawRect.top;
2013 
2014 		// Adjust title stuff for sort indicator
2015 		drawRect.right = upperLeft.x - 2;
2016 	}
2017 
2018 	if (drawRect.right > drawRect.left) {
2019 #if CONSTRAIN_CLIPPING_REGION
2020 		BRegion clipRegion;
2021 		clipRegion.Set(drawRect);
2022 		view->ConstrainClippingRegion(&clipRegion);
2023 		view->PushState();
2024 #endif
2025 		view->MovePenTo(BPoint(drawRect.left+8, baseline));
2026 		view->SetDrawingMode(B_OP_COPY);
2027 		view->SetHighColor(fMasterView->Color(B_COLOR_HEADER_TEXT));
2028 		column->DrawTitle(drawRect, view);
2029 
2030 #if CONSTRAIN_CLIPPING_REGION
2031 		view->PopState();
2032 		view->ConstrainClippingRegion(NULL);
2033 #endif
2034 	}
2035 }
2036 
2037 void TitleView::Draw(BRect invalidRect)
2038 {
2039 	float columnLeftEdge = MAX(kLeftMargin, fMasterView->LatchWidth());
2040 	for (int32 columnIndex = 0; columnIndex < fColumns->CountItems(); columnIndex++) {
2041 		BColumn *column = (BColumn*) fColumns->ItemAt(columnIndex);
2042 		if (!column->IsVisible())
2043 			continue;
2044 
2045 		if (columnLeftEdge > invalidRect.right)
2046 			break;
2047 
2048 		if (columnLeftEdge + column->Width() >= invalidRect.left) {
2049 			BRect titleRect(columnLeftEdge, 0,
2050 							columnLeftEdge + column->Width(), fVisibleRect.Height());
2051 			DrawTitle(this, titleRect, column,
2052 				(fCurrentState == DRAG_COLUMN_INSIDE_TITLE && fSelectedColumn == column));
2053 		}
2054 
2055 		columnLeftEdge += column->Width() + 1;
2056 	}
2057 
2058 
2059 	// Bevels for right title margin
2060 	if (columnLeftEdge <= invalidRect.right) {
2061 		BRect titleRect(columnLeftEdge, 0, Bounds().right+2, fVisibleRect.Height());
2062 		DrawTitle(this, titleRect, NULL, false);
2063 	}
2064 
2065 	// Bevels for left title margin
2066 	if (invalidRect.left < MAX(kLeftMargin, fMasterView->LatchWidth())) {
2067 		BRect titleRect(0, 0, MAX(kLeftMargin, fMasterView->LatchWidth()) - 1, fVisibleRect.Height());
2068 		DrawTitle(this, titleRect, NULL, false);
2069 	}
2070 
2071 #if DRAG_TITLE_OUTLINE
2072 	// (Internal) Column Drag Indicator
2073 	if (fCurrentState == DRAG_COLUMN_INSIDE_TITLE) {
2074 		BRect dragRect(fSelectedColumnRect);
2075 		dragRect.OffsetTo(fCurrentDragPosition.x - fClickPoint.x, 0);
2076 		if (dragRect.Intersects(invalidRect)) {
2077 			SetHighColor(0, 0, 255);
2078 			StrokeRect(dragRect);
2079 		}
2080 	}
2081 #endif
2082 }
2083 
2084 void TitleView::ScrollTo(BPoint position)
2085 {
2086 	fOutlineView->ScrollBy(position.x - fVisibleRect.left, 0);
2087 	fVisibleRect.OffsetTo(position.x, position.y);
2088 
2089 	// Perform the little trick if the user is scrolled over too far.
2090 	// See OutlineView::ScrollTo for a more in depth explanation
2091 	float maxScrollBarValue = fColumnsWidth + MAX(kLeftMargin, fMasterView->LatchWidth()) +
2092 		kRightMargin * 2 - fVisibleRect.Width();
2093 	BScrollBar *hScrollBar = ScrollBar(B_HORIZONTAL);
2094 	float min, max;
2095 	hScrollBar->GetRange(&min, &max);
2096 	if (max != maxScrollBarValue && position.x > maxScrollBarValue)
2097 		FixScrollBar(true);
2098 
2099 	_inherited::ScrollTo(position);
2100 }
2101 
2102 void TitleView::MessageReceived(BMessage *message)
2103 {
2104 	if (message->what == kToggleColumn) {
2105 		int32 num;
2106 		if (message->FindInt32("be:field_num", &num) == B_OK) {
2107 			for (int index = 0; index < fColumns->CountItems(); index++) {
2108 				BColumn *column = (BColumn*) fColumns->ItemAt(index);
2109 				if (!column) continue;
2110 				if (column->LogicalFieldNum() == num) {
2111 					column->SetVisible(!column->IsVisible());
2112 				}
2113 			}
2114 		}
2115 		return;
2116 	} else {
2117 		BView::MessageReceived(message);
2118 	}
2119 }
2120 
2121 void TitleView::MouseDown(BPoint position)
2122 {
2123 	if(fEditMode)
2124 		return;
2125 
2126 	int32 buttons=1;
2127 	Window()->CurrentMessage()->FindInt32("buttons", &buttons);
2128 	if (buttons == B_SECONDARY_MOUSE_BUTTON && (fColumnFlags & B_ALLOW_COLUMN_POPUP)) {
2129 		// Right mouse button -- bring up menu to show/hide columns.
2130 		if (!fColumnPop) fColumnPop = new BPopUpMenu("Columns", false, false);
2131 		fColumnPop->RemoveItems(0, fColumnPop->CountItems(), true);
2132 		BMessenger me(this);
2133 		for (int index = 0; index < fColumns->CountItems(); index++) {
2134 			BColumn *column = (BColumn*) fColumns->ItemAt(index);
2135 			if (!column) continue;
2136 			BString name;
2137 			column->GetColumnName(&name);
2138 			BMessage* msg = new BMessage(kToggleColumn);
2139 			msg->AddInt32("be:field_num", column->LogicalFieldNum());
2140 			BMenuItem* it = new BMenuItem(name.String(), msg);
2141 			it->SetMarked(column->IsVisible());
2142 			it->SetTarget(me);
2143 			fColumnPop->AddItem(it);
2144 		}
2145 		BPoint screenPosition = ConvertToScreen(position);
2146 		BRect sticky(screenPosition, screenPosition);
2147 		sticky.InsetBy(-5, -5);
2148 		fColumnPop->Go(ConvertToScreen(position), true, false, sticky, true);
2149 		return;
2150 	}
2151 
2152 	fResizingFirstColumn = true;
2153 	float leftEdge = MAX(kLeftMargin, fMasterView->LatchWidth());
2154 	for (int index = 0; index < fColumns->CountItems(); index++) {
2155 		BColumn *column = (BColumn*) fColumns->ItemAt(index);
2156 		if (!column->IsVisible())
2157 			continue;
2158 
2159 		if (leftEdge > position.x + kColumnResizeAreaWidth / 2)
2160 			break;
2161 
2162 		//	Check for resizing a column
2163 		float rightEdge = leftEdge + column->Width();
2164 
2165 		if(column->ShowHeading()) {
2166 			if (position.x > rightEdge - kColumnResizeAreaWidth / 2
2167 				&& position.x < rightEdge + kColumnResizeAreaWidth / 2
2168 				&& column->MaxWidth() > column->MinWidth()
2169 				&& (fColumnFlags & B_ALLOW_COLUMN_RESIZE)) {
2170 				fCurrentState = RESIZING_COLUMN;
2171 				fSelectedColumn = column;
2172 				fSelectedColumnRect.Set(leftEdge, 0, rightEdge, fVisibleRect.Height());
2173 				SetMouseEventMask(B_POINTER_EVENTS, B_LOCK_WINDOW_FOCUS |
2174 					B_NO_POINTER_HISTORY);
2175 				break;
2176 			}
2177 
2178 			fResizingFirstColumn = false;
2179 
2180 			//	Check for clicking on a column.
2181 			if (position.x > leftEdge && position.x < rightEdge) {
2182 				fCurrentState = PRESSING_COLUMN;
2183 				fSelectedColumn = column;
2184 				fSelectedColumnRect.Set(leftEdge, 0, rightEdge, fVisibleRect.Height());
2185 				DrawTitle(this, fSelectedColumnRect, fSelectedColumn, true);
2186 				fClickPoint = BPoint(position.x - fSelectedColumnRect.left, position.y
2187 					- fSelectedColumnRect.top);
2188 				SetMouseEventMask(B_POINTER_EVENTS, B_LOCK_WINDOW_FOCUS |
2189 					B_NO_POINTER_HISTORY);
2190 				break;
2191 			}
2192 		}
2193 		leftEdge = rightEdge + 1;
2194 	}
2195 }
2196 
2197 void TitleView::MouseMoved(BPoint position, uint32 transit, const BMessage *)
2198 {
2199 
2200 
2201 	if(fEditMode)
2202 		return;
2203 
2204 
2205 	// Handle column manipulation
2206 	switch (fCurrentState) {
2207 		case RESIZING_COLUMN:
2208 			ResizeSelectedColumn(position);
2209 			break;
2210 
2211 		case PRESSING_COLUMN: {
2212 			if (abs((int32)(position.x - (fClickPoint.x + fSelectedColumnRect.left))) > kColumnResizeAreaWidth
2213 				|| abs((int32)(position.y - (fClickPoint.y + fSelectedColumnRect.top))) > kColumnResizeAreaWidth) {
2214 				// User has moved the mouse more than the tolerable amount,
2215 				// initiate a drag.
2216 				if (transit == B_INSIDE_VIEW || transit == B_ENTERED_VIEW) {
2217 					if(fColumnFlags & B_ALLOW_COLUMN_MOVE) {
2218 						fCurrentState = DRAG_COLUMN_INSIDE_TITLE;
2219 						ComputeDragBoundries(fSelectedColumn, position);
2220 #if _INCLUDES_CLASS_CURSOR
2221 						SetViewCursor(fColumnMoveCursor, false);
2222 #endif
2223 #if DRAG_TITLE_OUTLINE
2224 						BRect invalidRect(fSelectedColumnRect);
2225 						invalidRect.OffsetTo(position.x - fClickPoint.x, 0);
2226 						fCurrentDragPosition = position;
2227 						Invalidate(invalidRect);
2228 #endif
2229 					}
2230 				} else {
2231 					if(fColumnFlags & B_ALLOW_COLUMN_REMOVE) {
2232 						// Dragged outside view
2233 						fCurrentState = DRAG_COLUMN_OUTSIDE_TITLE;
2234 						fSelectedColumn->SetVisible(false);
2235 						BRect dragRect(fSelectedColumnRect);
2236 
2237 						// There is a race condition where the mouse may have moved by the
2238 						// time we get to handle this message.  If the user drags a column very
2239 						// quickly, this results in the annoying bug where the cursor is outside
2240 						// of the rectangle that is being dragged around.
2241 						// Call GetMouse with the checkQueue flag set to false so we
2242 						// can get the most recent position of the mouse.  This minimizes
2243 						// this problem (although it is currently not possible to completely
2244 						// eliminate it).
2245 						uint32 buttons;
2246 						GetMouse(&position, &buttons, false);
2247 						dragRect.OffsetTo(position.x - fClickPoint.x, position.y - dragRect.Height() / 2);
2248 						BeginRectTracking(dragRect, B_TRACK_WHOLE_RECT);
2249 					}
2250 				}
2251 			}
2252 
2253 			break;
2254 		}
2255 
2256 		case DRAG_COLUMN_INSIDE_TITLE: {
2257 			if (transit == B_EXITED_VIEW && (fColumnFlags & B_ALLOW_COLUMN_REMOVE)) {
2258 				// Dragged outside view
2259 				fCurrentState = DRAG_COLUMN_OUTSIDE_TITLE;
2260 				fSelectedColumn->SetVisible(false);
2261 				BRect dragRect(fSelectedColumnRect);
2262 
2263 				// See explanation above.
2264 				uint32 buttons;
2265 				GetMouse(&position, &buttons, false);
2266 
2267 				dragRect.OffsetTo(position.x - fClickPoint.x, position.y - fClickPoint.y);
2268 				BeginRectTracking(dragRect, B_TRACK_WHOLE_RECT);
2269 			} else if (position.x < fLeftDragBoundry || position.x > fRightDragBoundry)
2270 				DragSelectedColumn(position);
2271 
2272 #if DRAG_TITLE_OUTLINE
2273 			// Set up the invalid rect to include the rect for the previous position
2274 			// of the drag rect, as well as the new one.
2275 			BRect invalidRect(fSelectedColumnRect);
2276 			invalidRect.OffsetTo(fCurrentDragPosition.x - fClickPoint.x, 0);
2277 			if (position.x < fCurrentDragPosition.x)
2278 				invalidRect.left -= fCurrentDragPosition.x - position.x;
2279 			else
2280 				invalidRect.right += position.x - fCurrentDragPosition.x;
2281 
2282 			fCurrentDragPosition = position;
2283 			Invalidate(invalidRect);
2284 #endif
2285 			break;
2286 		}
2287 
2288 		case DRAG_COLUMN_OUTSIDE_TITLE:
2289 			if (transit == B_ENTERED_VIEW) {
2290 				// Drag back into view
2291 				EndRectTracking();
2292 				fCurrentState = DRAG_COLUMN_INSIDE_TITLE;
2293 				fSelectedColumn->SetVisible(true);
2294 				DragSelectedColumn(position);
2295 			}
2296 
2297 			break;
2298 
2299 		case INACTIVE:
2300 			// Check for cursor changes if we are over the resize area for
2301 			// a column.
2302 			BColumn *resizeColumn = 0;
2303 			float leftEdge = MAX(kLeftMargin, fMasterView->LatchWidth());
2304 			for (int index = 0; index < fColumns->CountItems(); index++) {
2305 				BColumn *column = (BColumn*) fColumns->ItemAt(index);
2306 				if (!column->IsVisible())
2307 					continue;
2308 
2309 				if (leftEdge > position.x + kColumnResizeAreaWidth / 2)
2310 					break;
2311 
2312 				float rightEdge = leftEdge + column->Width();
2313 				if (position.x > rightEdge - kColumnResizeAreaWidth / 2
2314 					&& position.x < rightEdge + kColumnResizeAreaWidth / 2
2315 					&& column->MaxWidth() > column->MinWidth()) {
2316 					resizeColumn = column;
2317 					break;
2318 				}
2319 
2320 				leftEdge = rightEdge + 1;
2321 			}
2322 
2323 			// Update the cursor
2324 #if _INCLUDES_CLASS_CURSOR
2325 			if (resizeColumn) {
2326 				if (resizeColumn->Width() == resizeColumn->MinWidth())
2327 					SetViewCursor(fMinResizeCursor, false);
2328 				else if (resizeColumn->Width() == resizeColumn->MaxWidth())
2329 					SetViewCursor(fMaxResizeCursor, false);
2330 				else
2331 					SetViewCursor(fResizeCursor, false);
2332 			} else
2333 				SetViewCursor(B_CURSOR_SYSTEM_DEFAULT, false);
2334 #endif
2335 			break;
2336 	}
2337 }
2338 
2339 void TitleView::MouseUp(BPoint position)
2340 {
2341 	if(fEditMode)
2342 		return;
2343 
2344 	switch (fCurrentState) {
2345 		case RESIZING_COLUMN:
2346 			ResizeSelectedColumn(position);
2347 			fCurrentState = INACTIVE;
2348 			FixScrollBar(false);
2349 #if _INCLUDES_CLASS_CURSOR
2350 			SetViewCursor(B_CURSOR_SYSTEM_DEFAULT, false);
2351 #endif
2352 			break;
2353 
2354 		case PRESSING_COLUMN: {
2355 			if (fMasterView->SortingEnabled()) {
2356 				if (fSortColumns->HasItem(fSelectedColumn)) {
2357 					if ((modifiers() & B_CONTROL_KEY) == 0 && fSortColumns->CountItems() > 1) {
2358 						fSortColumns->MakeEmpty();
2359 						fSortColumns->AddItem(fSelectedColumn);
2360 					}
2361 
2362 					fSelectedColumn->fSortAscending = !fSelectedColumn->fSortAscending;
2363 				} else {
2364 					if ((modifiers() & B_CONTROL_KEY) == 0)
2365 						fSortColumns->MakeEmpty();
2366 
2367 					fSortColumns->AddItem(fSelectedColumn);
2368 					fSelectedColumn->fSortAscending = true;
2369 				}
2370 
2371 				fOutlineView->StartSorting();
2372 			}
2373 
2374 			fCurrentState = INACTIVE;
2375 			Invalidate();
2376 			break;
2377 		}
2378 
2379 		case DRAG_COLUMN_INSIDE_TITLE:
2380 			fCurrentState = INACTIVE;
2381 
2382 #if DRAG_TITLE_OUTLINE
2383 			Invalidate();	// xxx Can make this smaller
2384 #else
2385 			Invalidate(fSelectedColumnRect);
2386 #endif
2387 #if _INCLUDES_CLASS_CURSOR
2388 			SetViewCursor(B_CURSOR_SYSTEM_DEFAULT, false);
2389 #endif
2390 			break;
2391 
2392 		case DRAG_COLUMN_OUTSIDE_TITLE:
2393 			fCurrentState = INACTIVE;
2394 			EndRectTracking();
2395 #if _INCLUDES_CLASS_CURSOR
2396 			SetViewCursor(B_CURSOR_SYSTEM_DEFAULT, false);
2397 #endif
2398 			break;
2399 
2400 		default:
2401 			;
2402 	}
2403 }
2404 
2405 void TitleView::FrameResized(float width, float height)
2406 {
2407 	fVisibleRect.right = fVisibleRect.left + width;
2408 	fVisibleRect.bottom = fVisibleRect.top + height;
2409 	FixScrollBar(true);
2410 }
2411 
2412 // #pragma mark -
2413 
2414 OutlineView::OutlineView(BRect rect, BList *visibleColumns, BList *sortColumns,
2415 	BColumnListView *listView)
2416 	:	BView(rect, "outline_view", B_FOLLOW_ALL_SIDES, B_WILL_DRAW | B_FRAME_EVENTS),
2417 		fColumns(visibleColumns),
2418 		fSortColumns(sortColumns),
2419 		fItemsHeight(24.),
2420 		fVisibleRect(rect.OffsetToCopy(0, 0)),
2421 		fFocusRow(0),
2422 		fRollOverRow(0),
2423 		fLastSelectedItem(0),
2424 		fFirstSelectedItem(0),
2425 		fSortThread(B_BAD_THREAD_ID),
2426 		fCurrentState(INACTIVE),
2427 		fMasterView(listView),
2428 		fSelectionMode(B_MULTIPLE_SELECTION_LIST),
2429 		fTrackMouse(false),
2430 		fCurrentField(0),
2431 		fCurrentRow(0),
2432 		fCurrentColumn(0),
2433 		fMouseDown(false),
2434 		fCurrentCode(B_OUTSIDE_VIEW),
2435 		fEditMode(false),
2436 		fDragging(false),
2437 		fClickCount(0),
2438 		fDropHighlightY(-1)
2439 {
2440 	SetViewColor(B_TRANSPARENT_32_BIT);
2441 
2442 #if DOUBLE_BUFFERED_COLUMN_RESIZE
2443 	// xxx this needs to be smart about the size of the buffer.  Also, the buffer can
2444 	// be shared with the title's buffer.
2445 	BRect doubleBufferRect(0, 0, 600, 35);
2446 	fDrawBuffer = new BBitmap(doubleBufferRect, B_RGB32, true);
2447 	fDrawBufferView = new BView(doubleBufferRect, "double_buffer_view",
2448 		B_FOLLOW_ALL_SIDES, 0);
2449 	fDrawBuffer->Lock();
2450 	fDrawBuffer->AddChild(fDrawBufferView);
2451 	fDrawBuffer->Unlock();
2452 #endif
2453 
2454 	FixScrollBar(true);
2455 	fSelectionListDummyHead.fNextSelected = &fSelectionListDummyHead;
2456 	fSelectionListDummyHead.fPrevSelected = &fSelectionListDummyHead;
2457 }
2458 
2459 OutlineView::~OutlineView()
2460 {
2461 	fDrawBuffer->Lock();
2462 	fDrawBufferView->RemoveSelf();
2463 	fDrawBuffer->Unlock();
2464 	delete fDrawBufferView;
2465 	delete fDrawBuffer;
2466 
2467 	Clear();
2468 }
2469 
2470 void OutlineView::Clear()
2471 {
2472 	DeselectAll();	// Make sure selection list doesn't point to deleted rows!
2473 	RecursiveDeleteRows(&fRows, false);
2474 	Invalidate();
2475 	fItemsHeight = 24.;
2476 	FixScrollBar(true);
2477 }
2478 
2479 void OutlineView::SetSelectionMode(list_view_type mode)
2480 {
2481 	DeselectAll();
2482 	fSelectionMode = mode;
2483 }
2484 
2485 list_view_type OutlineView::SelectionMode() const
2486 {
2487 	return fSelectionMode;
2488 }
2489 
2490 void OutlineView::Deselect(BRow *row)
2491 {
2492 	if (row) {
2493 		if (row->fNextSelected != 0) {
2494 			row->fNextSelected->fPrevSelected = row->fPrevSelected;
2495 			row->fPrevSelected->fNextSelected = row->fNextSelected;
2496 			row->fNextSelected = 0;
2497 			row->fPrevSelected = 0;
2498 			Invalidate();
2499 		}
2500 	}
2501 }
2502 
2503 void OutlineView::AddToSelection(BRow *row)
2504 {
2505 	if (row) {
2506 		if (row->fNextSelected == 0) {
2507 			if (fSelectionMode == B_SINGLE_SELECTION_LIST)
2508 				DeselectAll();
2509 
2510 			row->fNextSelected = fSelectionListDummyHead.fNextSelected;
2511 			row->fPrevSelected = &fSelectionListDummyHead;
2512 			row->fNextSelected->fPrevSelected = row;
2513 			row->fPrevSelected->fNextSelected = row;
2514 
2515 			BRect invalidRect;
2516 			if (FindVisibleRect(row, &invalidRect))
2517 				Invalidate(invalidRect);
2518 		}
2519 	}
2520 }
2521 
2522 void OutlineView::RecursiveDeleteRows(BRowContainer* List, bool IsOwner)
2523 {
2524 	if (List) {
2525 		while (true) {
2526 			BRow *row = List->RemoveItemAt(0L);
2527 			if (row == 0)
2528 				break;
2529 
2530 			if (row->fChildList)
2531 				RecursiveDeleteRows(row->fChildList, true);
2532 
2533 			delete row;
2534 		}
2535 
2536 		if (IsOwner)
2537 			delete List;
2538 	}
2539 }
2540 
2541 
2542 void OutlineView::RedrawColumn(BColumn *column, float leftEdge, bool isFirstColumn)
2543 {
2544 	if (column) {
2545 		font_height fh;
2546 		GetFontHeight(&fh);
2547 		float line = 0.0;
2548 		for (RecursiveOutlineIterator iterator(&fRows); iterator.CurrentRow();
2549 			line += iterator.CurrentRow()->Height() + 1, iterator.GoToNext()) {
2550 			BRow *row = iterator.CurrentRow();
2551 			float rowHeight = row->Height();
2552 			if (line > fVisibleRect.bottom)
2553 				break;
2554 
2555 			if (line + rowHeight >= fVisibleRect.top) {
2556 				BRect sourceRect(0, 0, column->Width(), rowHeight);
2557 				BRect destRect(leftEdge, line, leftEdge + column->Width(), line + rowHeight);
2558 
2559 	#if DOUBLE_BUFFERED_COLUMN_RESIZE
2560 				fDrawBuffer->Lock();
2561 				if (row->fNextSelected != 0) {
2562 					if(fEditMode) {
2563 						fDrawBufferView->SetHighColor(fMasterView->Color(B_COLOR_EDIT_BACKGROUND));
2564 						fDrawBufferView->SetLowColor(fMasterView->Color(B_COLOR_EDIT_BACKGROUND));
2565 					} else {
2566 						fDrawBufferView->SetHighColor(fMasterView->Color(B_COLOR_SELECTION));
2567 						fDrawBufferView->SetLowColor(fMasterView->Color(B_COLOR_SELECTION));
2568 					}
2569 				} else {
2570 					fDrawBufferView->SetHighColor(fMasterView->Color(B_COLOR_BACKGROUND));
2571 					fDrawBufferView->SetLowColor(fMasterView->Color(B_COLOR_BACKGROUND));
2572 				}
2573 				BFont	font;
2574 				GetFont(&font);
2575 				fDrawBufferView->SetFont(&font);
2576 				fDrawBufferView->FillRect(sourceRect);
2577 
2578 				if (isFirstColumn) {
2579 					// If this is the first column, double buffer drawing the latch too.
2580 					destRect.left += iterator.CurrentLevel() * kOutlineLevelIndent
2581 						- fMasterView->LatchWidth();
2582 					sourceRect.left += iterator.CurrentLevel() * kOutlineLevelIndent
2583 						- fMasterView->LatchWidth();
2584 
2585 					LatchType pos = B_NO_LATCH;
2586 					if (row->HasLatch())
2587 						pos = row->fIsExpanded ? B_OPEN_LATCH : B_CLOSED_LATCH;
2588 
2589 					BRect latchRect(sourceRect);
2590 					latchRect.right = latchRect.left + fMasterView->LatchWidth();
2591 					fMasterView->DrawLatch(fDrawBufferView, latchRect, pos, row);
2592 				}
2593 
2594 				BField *field = row->GetField(column->fFieldID);
2595 				if (field) {
2596 					BRect fieldRect(sourceRect);
2597 					if (isFirstColumn)
2598 						fieldRect.left += fMasterView->LatchWidth();
2599 
2600 		#if CONSTRAIN_CLIPPING_REGION
2601 					BRegion clipRegion;
2602 					clipRegion.Set(fieldRect);
2603 					fDrawBufferView->ConstrainClippingRegion(&clipRegion);
2604 					fDrawBufferView->PushState();
2605 		#endif
2606 					fDrawBufferView->SetHighColor(fMasterView->Color(B_COLOR_TEXT));
2607 					float baseline = floor(fieldRect.top + fh.ascent
2608 											+ (fieldRect.Height()+1-(fh.ascent+fh.descent))/2);
2609 					fDrawBufferView->MovePenTo(fieldRect.left + 8, baseline);
2610 					column->DrawField(field, fieldRect, fDrawBufferView);
2611 		#if CONSTRAIN_CLIPPING_REGION
2612 					fDrawBufferView->PopState();
2613 					fDrawBufferView->ConstrainClippingRegion(NULL);
2614 		#endif
2615 				}
2616 
2617 				if (fFocusRow == row) {
2618 					if(!fEditMode) {
2619 						fDrawBufferView->SetHighColor(0, 0, 0);
2620 						fDrawBufferView->StrokeRect(BRect(-1, sourceRect.top, 10000.0, sourceRect.bottom - 1));
2621 					}
2622 				}
2623 
2624 				fDrawBufferView->SetHighColor(fMasterView->Color(B_COLOR_ROW_DIVIDER));
2625 		//		StrokeLine(BPoint(0, line + rowHeight - 2), BPoint(Bounds().Width(), line + rowHeight - 2));
2626 		//		StrokeLine(BPoint(0, line + rowHeight - 1), BPoint(Bounds().Width(), line + rowHeight - 1));
2627 				fDrawBufferView->StrokeLine(BPoint(0, rowHeight), BPoint(Bounds().Width(), rowHeight));
2628 
2629 				fDrawBufferView->Sync();
2630 				fDrawBuffer->Unlock();
2631 				SetDrawingMode(B_OP_OVER);
2632 				DrawBitmap(fDrawBuffer, sourceRect, destRect);
2633 
2634 	#else
2635 
2636 				if (row->fNextSelected != 0) {
2637 					if(fEditMode) {
2638 						SetHighColor(fMasterView->Color(B_COLOR_EDIT_BACKGROUND));
2639 						SetLowColor(fMasterView->Color(B_COLOR_EDIT_BACKGROUND));
2640 					}
2641 					else {
2642 						SetHighColor(fMasterView->Color(B_COLOR_SELECTION));
2643 						SetLowColor(fMasterView->Color(B_COLOR_SELECTION));
2644 					}
2645 				} else {
2646 					SetHighColor(fMasterView->Color(B_COLOR_BACKGROUND));
2647 					SetLowColor(fMasterView->Color(B_COLOR_BACKGROUND));
2648 				}
2649 
2650 				FillRect(destRect);
2651 
2652 				BField *field = row->GetField(column->fFieldID);
2653 				if (field) {
2654 		#if CONSTRAIN_CLIPPING_REGION
2655 					BRegion clipRegion;
2656 					clipRegion.Set(destRect);
2657 					ConstrainClippingRegion(&clipRegion);
2658 					PushState();
2659 		#endif
2660 					SetHighColor(fColorList[B_TEXT_COLOR]);
2661 					float baseline = floor(destRect.top + fh.ascent
2662 											+ (destRect.Height()+1-(fh.ascent+fh.descent))/2);
2663 					MovePenTo(destRect.left + 8, baseline);
2664 					column->DrawField(field, destRect, this);
2665 		#if CONSTRAIN_CLIPPING_REGION
2666 					PopState();
2667 					ConstrainClippingRegion(NULL);
2668 		#endif
2669 				}
2670 
2671 				if (fFocusRow == row) {
2672 					if(!fEditMode) {
2673 						SetHighColor(0, 0, 0);
2674 						StrokeRect(BRect(0, destRect.top, 10000.0, destRect.bottom - 1));
2675 					}
2676 				}
2677 
2678 				rgb_color color = HighColor();
2679 				SetHighColor(fMasterView->Color(B_COLOR_ROW_DIVIDER));
2680 		//		StrokeLine(BPoint(0, line + rowHeight - 2), BPoint(Bounds().Width(), line + rowHeight - 2));
2681 		//		StrokeLine(BPoint(0, line + rowHeight - 1), BPoint(Bounds().Width(), line + rowHeight - 1));
2682 				StrokeLine(BPoint(0, line + rowHeight), BPoint(Bounds().Width(), line + rowHeight));
2683 				SetHighColor(color);
2684 	#endif
2685 			}
2686 		}
2687 	}
2688 }
2689 
2690 void OutlineView::Draw(BRect invalidBounds)
2691 {
2692 #if SMART_REDRAW
2693 	BRegion invalidRegion;
2694 	GetClippingRegion(&invalidRegion);
2695 #endif
2696 
2697 	font_height fh;
2698 	GetFontHeight(&fh);
2699 
2700 	float line = 0.0;
2701 	int32 numColumns = fColumns->CountItems();
2702 	for (RecursiveOutlineIterator iterator(&fRows); iterator.CurrentRow();
2703 		iterator.GoToNext()) {
2704 		BRow *row = iterator.CurrentRow();
2705 		if (line > invalidBounds.bottom)
2706 			break;
2707 
2708 		float rowHeight = row->Height();
2709 
2710 		if (line > invalidBounds.top - rowHeight) {
2711 			bool isFirstColumn = true;
2712 			float fieldLeftEdge = MAX(kLeftMargin, fMasterView->LatchWidth());
2713 			for (int columnIndex = 0; columnIndex < numColumns; columnIndex++) {
2714 				BColumn *column = (BColumn*) fColumns->ItemAt(columnIndex);
2715 				if (!column->IsVisible())
2716 					continue;
2717 
2718 				if (!isFirstColumn && fieldLeftEdge > invalidBounds.right)
2719 					break;
2720 
2721 				if (fieldLeftEdge + column->Width() >= invalidBounds.left) {
2722 					BRect fullRect(fieldLeftEdge, line,
2723 						fieldLeftEdge + column->Width(), line + rowHeight);
2724 
2725 					bool clippedFirstColumn = false;
2726 						// This happens when a column is indented past the
2727 						// beginning of the next column.
2728 
2729 					if (row->fNextSelected != 0) {
2730 						if (Window()->IsActive()) {
2731 							if(fEditMode)
2732 								SetHighColor(fMasterView->Color(B_COLOR_EDIT_BACKGROUND));
2733 							else
2734 								SetHighColor(fMasterView->Color(B_COLOR_SELECTION));
2735 						}
2736 						else
2737 							SetHighColor(fMasterView->Color(B_COLOR_NON_FOCUS_SELECTION));
2738 					} else
2739 						SetHighColor(fMasterView->Color(B_COLOR_BACKGROUND));
2740 
2741 					BRect destRect(fullRect);
2742 					if (isFirstColumn) {
2743 						fullRect.left -= fMasterView->LatchWidth();
2744 						destRect.left += iterator.CurrentLevel() * kOutlineLevelIndent;
2745 						if (destRect.left >= destRect.right) {
2746 							// clipped
2747 							FillRect(BRect(0, line, fieldLeftEdge + column->Width(),
2748 								line + rowHeight));
2749 							clippedFirstColumn = true;
2750 						}
2751 
2752 
2753 						FillRect(BRect(0, line, MAX(kLeftMargin, fMasterView->LatchWidth()), line + row->Height()));
2754 					}
2755 
2756 
2757 #if SMART_REDRAW
2758 					if (!clippedFirstColumn && invalidRegion.Intersects(fullRect))
2759 #else
2760 					if (!clippedFirstColumn)
2761 #endif
2762 					{
2763 						FillRect(fullRect);	// Using color set above
2764 
2765 						// Draw the latch widget if it has one.
2766 						if (isFirstColumn) {
2767 							if (row == fTargetRow && fCurrentState == LATCH_CLICKED) {
2768 								// Note that this only occurs if the user is holding
2769 								// down a latch while items are added in the background.
2770 								BPoint pos;
2771 								uint32 buttons;
2772 								GetMouse(&pos, &buttons);
2773 								if (fLatchRect.Contains(pos))
2774 									fMasterView->DrawLatch(this, fLatchRect, B_PRESSED_LATCH,
2775 										fTargetRow);
2776 								else
2777 									fMasterView->DrawLatch(this, fLatchRect,
2778 										row->fIsExpanded ? B_OPEN_LATCH : B_CLOSED_LATCH,
2779 										fTargetRow);
2780 							} else {
2781 								LatchType pos = B_NO_LATCH;
2782 								if (row->HasLatch())
2783 									pos = row->fIsExpanded ? B_OPEN_LATCH : B_CLOSED_LATCH;
2784 
2785 								fMasterView->DrawLatch(this, BRect(destRect.left -
2786 									fMasterView->LatchWidth(),
2787 									destRect.top, destRect.left, destRect.bottom), pos,
2788 									row);
2789 							}
2790 						}
2791 
2792 						if (row->fNextSelected != 0) {
2793 							if (Window()->IsActive()) {
2794 								if(fEditMode)
2795 									SetLowColor(fMasterView->Color(B_COLOR_EDIT_BACKGROUND));
2796 								else
2797 									SetLowColor(fMasterView->Color(B_COLOR_SELECTION));
2798 							}
2799 							else
2800 								SetLowColor(fMasterView->Color(B_COLOR_NON_FOCUS_SELECTION));
2801 						} else
2802 							SetLowColor(fMasterView->Color(B_COLOR_BACKGROUND));
2803 
2804 						SetHighColor(fMasterView->HighColor());
2805 							// The master view just holds the high color for us.
2806 
2807 						BField *field = row->GetField(column->fFieldID);
2808 						if (field) {
2809 #if CONSTRAIN_CLIPPING_REGION
2810 							BRegion clipRegion;
2811 							clipRegion.Set(destRect);
2812 							ConstrainClippingRegion(&clipRegion);
2813 							PushState();
2814 #endif
2815 							SetHighColor(fMasterView->Color(B_COLOR_TEXT));
2816 							float baseline = floor(destRect.top + fh.ascent
2817 													+ (destRect.Height()+1-(fh.ascent+fh.descent))/2);
2818 							MovePenTo(destRect.left + 8, baseline);
2819 							column->DrawField(field, destRect, this);
2820 #if CONSTRAIN_CLIPPING_REGION
2821 							PopState();
2822 							ConstrainClippingRegion(NULL);
2823 #endif
2824 						}
2825 					}
2826 				}
2827 
2828 				isFirstColumn = false;
2829 				fieldLeftEdge += column->Width() + 1;
2830 			}
2831 
2832 			if (fieldLeftEdge <= invalidBounds.right) {
2833 				if (row->fNextSelected != 0) {
2834 					if (Window()->IsActive()) {
2835 						if(fEditMode)
2836 							SetHighColor(fMasterView->Color(B_COLOR_EDIT_BACKGROUND));
2837 						else
2838 							SetHighColor(fMasterView->Color(B_COLOR_SELECTION));
2839 					}
2840 					else
2841 						SetHighColor(fMasterView->Color(B_COLOR_NON_FOCUS_SELECTION));
2842 				} else
2843 					SetHighColor(fMasterView->Color(B_COLOR_BACKGROUND));
2844 
2845 				FillRect(BRect(fieldLeftEdge, line, invalidBounds.right,
2846 					line + rowHeight));
2847 			}
2848 		}
2849 
2850 		if (fFocusRow == row && fMasterView->IsFocus() && Window()->IsActive()) {
2851 			if(!fEditMode) {
2852 				SetHighColor(0, 0, 0);
2853 				StrokeRect(BRect(0, line, 10000.0, line + rowHeight - 1));
2854 			}
2855 		}
2856 		rgb_color color = HighColor();
2857 		SetHighColor(fMasterView->Color(B_COLOR_ROW_DIVIDER));
2858 //		StrokeLine(BPoint(0, line + rowHeight - 2), BPoint(Bounds().Width(), line + rowHeight - 2));
2859 //		StrokeLine(BPoint(0, line + rowHeight - 1), BPoint(Bounds().Width(), line + rowHeight - 1));
2860 		StrokeLine(BPoint(0, line + rowHeight), BPoint(Bounds().Width(), line + rowHeight));
2861 		SetHighColor(color);
2862 		line += rowHeight + 1;
2863 	}
2864 
2865 	if (line <= invalidBounds.bottom) {
2866 		SetHighColor(fMasterView->Color(B_COLOR_BACKGROUND));
2867 		FillRect(BRect(invalidBounds.left, line, invalidBounds.right, invalidBounds.bottom));
2868 	}
2869 
2870 	// Draw the drop target line
2871 	if (fDropHighlightY != -1)
2872 		InvertRect(BRect(0, fDropHighlightY - kDropHighlightLineHeight / 2, 1000000,
2873 			fDropHighlightY + kDropHighlightLineHeight / 2));
2874 }
2875 
2876 BRow* OutlineView::FindRow(float ypos, int32 *out_rowIndent, float *out_top)
2877 {
2878 	if (out_rowIndent && out_top) {
2879 		float line = 0.0;
2880 		for (RecursiveOutlineIterator iterator(&fRows); iterator.CurrentRow();
2881 			iterator.GoToNext()) {
2882 			BRow *row = iterator.CurrentRow();
2883 			if (line > ypos)
2884 				break;
2885 
2886 			float rowHeight = row->Height();
2887 			if (ypos <= line + rowHeight) {
2888 				*out_top = line;
2889 				*out_rowIndent = iterator.CurrentLevel();
2890 				return row;
2891 			}
2892 
2893 			line += rowHeight + 1;
2894 		}
2895 	}
2896 	return 0;
2897 }
2898 
2899 void OutlineView::SetMouseTrackingEnabled(bool enabled)
2900 {
2901 	fTrackMouse = enabled;
2902 	if (!enabled && fDropHighlightY != -1) {
2903 		InvertRect(BRect(0, fDropHighlightY - kDropHighlightLineHeight / 2, 1000000,
2904 			fDropHighlightY + kDropHighlightLineHeight / 2));	// Erase the old target line
2905 		fDropHighlightY = -1;
2906 	}
2907 }
2908 
2909 
2910 //
2911 // Note that this interaction is not totally safe.  If items are added to
2912 // the list in the background, the widget rect will be incorrect, possibly
2913 // resulting in drawing glitches.  The code that adds items needs to be a little smarter
2914 // about invalidating state.
2915 //
2916 void OutlineView::MouseDown(BPoint position)
2917 {
2918 	if(!fEditMode)
2919 		fMasterView->MakeFocus(true);
2920 
2921 	// Check to see if the user is clicking on a widget to open a section
2922 	// of the list.
2923 	bool reset_click_count = false;
2924 	int32 indent;
2925 	float rowTop;
2926 	BRow *row = FindRow(position.y, &indent, &rowTop);
2927 	if (row) {
2928 
2929 		// Update fCurrentField
2930 		bool handle_field = false;
2931 		BField *new_field = 0;
2932 		BRow *new_row = 0;
2933 		BColumn *new_column = 0;
2934 		BRect new_rect;
2935 
2936 		if(position.y >=0 ) {
2937 			if(position.x >=0 ) {
2938 				float x=0;
2939 				for(int32 c=0;c<fMasterView->CountColumns();c++) {
2940 					new_column = fMasterView->ColumnAt(c);
2941 					if (!new_column->IsVisible())
2942 						continue;
2943 					if((MAX(kLeftMargin, fMasterView->LatchWidth())+x)+new_column->Width() >= position.x) {
2944 						if(new_column->WantsEvents()) {
2945 							new_field = row->GetField(c);
2946 							new_row = row;
2947 							FindRect(new_row,&new_rect);
2948 							new_rect.left = MAX(kLeftMargin, fMasterView->LatchWidth()) + x;
2949 							new_rect.right = new_rect.left + new_column->Width() - 1;
2950 							handle_field = true;
2951 						}
2952 						break;
2953 					}
2954 					x += new_column->Width();
2955 				}
2956 			}
2957 		}
2958 
2959 		// Handle mouse down
2960 		if(handle_field) {
2961 			fMouseDown = true;
2962 			fFieldRect = new_rect;
2963 			fCurrentColumn = new_column;
2964 			fCurrentRow = new_row;
2965 			fCurrentField = new_field;
2966 			fCurrentCode = B_INSIDE_VIEW;
2967 			fCurrentColumn->MouseDown(fMasterView,fCurrentRow,fCurrentField,fFieldRect,position,1);
2968 		}
2969 
2970 		if(!fEditMode) {
2971 
2972 			fTargetRow = row;
2973 			fTargetRowTop = rowTop;
2974 			FindVisibleRect(fFocusRow, &fFocusRowRect);
2975 
2976 			float leftWidgetBoundry = indent * kOutlineLevelIndent + MAX(kLeftMargin, fMasterView->LatchWidth()) -
2977 				fMasterView->LatchWidth();
2978 			fLatchRect.Set(leftWidgetBoundry, rowTop, leftWidgetBoundry +
2979 				fMasterView->LatchWidth(), rowTop + row->Height());
2980 			if (fLatchRect.Contains(position) && row->HasLatch()) {
2981 				fCurrentState = LATCH_CLICKED;
2982 				if (fTargetRow->fNextSelected != 0) {
2983 					if(fEditMode)
2984 						SetHighColor(fMasterView->Color(B_COLOR_EDIT_BACKGROUND));
2985 					else
2986 						SetHighColor(fMasterView->Color(B_COLOR_SELECTION));
2987 				}
2988 				else
2989 					SetHighColor(fMasterView->Color(B_COLOR_BACKGROUND));
2990 
2991 				FillRect(fLatchRect);
2992 				if (fLatchRect.Contains(position))
2993 					fMasterView->DrawLatch(this, fLatchRect, B_PRESSED_LATCH, row);
2994 				else
2995 					fMasterView->DrawLatch(this, fLatchRect, fTargetRow->fIsExpanded
2996 						? B_OPEN_LATCH : B_CLOSED_LATCH, row);
2997 
2998 			} else {
2999 				Invalidate(fFocusRowRect);
3000 				fFocusRow = fTargetRow;
3001 				FindVisibleRect(fFocusRow, &fFocusRowRect);
3002 
3003 				ASSERT(fTargetRow != 0);
3004 
3005 				if ((modifiers() & B_CONTROL_KEY) == 0)
3006 					DeselectAll();
3007 
3008 				if ((modifiers() & B_SHIFT_KEY) != 0 && fFirstSelectedItem != 0
3009 					&& fSelectionMode == B_MULTIPLE_SELECTION_LIST) {
3010 					SelectRange(fFirstSelectedItem, fTargetRow);
3011 				}
3012 				else {
3013 					if (fTargetRow->fNextSelected != 0) {
3014 						// Unselect row
3015 						fTargetRow->fNextSelected->fPrevSelected = fTargetRow->fPrevSelected;
3016 						fTargetRow->fPrevSelected->fNextSelected = fTargetRow->fNextSelected;
3017 						fTargetRow->fPrevSelected = 0;
3018 						fTargetRow->fNextSelected = 0;
3019 						fFirstSelectedItem = NULL;
3020 					} else {
3021 						// Select row
3022 						if (fSelectionMode == B_SINGLE_SELECTION_LIST)
3023 							DeselectAll();
3024 
3025 						fTargetRow->fNextSelected = fSelectionListDummyHead.fNextSelected;
3026 						fTargetRow->fPrevSelected = &fSelectionListDummyHead;
3027 						fTargetRow->fNextSelected->fPrevSelected = fTargetRow;
3028 						fTargetRow->fPrevSelected->fNextSelected = fTargetRow;
3029 						fFirstSelectedItem = fTargetRow;
3030 					}
3031 
3032 					Invalidate(BRect(fVisibleRect.left, fTargetRowTop, fVisibleRect.right,
3033 						fTargetRowTop + fTargetRow->Height()));
3034 				}
3035 
3036 				fCurrentState = ROW_CLICKED;
3037 				if (fLastSelectedItem != fTargetRow)
3038 					reset_click_count = true;
3039 				fLastSelectedItem = fTargetRow;
3040 				fMasterView->SelectionChanged();
3041 
3042 			}
3043 		}
3044 
3045 		SetMouseEventMask(B_POINTER_EVENTS, B_LOCK_WINDOW_FOCUS |
3046 			B_NO_POINTER_HISTORY);
3047 
3048 	} else if (fFocusRow != 0) {
3049 		// User clicked in open space, unhighlight focus row.
3050 		fFocusRow = 0;
3051 		Invalidate(fFocusRowRect);
3052 	}
3053 
3054 	// We stash the click counts here because the 'clicks' field
3055 	// is not in the CurrentMessage() when MouseUp is called... ;(
3056 	if (reset_click_count)
3057 		fClickCount = 1;
3058 	else
3059 		Window()->CurrentMessage()->FindInt32("clicks", &fClickCount);
3060 	fClickPoint = position;
3061 
3062 } // end of MouseDown()
3063 
3064 void OutlineView::MouseMoved(BPoint position, uint32 /*transit*/, const BMessage */*message*/)
3065 {
3066 	if(!fMouseDown) {
3067 		// Update fCurrentField
3068 		bool handle_field = false;
3069 		BField *new_field = 0;
3070 		BRow *new_row = 0;
3071 		BColumn *new_column = 0;
3072 		BRect new_rect(0,0,0,0);
3073 		if(position.y >=0 ) {
3074 			float top;
3075 			int32 indent;
3076 			BRow *row = FindRow(position.y, &indent, &top);
3077 			if(row && position.x >=0 ) {
3078 				float x=0;
3079 				for(int32 c=0;c<fMasterView->CountColumns();c++) {
3080 					new_column = fMasterView->ColumnAt(c);
3081 					if (!new_column->IsVisible())
3082 						continue;
3083 					if((MAX(kLeftMargin, fMasterView->LatchWidth())+x)+new_column->Width() > position.x) {
3084 						if(new_column->WantsEvents()) {
3085 							new_field = row->GetField(c);
3086 							new_row = row;
3087 							FindRect(new_row,&new_rect);
3088 							new_rect.left = MAX(kLeftMargin, fMasterView->LatchWidth()) + x;
3089 							new_rect.right = new_rect.left + new_column->Width() - 1;
3090 							handle_field = true;
3091 						}
3092 						break;
3093 					}
3094 					x += new_column->Width();
3095 				}
3096 			}
3097 		}
3098 
3099 		// Handle mouse moved
3100 		if(handle_field) {
3101 			if(new_field != fCurrentField) {
3102 				if(fCurrentField) {
3103 					fCurrentColumn->MouseMoved(fMasterView, fCurrentRow,
3104 						fCurrentField, fFieldRect, position, 0, fCurrentCode = B_EXITED_VIEW);
3105 				}
3106 				fCurrentColumn = new_column;
3107 				fCurrentRow = new_row;
3108 				fCurrentField = new_field;
3109 				fFieldRect = new_rect;
3110 				if(fCurrentField) {
3111 					fCurrentColumn->MouseMoved(fMasterView, fCurrentRow,
3112 						fCurrentField, fFieldRect, position, 0, fCurrentCode = B_ENTERED_VIEW);
3113 				}
3114 			} else {
3115 				if(fCurrentField) {
3116 					fCurrentColumn->MouseMoved(fMasterView, fCurrentRow,
3117 						fCurrentField, fFieldRect, position, 0, fCurrentCode = B_INSIDE_VIEW);
3118 				}
3119 			}
3120 		} else {
3121 			if(fCurrentField) {
3122 				fCurrentColumn->MouseMoved(fMasterView, fCurrentRow,
3123 						fCurrentField, fFieldRect, position, 0, fCurrentCode = B_EXITED_VIEW);
3124 				fCurrentField = 0;
3125 				fCurrentColumn = 0;
3126 				fCurrentRow = 0;
3127 			}
3128 		}
3129 	} else {
3130 		if(fCurrentField) {
3131 			if(fFieldRect.Contains(position)) {
3132 				if (   fCurrentCode == B_OUTSIDE_VIEW
3133 					|| fCurrentCode == B_EXITED_VIEW
3134 				) {
3135 					fCurrentColumn->MouseMoved(fMasterView, fCurrentRow,
3136 						fCurrentField, fFieldRect, position, 1, fCurrentCode = B_ENTERED_VIEW);
3137 				} else {
3138 					fCurrentColumn->MouseMoved(fMasterView, fCurrentRow,
3139 						fCurrentField, fFieldRect, position, 1, fCurrentCode = B_INSIDE_VIEW);
3140 				}
3141 			} else {
3142 				if (   fCurrentCode == B_INSIDE_VIEW
3143 					|| fCurrentCode == B_ENTERED_VIEW
3144 				) {
3145 					fCurrentColumn->MouseMoved(fMasterView, fCurrentRow,
3146 						fCurrentField, fFieldRect, position, 1, fCurrentCode = B_EXITED_VIEW);
3147 				} else {
3148 					fCurrentColumn->MouseMoved(fMasterView, fCurrentRow,
3149 						fCurrentField, fFieldRect, position, 1, fCurrentCode = B_OUTSIDE_VIEW);
3150 				}
3151 			}
3152 		}
3153 	}
3154 
3155 	if(!fEditMode) {
3156 
3157 		switch (fCurrentState) {
3158 			case LATCH_CLICKED:
3159 				if (fTargetRow->fNextSelected != 0) {
3160 					if(fEditMode)
3161 						SetHighColor(fMasterView->Color(B_COLOR_EDIT_BACKGROUND));
3162 					else
3163 						SetHighColor(fMasterView->Color(B_COLOR_SELECTION));
3164 				}
3165 				else
3166 					SetHighColor(fMasterView->Color(B_COLOR_BACKGROUND));
3167 
3168 				FillRect(fLatchRect);
3169 				if (fLatchRect.Contains(position))
3170 					fMasterView->DrawLatch(this, fLatchRect, B_PRESSED_LATCH, fTargetRow);
3171 				else
3172 					fMasterView->DrawLatch(this, fLatchRect, fTargetRow->fIsExpanded
3173 						? B_OPEN_LATCH : B_CLOSED_LATCH, fTargetRow);
3174 
3175 				break;
3176 
3177 
3178 			case ROW_CLICKED:
3179 				if (abs((int)(position.x - fClickPoint.x)) > kRowDragSensitivity
3180 					|| abs((int)(position.y - fClickPoint.y)) > kRowDragSensitivity) {
3181 					fCurrentState = DRAGGING_ROWS;
3182 					fMasterView->InitiateDrag(fClickPoint, fTargetRow->fNextSelected != 0);
3183 				}
3184 				break;
3185 
3186 			case DRAGGING_ROWS:
3187 #if 0
3188 				// falls through...
3189 #else
3190 				if (fTrackMouse /*&& message*/) {
3191 					if (fVisibleRect.Contains(position)) {
3192 						float top;
3193 						int32 indent;
3194 						BRow *target = FindRow(position.y, &indent, &top);
3195 						if(target)
3196 							SetFocusRow(target,true);
3197 					}
3198 				}
3199 				break;
3200 #endif
3201 
3202 			default: {
3203 
3204 				if (fTrackMouse /*&& message*/) {
3205 					// Draw a highlight line...
3206 					if (fVisibleRect.Contains(position)) {
3207 						float top;
3208 						int32 indent;
3209 						BRow *target = FindRow(position.y, &indent, &top);
3210 						if(target==fRollOverRow)
3211 							break;
3212 						if(fRollOverRow)
3213 						{
3214 							BRect rect;
3215 							FindRect(fRollOverRow, &rect);
3216 							Invalidate(rect);
3217 						}
3218 						fRollOverRow=target;
3219 #if 0
3220 						SetFocusRow(fRollOverRow,false);
3221 #else
3222 						PushState();
3223 						SetDrawingMode(B_OP_BLEND);
3224 						SetHighColor(255,255,255,255);
3225 						BRect rect;
3226 						FindRect(fRollOverRow, &rect);
3227 						rect.bottom -= 1.0;
3228 						FillRect(rect);
3229 						PopState();
3230 #endif
3231 					} else {
3232 						if(fRollOverRow)
3233 						{
3234 							BRect rect;
3235 							FindRect(fRollOverRow, &rect);
3236 							Invalidate(rect);
3237 							fRollOverRow = NULL;
3238 						}
3239 					}
3240 				}
3241 			}
3242 		}
3243 	}
3244 } // end of MouseMoved()
3245 
3246 void OutlineView::MouseUp(BPoint position)
3247 {
3248 	if(fCurrentField) {
3249 		fCurrentColumn->MouseUp(fMasterView,fCurrentRow,fCurrentField);
3250 		fMouseDown = false;
3251 	}
3252 
3253 	if(!fEditMode) {
3254 		switch (fCurrentState) {
3255 			case LATCH_CLICKED:
3256 				if (fLatchRect.Contains(position))
3257 					fMasterView->ExpandOrCollapse(fTargetRow, !fTargetRow->fIsExpanded);
3258 
3259 				Invalidate(fLatchRect);
3260 				fCurrentState = INACTIVE;
3261 				break;
3262 
3263 			case ROW_CLICKED:
3264 				if (fClickCount > 1
3265 					&& abs((int)fClickPoint.x - (int)position.x) < kDoubleClickMoveSensitivity
3266 					&& abs((int)fClickPoint.y - (int)position.y) < kDoubleClickMoveSensitivity) {
3267 					fMasterView->ItemInvoked();
3268 				}
3269 				fCurrentState = INACTIVE;
3270 				break;
3271 
3272 			case DRAGGING_ROWS:
3273 				fCurrentState = INACTIVE;
3274 				// Falls through
3275 
3276 			default:
3277 				if (fDropHighlightY != -1) {
3278 					InvertRect(BRect(0, fDropHighlightY - kDropHighlightLineHeight / 2, 1000000,
3279 						fDropHighlightY + kDropHighlightLineHeight / 2));	// Erase the old target line
3280 					fDropHighlightY = -1;
3281 				}
3282 		}
3283 	}
3284 } // end of MouseUp()
3285 
3286 void OutlineView::MessageReceived(BMessage *message)
3287 {
3288 	if (message->WasDropped()) {
3289 		fMasterView->MessageDropped(message, ConvertFromScreen(message->DropPoint()));
3290 	} else {
3291 		BView::MessageReceived(message);
3292 	}
3293 }
3294 
3295 void OutlineView::ChangeFocusRow(bool up, bool updateSelection, bool addToCurrentSelection)
3296 {
3297 	int32 indent;
3298 	float top;
3299 	float newRowPos = 0;
3300 	float verticalScroll = 0;
3301 
3302 	if (fFocusRow) {
3303 		// A row currently has the focus, get information about it
3304 		newRowPos = fFocusRowRect.top + (up ? -4 : fFocusRow->Height() + 4);
3305 		if (newRowPos < fVisibleRect.top + 20)
3306 			verticalScroll = newRowPos - 20;
3307 		else if (newRowPos > fVisibleRect.bottom - 20)
3308 			verticalScroll = newRowPos - fVisibleRect.Height() + 20;
3309 	} else
3310 		newRowPos = fVisibleRect.top + 2;
3311 			// no row is currently focused, set this to the top of the window
3312 			// so we will select the first visible item in the list.
3313 
3314 	BRow *newRow = FindRow(newRowPos, &indent, &top);
3315 	if (newRow) {
3316 		if (fFocusRow) {
3317 			fFocusRowRect.right = 10000;
3318 			Invalidate(fFocusRowRect);
3319 		}
3320 		fFocusRow = newRow;
3321 		fFocusRowRect.top = top;
3322 		fFocusRowRect.left = 0;
3323 		fFocusRowRect.right = 10000;
3324 		fFocusRowRect.bottom = fFocusRowRect.top + fFocusRow->Height();
3325 		Invalidate(fFocusRowRect);
3326 
3327 		if (updateSelection) {
3328 			if (!addToCurrentSelection || fSelectionMode == B_SINGLE_SELECTION_LIST)
3329 				DeselectAll();
3330 
3331 			if (fFocusRow->fNextSelected == 0) {
3332 				fFocusRow->fNextSelected = fSelectionListDummyHead.fNextSelected;
3333 				fFocusRow->fPrevSelected = &fSelectionListDummyHead;
3334 				fFocusRow->fNextSelected->fPrevSelected = fFocusRow;
3335 				fFocusRow->fPrevSelected->fNextSelected = fFocusRow;
3336 			}
3337 
3338 			fLastSelectedItem = fFocusRow;
3339 		}
3340 	} else
3341 		Invalidate(fFocusRowRect);
3342 
3343 	if (verticalScroll != 0) {
3344 		BScrollBar *vScrollBar = ScrollBar(B_VERTICAL);
3345 		float min, max;
3346 		vScrollBar->GetRange(&min, &max);
3347 		if (verticalScroll < min)
3348 			verticalScroll = min;
3349 		else if (verticalScroll > max)
3350 			verticalScroll = max;
3351 
3352 		vScrollBar->SetValue(verticalScroll);
3353 	}
3354 
3355 	if (newRow && updateSelection)
3356 		fMasterView->SelectionChanged();
3357 }
3358 
3359 void OutlineView::MoveFocusToVisibleRect()
3360 {
3361 	fFocusRow = 0;
3362 	ChangeFocusRow(true, true, false);
3363 }
3364 
3365 BRow* OutlineView::CurrentSelection(BRow *lastSelected) const
3366 {
3367 	BRow *row;
3368 	if (lastSelected == 0)
3369 		row = fSelectionListDummyHead.fNextSelected;
3370 	else
3371 		row = lastSelected->fNextSelected;
3372 
3373 
3374 	if (row == &fSelectionListDummyHead)
3375 		row = 0;
3376 
3377 	return row;
3378 }
3379 
3380 void OutlineView::ToggleFocusRowSelection(bool selectRange)
3381 {
3382 	if (fFocusRow == 0)
3383 		return;
3384 
3385 	if (selectRange && fSelectionMode == B_MULTIPLE_SELECTION_LIST)
3386 		SelectRange(fLastSelectedItem, fFocusRow);
3387 	else {
3388 		if (fFocusRow->fNextSelected != 0) {
3389 			// Unselect row
3390 			fFocusRow->fNextSelected->fPrevSelected = fFocusRow->fPrevSelected;
3391 			fFocusRow->fPrevSelected->fNextSelected = fFocusRow->fNextSelected;
3392 			fFocusRow->fPrevSelected = 0;
3393 			fFocusRow->fNextSelected = 0;
3394 		} else {
3395 			// Select row
3396 			if (fSelectionMode == B_SINGLE_SELECTION_LIST)
3397 				DeselectAll();
3398 
3399 			fFocusRow->fNextSelected = fSelectionListDummyHead.fNextSelected;
3400 			fFocusRow->fPrevSelected = &fSelectionListDummyHead;
3401 			fFocusRow->fNextSelected->fPrevSelected = fFocusRow;
3402 			fFocusRow->fPrevSelected->fNextSelected = fFocusRow;
3403 		}
3404 	}
3405 
3406 	fLastSelectedItem = fFocusRow;
3407 	fMasterView->SelectionChanged();
3408 	Invalidate(fFocusRowRect);
3409 }
3410 
3411 void OutlineView::ToggleFocusRowOpen()
3412 {
3413 	if (fFocusRow)
3414 		fMasterView->ExpandOrCollapse(fFocusRow, !fFocusRow->fIsExpanded);
3415 }
3416 
3417 
3418 // xxx Could use CopyBits here to speed things up.
3419 void OutlineView::ExpandOrCollapse(BRow* ParentRow, bool Expand)
3420 {
3421 	if (ParentRow) {
3422 
3423 		if (ParentRow->fIsExpanded == Expand)
3424 			return;
3425 
3426 		ParentRow->fIsExpanded = Expand;
3427 
3428 		BRect parentRect;
3429 		if (FindRect(ParentRow, &parentRect)) {
3430 			// Determine my new height
3431 			float subTreeHeight = 0.0;
3432 			if (ParentRow->fIsExpanded)
3433 				for (RecursiveOutlineIterator iterator(ParentRow->fChildList);
3434 				     iterator.CurrentRow();
3435 				     iterator.GoToNext()
3436 				    )
3437 				{
3438 					subTreeHeight += iterator.CurrentRow()->Height()+1;
3439 				}
3440 			else
3441 				for (RecursiveOutlineIterator iterator(ParentRow->fChildList);
3442 				     iterator.CurrentRow();
3443 				     iterator.GoToNext()
3444 				    )
3445 				{
3446 					subTreeHeight -= iterator.CurrentRow()->Height()+1;
3447 				}
3448 			fItemsHeight += subTreeHeight;
3449 
3450 			// Adjust focus row if necessary.
3451 			if (FindRect(fFocusRow, &fFocusRowRect) == false) {
3452 				// focus row is in a subtree that has collapsed, move it up to the parent.
3453 				fFocusRow = ParentRow;
3454 				FindRect(fFocusRow, &fFocusRowRect);
3455 			}
3456 
3457 			Invalidate(BRect(0, parentRect.top, fVisibleRect.right, fVisibleRect.bottom));
3458 			FixScrollBar(false);
3459 		}
3460 	}
3461 
3462 }
3463 
3464 void OutlineView::RemoveRow(BRow *row)
3465 {
3466 	if (row) {
3467 		BRow *parentRow;
3468 		bool parentIsVisible;
3469 		float subTreeHeight = row->Height();
3470 		if (FindParent(row, &parentRow, &parentIsVisible)) {
3471 			// adjust height
3472 			if (parentIsVisible && (parentRow == 0 || parentRow->fIsExpanded)) {
3473 				if (row->fIsExpanded) {
3474 					for (RecursiveOutlineIterator iterator(row->fChildList);
3475 						iterator.CurrentRow(); iterator.GoToNext())
3476 						subTreeHeight += iterator.CurrentRow()->Height();
3477 				}
3478 			}
3479 		}
3480 		if (parentRow) {
3481 			if (parentRow->fIsExpanded)
3482 				fItemsHeight -= subTreeHeight + 1;
3483 		}
3484 		else {
3485 			fItemsHeight -= subTreeHeight + 1;
3486 		}
3487 		FixScrollBar(false);
3488 		if (parentRow)
3489 			parentRow->fChildList->RemoveItem(row);
3490 		else
3491 			fRows.RemoveItem(row);
3492 
3493 		if (parentRow != 0 && parentRow->fChildList->CountItems() == 0) {
3494 			delete parentRow->fChildList;
3495 			parentRow->fChildList = 0;
3496 			if (parentIsVisible)
3497 				Invalidate();	// xxx crude way of redrawing latch
3498 		}
3499 
3500 		if (parentIsVisible && (parentRow == 0 || parentRow->fIsExpanded))
3501 			Invalidate();	// xxx make me smarter.
3502 
3503 
3504 		// Adjust focus row if necessary.
3505 		if (fFocusRow && FindRect(fFocusRow, &fFocusRowRect) == false) {
3506 			// focus row is in a subtree that is gone, move it up to the parent.
3507 			fFocusRow = parentRow;
3508 			if (fFocusRow)
3509 				FindRect(fFocusRow, &fFocusRowRect);
3510 		}
3511 
3512 		// Remove this from the selection if necessary
3513 		if (row->fNextSelected != 0) {
3514 			row->fNextSelected->fPrevSelected = row->fPrevSelected;
3515 			row->fPrevSelected->fNextSelected = row->fNextSelected;
3516 			row->fPrevSelected = 0;
3517 			row->fNextSelected = 0;
3518 			fMasterView->SelectionChanged();
3519 		}
3520 
3521 		fCurrentColumn = 0;
3522 		fCurrentRow = 0;
3523 		fCurrentField = 0;
3524 	}
3525 }
3526 
3527 BRowContainer* OutlineView::RowList()
3528 {
3529 	return &fRows;
3530 }
3531 
3532 void OutlineView::UpdateRow(BRow *row)
3533 {
3534 	if (row) {
3535 		// Determine if this row has changed its sort order
3536 		BRow *parentRow = NULL;
3537 		bool parentIsVisible = false;
3538 		FindParent(row, &parentRow, &parentIsVisible);
3539 
3540 		BRowContainer* list = (parentRow == NULL) ? &fRows : parentRow->fChildList;
3541 
3542 		if(list) {
3543 			int32 rowIndex = list->IndexOf(row);
3544 			ASSERT(rowIndex >= 0);
3545 			ASSERT(list->ItemAt(rowIndex) == row);
3546 
3547 			bool rowMoved = false;
3548 			if (rowIndex > 0 && CompareRows(list->ItemAt(rowIndex - 1), row) > 0)
3549 				rowMoved = true;
3550 
3551 			if (rowIndex < list->CountItems() - 1 && CompareRows(list->ItemAt(rowIndex + 1),
3552 				row) < 0)
3553 				rowMoved = true;
3554 
3555 			if (rowMoved) {
3556 				// Sort location of this row has changed.
3557 				// Remove and re-add in the right spot
3558 				SortList(list, parentIsVisible && (parentRow == NULL || parentRow->fIsExpanded));
3559 			} else if (parentIsVisible && (parentRow == NULL || parentRow->fIsExpanded)) {
3560 				BRect invalidRect;
3561 				if (FindVisibleRect(row, &invalidRect))
3562 					Invalidate(invalidRect);
3563 			}
3564 		}
3565 	}
3566 }
3567 
3568 void OutlineView::AddRow(BRow* Row, int32 Index, BRow* ParentRow)
3569 {
3570 	if(Row) {
3571 		Row->fParent = ParentRow;
3572 
3573 		if (fMasterView->SortingEnabled()) {
3574 			// Ignore index here.
3575 			if (ParentRow) {
3576 				if (ParentRow->fChildList == 0)
3577 					ParentRow->fChildList = new BRowContainer;
3578 
3579 				AddSorted(ParentRow->fChildList, Row);
3580 			} else
3581 				AddSorted(&fRows, Row);
3582 		} else {
3583 			// Note, a -1 index implies add to end if sorting is not enabled
3584 			if (ParentRow) {
3585 				if (ParentRow->fChildList == 0)
3586 					ParentRow->fChildList = new BRowContainer;
3587 
3588 				if (Index < 0 || Index > ParentRow->fChildList->CountItems())
3589 					ParentRow->fChildList->AddItem(Row);
3590 				else
3591 					ParentRow->fChildList->AddItem(Row, Index);
3592 			} else {
3593 				if (Index < 0 || Index >= fRows.CountItems())
3594 					fRows.AddItem(Row);
3595 				else
3596 					fRows.AddItem(Row, Index);
3597 			}
3598 		}
3599 
3600 		if (ParentRow == 0 || ParentRow->fIsExpanded)
3601 			fItemsHeight += Row->Height() + 1;
3602 
3603 		FixScrollBar(false);
3604 
3605 		BRect newRowRect;
3606 		bool newRowIsInOpenBranch = FindRect(Row, &newRowRect);
3607 
3608 		if (fFocusRow && fFocusRowRect.top > newRowRect.bottom) {
3609 			// The focus row has moved.
3610 			Invalidate(fFocusRowRect);
3611 			FindRect(fFocusRow, &fFocusRowRect);
3612 			Invalidate(fFocusRowRect);
3613 		}
3614 
3615 		if (newRowIsInOpenBranch) {
3616 			if (fCurrentState == INACTIVE) {
3617 				if (newRowRect.bottom < fVisibleRect.top) {
3618 					// The new row is totally above the current viewport, move
3619 					// everything down and redraw the first line.
3620 					BRect source(fVisibleRect);
3621 					BRect dest(fVisibleRect);
3622 					source.bottom -= Row->Height() + 1;
3623 					dest.top += Row->Height() + 1;
3624 					CopyBits(source, dest);
3625 					Invalidate(BRect(fVisibleRect.left, fVisibleRect.top, fVisibleRect.right,
3626 						fVisibleRect.top + newRowRect.Height()));
3627 				} else if (newRowRect.top < fVisibleRect.bottom) {
3628 					// New item is somewhere in the current region.  Scroll everything
3629 					// beneath it down and invalidate just the new row rect.
3630 					BRect source(fVisibleRect.left, newRowRect.top, fVisibleRect.right,
3631 						fVisibleRect.bottom - newRowRect.Height());
3632 					BRect dest(source);
3633 					dest.OffsetBy(0, newRowRect.Height() + 1);
3634 					CopyBits(source, dest);
3635 					Invalidate(newRowRect);
3636 				} // otherwise, this is below the currently visible region
3637 			} else {
3638 				// Adding the item may have caused the item that the user is currently
3639 				// selected to move.  This would cause annoying drawing and interaction
3640 				// bugs, as the position of that item is cached.  If this happens, resize
3641 				// the scroll bar, then scroll back so the selected item is in view.
3642 				BRect targetRect;
3643 				if (FindRect(fTargetRow, &targetRect)) {
3644 					float delta = targetRect.top - fTargetRowTop;
3645 					if (delta != 0) {
3646 						// This causes a jump because ScrollBy will copy a chunk of the view.
3647 						// Since the actual contents of the view have been offset, we don't
3648 						// want this, we just want to change the virtual origin of the window.
3649 						// Constrain the clipping region so everything is clipped out so no
3650 						// copy occurs.
3651 						//
3652 						//	xxx this currently doesn't work if the scroll bars aren't enabled.
3653 						//  everything will still move anyway.  A minor annoyance.
3654 						BRegion emptyRegion;
3655 						ConstrainClippingRegion(&emptyRegion);
3656 						PushState();
3657 						ScrollBy(0, delta);
3658 						PopState();
3659 						ConstrainClippingRegion(NULL);
3660 
3661 						fTargetRowTop += delta;
3662 						fClickPoint.y += delta;
3663 						fLatchRect.OffsetBy(0, delta);
3664 					}
3665 				}
3666 			}
3667 		}
3668 
3669 		// If the parent was previously childless, it will need to have a latch drawn.
3670 		BRect parentRect;
3671 		if (ParentRow && ParentRow->fChildList->CountItems() == 1
3672 			&& FindVisibleRect(ParentRow, &parentRect))
3673 			Invalidate(parentRect);
3674 	}
3675 }
3676 
3677 
3678 void OutlineView::FixScrollBar(bool scrollToFit)
3679 {
3680 	BScrollBar *vScrollBar = ScrollBar(B_VERTICAL);
3681 	if (vScrollBar) {
3682 		if (fItemsHeight > fVisibleRect.Height()) {
3683 			float maxScrollBarValue = (fItemsHeight + kBottomMargin) - fVisibleRect.Height();
3684 			vScrollBar->SetProportion(fVisibleRect.Height() / (fItemsHeight + kBottomMargin));
3685 
3686 			// If the user is scrolled down too far when makes the range smaller, the list
3687 			// will jump suddenly, which is undesirable.  In this case, don't fix the scroll
3688 			// bar here. In ScrollTo, it checks to see if this has occured, and will
3689 			// fix the scroll bars sneakily if the user has scrolled up far enough.
3690 			if (scrollToFit || vScrollBar->Value() <= maxScrollBarValue) {
3691 				vScrollBar->SetRange(0.0, maxScrollBarValue);
3692 				vScrollBar->SetSteps(20.0, fVisibleRect.Height());
3693 			}
3694 		} else if (vScrollBar->Value() == 0.0)
3695 			vScrollBar->SetRange(0.0, 0.0);		// disable scroll bar.
3696 	}
3697 }
3698 
3699 void OutlineView::AddSorted(BRowContainer *list, BRow *row)
3700 {
3701 	if (list && row) {
3702 		// Find general vicinity with binary search.
3703 		int32 lower = 0;
3704 		int32 upper = list->CountItems()-1;
3705 		while( lower < upper ) {
3706 			int32 middle = lower + (upper-lower+1)/2;
3707 			int32 cmp = CompareRows(row, list->ItemAt(middle));
3708 			if( cmp < 0 ) upper = middle-1;
3709 			else if( cmp > 0 ) lower = middle+1;
3710 			else lower = upper = middle;
3711 		}
3712 
3713 		// At this point, 'upper' and 'lower' at the last found item.
3714 		// Arbitrarily use 'upper' and determine the final insertion
3715 		// point -- either before or after this item.
3716 		if( upper < 0 ) upper = 0;
3717 		else if( upper < list->CountItems() ) {
3718 			if( CompareRows(row, list->ItemAt(upper)) > 0 ) upper++;
3719 		}
3720 
3721 		if (upper >= list->CountItems())
3722 			list->AddItem(row);				// Adding to end.
3723 		else
3724 			list->AddItem(row, upper);		// Insert
3725 	}
3726 }
3727 
3728 int32 OutlineView::CompareRows(BRow *row1, BRow *row2)
3729 {
3730 	int32 itemCount (fSortColumns->CountItems());
3731 	if (row1 && row2) {
3732 		for (int32 index = 0; index < itemCount; index++) {
3733 			BColumn *column = (BColumn*) fSortColumns->ItemAt(index);
3734 			int comp = 0;
3735 			BField *field1 = (BField*) row1->GetField(column->fFieldID);
3736 			BField *field2 = (BField*) row2->GetField(column->fFieldID);
3737 			if (field1 && field2)
3738 				comp = column->CompareFields(field1, field2);
3739 
3740 			if (!column->fSortAscending)
3741 				comp = -comp;
3742 
3743 			if (comp != 0)
3744 				return comp;
3745 		}
3746 	}
3747 	return 0;
3748 }
3749 
3750 void OutlineView::FrameResized(float width, float height)
3751 {
3752 	fVisibleRect.right = fVisibleRect.left + width;
3753 	fVisibleRect.bottom = fVisibleRect.top + height;
3754 	FixScrollBar(true);
3755 	_inherited::FrameResized(width, height);
3756 }
3757 
3758 void OutlineView::ScrollTo(BPoint position)
3759 {
3760 	fVisibleRect.OffsetTo(position.x, position.y);
3761 
3762 	// In FixScrollBar, we might not have been able to change the size of
3763 	// the scroll bar because the user was scrolled down too far.  Take
3764 	// this opportunity to sneak it in if we can.
3765 	BScrollBar *vScrollBar = ScrollBar(B_VERTICAL);
3766 	float maxScrollBarValue = (fItemsHeight + kBottomMargin) - fVisibleRect.Height();
3767 	float min, max;
3768 	vScrollBar->GetRange(&min, &max);
3769 	if (max != maxScrollBarValue && position.y > maxScrollBarValue)
3770 		FixScrollBar(true);
3771 
3772 	_inherited::ScrollTo(position);
3773 }
3774 
3775 const BRect& OutlineView::VisibleRect() const
3776 {
3777 	return fVisibleRect;
3778 }
3779 
3780 
3781 bool OutlineView::FindVisibleRect(BRow *row, BRect *out_rect)
3782 {
3783 	if (row && out_rect) {
3784 		float line = 0.0;
3785 		for (RecursiveOutlineIterator iterator(&fRows); iterator.CurrentRow();
3786 			iterator.GoToNext()) {
3787 			if (line > fVisibleRect.bottom)
3788 				break;
3789 
3790 			if (iterator.CurrentRow() == row) {
3791 				out_rect->Set(fVisibleRect.left, line, fVisibleRect.right, line + row->Height());
3792 				return true;
3793 			}
3794 
3795 			line += iterator.CurrentRow()->Height() + 1;
3796 		}
3797 	}
3798 	return false;
3799 }
3800 
3801 bool OutlineView::FindRect(const BRow *row, BRect *out_rect)
3802 {
3803 	float line = 0.0;
3804 	for (RecursiveOutlineIterator iterator(&fRows); iterator.CurrentRow();
3805 		iterator.GoToNext()) {
3806 		if (iterator.CurrentRow() == row) {
3807 			out_rect->Set(fVisibleRect.left, line, fVisibleRect.right, line + row->Height());
3808 			return true;
3809 		}
3810 
3811 		line += iterator.CurrentRow()->Height() + 1;
3812 	}
3813 
3814 	return false;
3815 }
3816 
3817 
3818 void OutlineView::ScrollTo(const BRow* Row)
3819 {
3820 	BRect rect;
3821 	if( true == FindRect(Row, &rect) )
3822 	{
3823 		ScrollTo(BPoint(rect.left, rect.top));
3824 	}
3825 }
3826 
3827 
3828 void OutlineView::DeselectAll()
3829 {
3830 	// Invalidate all selected rows
3831 	float line = 0.0;
3832 	for (RecursiveOutlineIterator iterator(&fRows); iterator.CurrentRow();
3833 		iterator.GoToNext()) {
3834 		if (line > fVisibleRect.bottom)
3835 			break;
3836 
3837 		BRow *row = iterator.CurrentRow();
3838 		if (line + row->Height() > fVisibleRect.top) {
3839 			if (row->fNextSelected != 0)
3840 				Invalidate(BRect(fVisibleRect.left, line, fVisibleRect.right,
3841 					line + row->Height()));
3842 		}
3843 
3844 		line += row->Height() + 1;
3845 	}
3846 
3847 	// Set items not selected
3848 	while (fSelectionListDummyHead.fNextSelected != &fSelectionListDummyHead) {
3849 		BRow *row = fSelectionListDummyHead.fNextSelected;
3850 		row->fNextSelected->fPrevSelected = row->fPrevSelected;
3851 		row->fPrevSelected->fNextSelected = row->fNextSelected;
3852 		row->fNextSelected = 0;
3853 		row->fPrevSelected = 0;
3854 	}
3855 }
3856 
3857 BRow* OutlineView::FocusRow() const
3858 {
3859 	return fFocusRow;
3860 }
3861 
3862 void OutlineView::SetFocusRow(BRow* Row, bool Select)
3863 {
3864 	if (Row)
3865 	{
3866 		if(Select)
3867 			AddToSelection(Row);
3868 
3869 		if(fFocusRow == Row)
3870 			return;
3871 
3872 		Invalidate(fFocusRowRect); // invalidate previous
3873 
3874 		fTargetRow = fFocusRow = Row;
3875 
3876 		FindVisibleRect(fFocusRow, &fFocusRowRect);
3877 		Invalidate(fFocusRowRect); // invalidate current
3878 
3879 		fFocusRowRect.right = 10000;
3880 		fMasterView->SelectionChanged();
3881 	}
3882 }
3883 
3884 bool OutlineView::SortList(BRowContainer *list, bool isVisible)
3885 {
3886 	if (list) {
3887 		// Shellsort
3888 		BRow **items = (BRow**) list->AsBList()->Items();
3889 		int32 numItems = list->CountItems();
3890 		int h;
3891 		for (h = 1; h < numItems / 9; h = 3 * h + 1)
3892 			;
3893 
3894 		for (;h > 0; h /= 3) {
3895 			for (int step = h; step < numItems; step++) {
3896 				BRow *temp = items[step];
3897 				int i;
3898 				for (i = step - h; i >= 0; i -= h) {
3899 					if (CompareRows(temp, items[i]) < 0)
3900 						items[i + h] = items[i];
3901 					else
3902 						break;
3903 				}
3904 
3905 				items[i + h] = temp;
3906 			}
3907 		}
3908 
3909 		if (isVisible) {
3910 			Invalidate();
3911 
3912 			InvalidateCachedPositions();
3913 			int lockCount = Window()->CountLocks();
3914 			for (int i = 0; i < lockCount; i++)
3915 				Window()->Unlock();
3916 
3917 			while (lockCount--)
3918 				if (!Window()->Lock())
3919 					return false;	// Window is gone...
3920 		}
3921 	}
3922 	return true;
3923 }
3924 
3925 int32 OutlineView::DeepSortThreadEntry(void *_outlineView)
3926 {
3927 	((OutlineView*) _outlineView)->DeepSort();
3928 	return 0;
3929 }
3930 
3931 void OutlineView::DeepSort()
3932 {
3933 	struct stack_entry {
3934 		bool isVisible;
3935 		BRowContainer *list;
3936 		int32 listIndex;
3937 	} stack[kMaxDepth];
3938 	int32 stackTop = 0;
3939 
3940 	stack[stackTop].list = &fRows;
3941 	stack[stackTop].isVisible = true;
3942 	stack[stackTop].listIndex = 0;
3943 	fNumSorted = 0;
3944 
3945 	if (Window()->Lock() == false)
3946 		return;
3947 
3948 	bool doneSorting = false;
3949 	while (!doneSorting && !fSortCancelled) {
3950 
3951 		stack_entry *currentEntry = &stack[stackTop];
3952 
3953 		// xxx Can make the invalidate area smaller by finding the rect for the
3954 		// parent item and using that as the top of the invalid rect.
3955 
3956 		bool haveLock = SortList(currentEntry->list, currentEntry->isVisible);
3957 		if (!haveLock)
3958 			return ;	// window is gone.
3959 
3960 		// Fix focus rect.
3961 		InvalidateCachedPositions();
3962 		if (fCurrentState != INACTIVE)
3963 			fCurrentState = INACTIVE;	// sorry...
3964 
3965 		// next list.
3966 		bool foundNextList = false;
3967 		while (!foundNextList && !fSortCancelled) {
3968 			for (int32 index = currentEntry->listIndex; index < currentEntry->list->CountItems();
3969 				index++) {
3970 				BRow *parentRow = currentEntry->list->ItemAt(index);
3971 				BRowContainer *childList = parentRow->fChildList;
3972 				if (childList != 0) {
3973 					currentEntry->listIndex = index + 1;
3974 					stackTop++;
3975 					ASSERT(stackTop < kMaxDepth);
3976 					stack[stackTop].listIndex = 0;
3977 					stack[stackTop].list = childList;
3978 					stack[stackTop].isVisible = (currentEntry->isVisible && parentRow->fIsExpanded);
3979 					foundNextList = true;
3980 					break;
3981 				}
3982 			}
3983 
3984 			if (!foundNextList) {
3985 				// back up
3986 				if (--stackTop < 0) {
3987 					doneSorting = true;
3988 					break;
3989 				}
3990 
3991 				currentEntry = &stack[stackTop];
3992 			}
3993 		}
3994 	}
3995 
3996 	Window()->Unlock();
3997 }
3998 
3999 void OutlineView::StartSorting()
4000 {
4001 	// If this view is not yet attached to a window, don't start a sort thread!
4002 	if (Window() == NULL)
4003 		return;
4004 
4005 	if (fSortThread != B_BAD_THREAD_ID) {
4006 		thread_info tinfo;
4007 		if (get_thread_info(fSortThread, &tinfo) == B_OK) {
4008 			// Unlock window so this won't deadlock (sort thread is probably
4009 			// waiting to lock window).
4010 
4011 			int lockCount = Window()->CountLocks();
4012 			for (int i = 0; i < lockCount; i++)
4013 				Window()->Unlock();
4014 
4015 			fSortCancelled = true;
4016 			int32 status;
4017 			wait_for_thread(fSortThread, &status);
4018 
4019 			while (lockCount--)
4020 				if (!Window()->Lock())
4021 					return ;	// Window is gone...
4022 		}
4023 	}
4024 
4025 	fSortCancelled = false;
4026 	fSortThread = spawn_thread(DeepSortThreadEntry, "sort_thread", B_NORMAL_PRIORITY, this);
4027 	resume_thread(fSortThread);
4028 }
4029 
4030 void OutlineView::SelectRange(BRow *start, BRow *end)
4031 {
4032 	if (!start || !end)
4033 		return;
4034 
4035 	if (start == end)	// start is always selected when this is called
4036 		return;
4037 
4038 	RecursiveOutlineIterator iterator(&fRows, false);
4039 	while (iterator.CurrentRow() != 0) {
4040 		if (iterator.CurrentRow() == end) {
4041 			// reverse selection, swap to fix special case
4042 			BRow *temp = start;
4043 			start = end;
4044 			end = temp;
4045 			break;
4046 		} else if (iterator.CurrentRow() == start)
4047 			break;
4048 
4049 		iterator.GoToNext();
4050 	}
4051 
4052 	while (true) {
4053 		BRow *row = iterator.CurrentRow();
4054 		if(row) {
4055 			if (row->fNextSelected == 0) {
4056 				row->fNextSelected = fSelectionListDummyHead.fNextSelected;
4057 				row->fPrevSelected = &fSelectionListDummyHead;
4058 				row->fNextSelected->fPrevSelected = row;
4059 				row->fPrevSelected->fNextSelected = row;
4060 			}
4061 		} else
4062 			break;
4063 
4064 		if (row == end)
4065 			break;
4066 
4067 		iterator.GoToNext();
4068 	}
4069 
4070 	Invalidate();  // xxx make invalidation smaller
4071 }
4072 
4073 bool OutlineView::FindParent(BRow *row, BRow **outParent, bool *out_parentIsVisible)
4074 {
4075 	bool result = false;
4076 	if (row && outParent) {
4077 		*outParent = row->fParent;
4078 
4079 		// Walk up the parent chain to determine if this row is visible
4080 		bool isVisible = true;
4081 		for (BRow *currentRow = row->fParent; currentRow; currentRow = currentRow->fParent) {
4082 			if (!currentRow->fIsExpanded) {
4083 				isVisible = false;
4084 				break;
4085 			}
4086 		}
4087 
4088 		if (out_parentIsVisible) *out_parentIsVisible = isVisible;
4089 		result = (NULL != *outParent);
4090 	}
4091 
4092 	return result;
4093 }
4094 
4095 int32 OutlineView::IndexOf(BRow *row)
4096 {
4097 	if (row) {
4098 		if (row->fParent == 0)
4099 			return fRows.IndexOf(row);
4100 
4101 		ASSERT(row->fParent->fChildList);
4102 		return row->fParent->fChildList->IndexOf(row);
4103 	}
4104 
4105 	return B_ERROR;
4106 }
4107 
4108 void OutlineView::InvalidateCachedPositions()
4109 {
4110 	if (fFocusRow)
4111 		FindRect(fFocusRow, &fFocusRowRect);
4112 }
4113 
4114 // #pragma mark -
4115 
4116 
4117 RecursiveOutlineIterator::RecursiveOutlineIterator(BRowContainer *list, bool openBranchesOnly)
4118 	:	fStackIndex(0),
4119 		fCurrentListIndex(0),
4120 		fCurrentListDepth(0),
4121 		fOpenBranchesOnly(openBranchesOnly)
4122 {
4123 	if (list == 0 || list->CountItems() == 0)
4124 		fCurrentList = 0;
4125 	else
4126 		fCurrentList = list;
4127 }
4128 
4129 BRow* RecursiveOutlineIterator::CurrentRow() const
4130 {
4131 	if (fCurrentList == 0)
4132 		return 0;
4133 
4134 	return fCurrentList->ItemAt(fCurrentListIndex);
4135 }
4136 
4137 void RecursiveOutlineIterator::GoToNext()
4138 {
4139 	if (fCurrentList == 0)
4140 		return;
4141 	if (fCurrentListIndex < 0 || fCurrentListIndex >= fCurrentList->CountItems()) {
4142 		fCurrentList = 0;
4143 		return;
4144 	}
4145 
4146 	BRow *currentRow = fCurrentList->ItemAt(fCurrentListIndex);
4147 	if(currentRow) {
4148 		if (currentRow->fChildList && (currentRow->fIsExpanded || !fOpenBranchesOnly)
4149 			&& currentRow->fChildList->CountItems() > 0) {
4150 			// Visit child.
4151 			// Put current list on the stack if it needs to be revisited.
4152 			if (fCurrentListIndex < fCurrentList->CountItems() - 1) {
4153 				fStack[fStackIndex].fRowSet = fCurrentList;
4154 				fStack[fStackIndex].fIndex = fCurrentListIndex + 1;
4155 				fStack[fStackIndex].fDepth = fCurrentListDepth;
4156 				fStackIndex++;
4157 			}
4158 
4159 			fCurrentList = currentRow->fChildList;
4160 			fCurrentListIndex = 0;
4161 			fCurrentListDepth++;
4162 		} else if (fCurrentListIndex < fCurrentList->CountItems() - 1)
4163 			fCurrentListIndex++; // next item in current list
4164 		else if (--fStackIndex >= 0) {
4165 			fCurrentList = fStack[fStackIndex].fRowSet;
4166 			fCurrentListIndex = fStack[fStackIndex].fIndex;
4167 			fCurrentListDepth = fStack[fStackIndex].fDepth;
4168 		} else
4169 			fCurrentList = 0;
4170 	}
4171 }
4172 
4173 int32 RecursiveOutlineIterator::CurrentLevel() const
4174 {
4175 	return fCurrentListDepth;
4176 }
4177