xref: /haiku/src/kits/interface/ColumnListView.cpp (revision be3db2942c0e8dda63cdd226ec3c99309d3eab0c)
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(row->fNextSelected ?  B_COLOR_SELECTION_TEXT : 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 						SetHighColor(fMasterView->Color(B_COLOR_SELECTION_TEXT));
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().right, 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[row->fNextSelected ?  B_COLOR_SELECTION_TEXT : B_COLOR_TEXT]);
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(fColorList[B_COLOR_SELECTION_TEXT]);
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().right, 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(row->fNextSelected ?  B_COLOR_SELECTION_TEXT : 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(fMasterView->Color(B_COLOR_SELECTION_TEXT));
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(invalidBounds.left, line + rowHeight), BPoint(invalidBounds.right, 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 		FindVisibleRect(fFocusRow, &fFocusRowRect);
3051 		fFocusRow = 0;
3052 		Invalidate(fFocusRowRect);
3053 	}
3054 
3055 	// We stash the click counts here because the 'clicks' field
3056 	// is not in the CurrentMessage() when MouseUp is called... ;(
3057 	if (reset_click_count)
3058 		fClickCount = 1;
3059 	else
3060 		Window()->CurrentMessage()->FindInt32("clicks", &fClickCount);
3061 	fClickPoint = position;
3062 
3063 } // end of MouseDown()
3064 
3065 void OutlineView::MouseMoved(BPoint position, uint32 /*transit*/, const BMessage */*message*/)
3066 {
3067 	if(!fMouseDown) {
3068 		// Update fCurrentField
3069 		bool handle_field = false;
3070 		BField *new_field = 0;
3071 		BRow *new_row = 0;
3072 		BColumn *new_column = 0;
3073 		BRect new_rect(0,0,0,0);
3074 		if(position.y >=0 ) {
3075 			float top;
3076 			int32 indent;
3077 			BRow *row = FindRow(position.y, &indent, &top);
3078 			if(row && position.x >=0 ) {
3079 				float x=0;
3080 				for(int32 c=0;c<fMasterView->CountColumns();c++) {
3081 					new_column = fMasterView->ColumnAt(c);
3082 					if (!new_column->IsVisible())
3083 						continue;
3084 					if((MAX(kLeftMargin, fMasterView->LatchWidth())+x)+new_column->Width() > position.x) {
3085 						if(new_column->WantsEvents()) {
3086 							new_field = row->GetField(c);
3087 							new_row = row;
3088 							FindRect(new_row,&new_rect);
3089 							new_rect.left = MAX(kLeftMargin, fMasterView->LatchWidth()) + x;
3090 							new_rect.right = new_rect.left + new_column->Width() - 1;
3091 							handle_field = true;
3092 						}
3093 						break;
3094 					}
3095 					x += new_column->Width();
3096 				}
3097 			}
3098 		}
3099 
3100 		// Handle mouse moved
3101 		if(handle_field) {
3102 			if(new_field != fCurrentField) {
3103 				if(fCurrentField) {
3104 					fCurrentColumn->MouseMoved(fMasterView, fCurrentRow,
3105 						fCurrentField, fFieldRect, position, 0, fCurrentCode = B_EXITED_VIEW);
3106 				}
3107 				fCurrentColumn = new_column;
3108 				fCurrentRow = new_row;
3109 				fCurrentField = new_field;
3110 				fFieldRect = new_rect;
3111 				if(fCurrentField) {
3112 					fCurrentColumn->MouseMoved(fMasterView, fCurrentRow,
3113 						fCurrentField, fFieldRect, position, 0, fCurrentCode = B_ENTERED_VIEW);
3114 				}
3115 			} else {
3116 				if(fCurrentField) {
3117 					fCurrentColumn->MouseMoved(fMasterView, fCurrentRow,
3118 						fCurrentField, fFieldRect, position, 0, fCurrentCode = B_INSIDE_VIEW);
3119 				}
3120 			}
3121 		} else {
3122 			if(fCurrentField) {
3123 				fCurrentColumn->MouseMoved(fMasterView, fCurrentRow,
3124 						fCurrentField, fFieldRect, position, 0, fCurrentCode = B_EXITED_VIEW);
3125 				fCurrentField = 0;
3126 				fCurrentColumn = 0;
3127 				fCurrentRow = 0;
3128 			}
3129 		}
3130 	} else {
3131 		if(fCurrentField) {
3132 			if(fFieldRect.Contains(position)) {
3133 				if (   fCurrentCode == B_OUTSIDE_VIEW
3134 					|| fCurrentCode == B_EXITED_VIEW
3135 				) {
3136 					fCurrentColumn->MouseMoved(fMasterView, fCurrentRow,
3137 						fCurrentField, fFieldRect, position, 1, fCurrentCode = B_ENTERED_VIEW);
3138 				} else {
3139 					fCurrentColumn->MouseMoved(fMasterView, fCurrentRow,
3140 						fCurrentField, fFieldRect, position, 1, fCurrentCode = B_INSIDE_VIEW);
3141 				}
3142 			} else {
3143 				if (   fCurrentCode == B_INSIDE_VIEW
3144 					|| fCurrentCode == B_ENTERED_VIEW
3145 				) {
3146 					fCurrentColumn->MouseMoved(fMasterView, fCurrentRow,
3147 						fCurrentField, fFieldRect, position, 1, fCurrentCode = B_EXITED_VIEW);
3148 				} else {
3149 					fCurrentColumn->MouseMoved(fMasterView, fCurrentRow,
3150 						fCurrentField, fFieldRect, position, 1, fCurrentCode = B_OUTSIDE_VIEW);
3151 				}
3152 			}
3153 		}
3154 	}
3155 
3156 	if(!fEditMode) {
3157 
3158 		switch (fCurrentState) {
3159 			case LATCH_CLICKED:
3160 				if (fTargetRow->fNextSelected != 0) {
3161 					if(fEditMode)
3162 						SetHighColor(fMasterView->Color(B_COLOR_EDIT_BACKGROUND));
3163 					else
3164 						SetHighColor(fMasterView->Color(B_COLOR_SELECTION));
3165 				}
3166 				else
3167 					SetHighColor(fMasterView->Color(B_COLOR_BACKGROUND));
3168 
3169 				FillRect(fLatchRect);
3170 				if (fLatchRect.Contains(position))
3171 					fMasterView->DrawLatch(this, fLatchRect, B_PRESSED_LATCH, fTargetRow);
3172 				else
3173 					fMasterView->DrawLatch(this, fLatchRect, fTargetRow->fIsExpanded
3174 						? B_OPEN_LATCH : B_CLOSED_LATCH, fTargetRow);
3175 
3176 				break;
3177 
3178 
3179 			case ROW_CLICKED:
3180 				if (abs((int)(position.x - fClickPoint.x)) > kRowDragSensitivity
3181 					|| abs((int)(position.y - fClickPoint.y)) > kRowDragSensitivity) {
3182 					fCurrentState = DRAGGING_ROWS;
3183 					fMasterView->InitiateDrag(fClickPoint, fTargetRow->fNextSelected != 0);
3184 				}
3185 				break;
3186 
3187 			case DRAGGING_ROWS:
3188 #if 0
3189 				// falls through...
3190 #else
3191 				if (fTrackMouse /*&& message*/) {
3192 					if (fVisibleRect.Contains(position)) {
3193 						float top;
3194 						int32 indent;
3195 						BRow *target = FindRow(position.y, &indent, &top);
3196 						if(target)
3197 							SetFocusRow(target,true);
3198 					}
3199 				}
3200 				break;
3201 #endif
3202 
3203 			default: {
3204 
3205 				if (fTrackMouse /*&& message*/) {
3206 					// Draw a highlight line...
3207 					if (fVisibleRect.Contains(position)) {
3208 						float top;
3209 						int32 indent;
3210 						BRow *target = FindRow(position.y, &indent, &top);
3211 						if(target==fRollOverRow)
3212 							break;
3213 						if(fRollOverRow)
3214 						{
3215 							BRect rect;
3216 							FindRect(fRollOverRow, &rect);
3217 							Invalidate(rect);
3218 						}
3219 						fRollOverRow=target;
3220 #if 0
3221 						SetFocusRow(fRollOverRow,false);
3222 #else
3223 						PushState();
3224 						SetDrawingMode(B_OP_BLEND);
3225 						SetHighColor(255,255,255,255);
3226 						BRect rect;
3227 						FindRect(fRollOverRow, &rect);
3228 						rect.bottom -= 1.0;
3229 						FillRect(rect);
3230 						PopState();
3231 #endif
3232 					} else {
3233 						if(fRollOverRow)
3234 						{
3235 							BRect rect;
3236 							FindRect(fRollOverRow, &rect);
3237 							Invalidate(rect);
3238 							fRollOverRow = NULL;
3239 						}
3240 					}
3241 				}
3242 			}
3243 		}
3244 	}
3245 } // end of MouseMoved()
3246 
3247 void OutlineView::MouseUp(BPoint position)
3248 {
3249 	if(fCurrentField) {
3250 		fCurrentColumn->MouseUp(fMasterView,fCurrentRow,fCurrentField);
3251 		fMouseDown = false;
3252 	}
3253 
3254 	if(!fEditMode) {
3255 		switch (fCurrentState) {
3256 			case LATCH_CLICKED:
3257 				if (fLatchRect.Contains(position))
3258 					fMasterView->ExpandOrCollapse(fTargetRow, !fTargetRow->fIsExpanded);
3259 
3260 				Invalidate(fLatchRect);
3261 				fCurrentState = INACTIVE;
3262 				break;
3263 
3264 			case ROW_CLICKED:
3265 				if (fClickCount > 1
3266 					&& abs((int)fClickPoint.x - (int)position.x) < kDoubleClickMoveSensitivity
3267 					&& abs((int)fClickPoint.y - (int)position.y) < kDoubleClickMoveSensitivity) {
3268 					fMasterView->ItemInvoked();
3269 				}
3270 				fCurrentState = INACTIVE;
3271 				break;
3272 
3273 			case DRAGGING_ROWS:
3274 				fCurrentState = INACTIVE;
3275 				// Falls through
3276 
3277 			default:
3278 				if (fDropHighlightY != -1) {
3279 					InvertRect(BRect(0, fDropHighlightY - kDropHighlightLineHeight / 2, 1000000,
3280 						fDropHighlightY + kDropHighlightLineHeight / 2));	// Erase the old target line
3281 					fDropHighlightY = -1;
3282 				}
3283 		}
3284 	}
3285 } // end of MouseUp()
3286 
3287 void OutlineView::MessageReceived(BMessage *message)
3288 {
3289 	if (message->WasDropped()) {
3290 		fMasterView->MessageDropped(message, ConvertFromScreen(message->DropPoint()));
3291 	} else {
3292 		BView::MessageReceived(message);
3293 	}
3294 }
3295 
3296 void OutlineView::ChangeFocusRow(bool up, bool updateSelection, bool addToCurrentSelection)
3297 {
3298 	int32 indent;
3299 	float top;
3300 	float newRowPos = 0;
3301 	float verticalScroll = 0;
3302 
3303 	if (fFocusRow) {
3304 		// A row currently has the focus, get information about it
3305 		newRowPos = fFocusRowRect.top + (up ? -4 : fFocusRow->Height() + 4);
3306 		if (newRowPos < fVisibleRect.top + 20)
3307 			verticalScroll = newRowPos - 20;
3308 		else if (newRowPos > fVisibleRect.bottom - 20)
3309 			verticalScroll = newRowPos - fVisibleRect.Height() + 20;
3310 	} else
3311 		newRowPos = fVisibleRect.top + 2;
3312 			// no row is currently focused, set this to the top of the window
3313 			// so we will select the first visible item in the list.
3314 
3315 	BRow *newRow = FindRow(newRowPos, &indent, &top);
3316 	if (newRow) {
3317 		if (fFocusRow) {
3318 			fFocusRowRect.right = 10000;
3319 			Invalidate(fFocusRowRect);
3320 		}
3321 		fFocusRow = newRow;
3322 		fFocusRowRect.top = top;
3323 		fFocusRowRect.left = 0;
3324 		fFocusRowRect.right = 10000;
3325 		fFocusRowRect.bottom = fFocusRowRect.top + fFocusRow->Height();
3326 		Invalidate(fFocusRowRect);
3327 
3328 		if (updateSelection) {
3329 			if (!addToCurrentSelection || fSelectionMode == B_SINGLE_SELECTION_LIST)
3330 				DeselectAll();
3331 
3332 			if (fFocusRow->fNextSelected == 0) {
3333 				fFocusRow->fNextSelected = fSelectionListDummyHead.fNextSelected;
3334 				fFocusRow->fPrevSelected = &fSelectionListDummyHead;
3335 				fFocusRow->fNextSelected->fPrevSelected = fFocusRow;
3336 				fFocusRow->fPrevSelected->fNextSelected = fFocusRow;
3337 			}
3338 
3339 			fLastSelectedItem = fFocusRow;
3340 		}
3341 	} else
3342 		Invalidate(fFocusRowRect);
3343 
3344 	if (verticalScroll != 0) {
3345 		BScrollBar *vScrollBar = ScrollBar(B_VERTICAL);
3346 		float min, max;
3347 		vScrollBar->GetRange(&min, &max);
3348 		if (verticalScroll < min)
3349 			verticalScroll = min;
3350 		else if (verticalScroll > max)
3351 			verticalScroll = max;
3352 
3353 		vScrollBar->SetValue(verticalScroll);
3354 	}
3355 
3356 	if (newRow && updateSelection)
3357 		fMasterView->SelectionChanged();
3358 }
3359 
3360 void OutlineView::MoveFocusToVisibleRect()
3361 {
3362 	fFocusRow = 0;
3363 	ChangeFocusRow(true, true, false);
3364 }
3365 
3366 BRow* OutlineView::CurrentSelection(BRow *lastSelected) const
3367 {
3368 	BRow *row;
3369 	if (lastSelected == 0)
3370 		row = fSelectionListDummyHead.fNextSelected;
3371 	else
3372 		row = lastSelected->fNextSelected;
3373 
3374 
3375 	if (row == &fSelectionListDummyHead)
3376 		row = 0;
3377 
3378 	return row;
3379 }
3380 
3381 void OutlineView::ToggleFocusRowSelection(bool selectRange)
3382 {
3383 	if (fFocusRow == 0)
3384 		return;
3385 
3386 	if (selectRange && fSelectionMode == B_MULTIPLE_SELECTION_LIST)
3387 		SelectRange(fLastSelectedItem, fFocusRow);
3388 	else {
3389 		if (fFocusRow->fNextSelected != 0) {
3390 			// Unselect row
3391 			fFocusRow->fNextSelected->fPrevSelected = fFocusRow->fPrevSelected;
3392 			fFocusRow->fPrevSelected->fNextSelected = fFocusRow->fNextSelected;
3393 			fFocusRow->fPrevSelected = 0;
3394 			fFocusRow->fNextSelected = 0;
3395 		} else {
3396 			// Select row
3397 			if (fSelectionMode == B_SINGLE_SELECTION_LIST)
3398 				DeselectAll();
3399 
3400 			fFocusRow->fNextSelected = fSelectionListDummyHead.fNextSelected;
3401 			fFocusRow->fPrevSelected = &fSelectionListDummyHead;
3402 			fFocusRow->fNextSelected->fPrevSelected = fFocusRow;
3403 			fFocusRow->fPrevSelected->fNextSelected = fFocusRow;
3404 		}
3405 	}
3406 
3407 	fLastSelectedItem = fFocusRow;
3408 	fMasterView->SelectionChanged();
3409 	Invalidate(fFocusRowRect);
3410 }
3411 
3412 void OutlineView::ToggleFocusRowOpen()
3413 {
3414 	if (fFocusRow)
3415 		fMasterView->ExpandOrCollapse(fFocusRow, !fFocusRow->fIsExpanded);
3416 }
3417 
3418 
3419 // xxx Could use CopyBits here to speed things up.
3420 void OutlineView::ExpandOrCollapse(BRow* ParentRow, bool Expand)
3421 {
3422 	if (ParentRow) {
3423 
3424 		if (ParentRow->fIsExpanded == Expand)
3425 			return;
3426 
3427 		ParentRow->fIsExpanded = Expand;
3428 
3429 		BRect parentRect;
3430 		if (FindRect(ParentRow, &parentRect)) {
3431 			// Determine my new height
3432 			float subTreeHeight = 0.0;
3433 			if (ParentRow->fIsExpanded)
3434 				for (RecursiveOutlineIterator iterator(ParentRow->fChildList);
3435 				     iterator.CurrentRow();
3436 				     iterator.GoToNext()
3437 				    )
3438 				{
3439 					subTreeHeight += iterator.CurrentRow()->Height()+1;
3440 				}
3441 			else
3442 				for (RecursiveOutlineIterator iterator(ParentRow->fChildList);
3443 				     iterator.CurrentRow();
3444 				     iterator.GoToNext()
3445 				    )
3446 				{
3447 					subTreeHeight -= iterator.CurrentRow()->Height()+1;
3448 				}
3449 			fItemsHeight += subTreeHeight;
3450 
3451 			// Adjust focus row if necessary.
3452 			if (FindRect(fFocusRow, &fFocusRowRect) == false) {
3453 				// focus row is in a subtree that has collapsed, move it up to the parent.
3454 				fFocusRow = ParentRow;
3455 				FindRect(fFocusRow, &fFocusRowRect);
3456 			}
3457 
3458 			Invalidate(BRect(0, parentRect.top, fVisibleRect.right, fVisibleRect.bottom));
3459 			FixScrollBar(false);
3460 		}
3461 	}
3462 
3463 }
3464 
3465 void OutlineView::RemoveRow(BRow *row)
3466 {
3467 	if (row) {
3468 		BRow *parentRow;
3469 		bool parentIsVisible;
3470 		float subTreeHeight = row->Height();
3471 		if (FindParent(row, &parentRow, &parentIsVisible)) {
3472 			// adjust height
3473 			if (parentIsVisible && (parentRow == 0 || parentRow->fIsExpanded)) {
3474 				if (row->fIsExpanded) {
3475 					for (RecursiveOutlineIterator iterator(row->fChildList);
3476 						iterator.CurrentRow(); iterator.GoToNext())
3477 						subTreeHeight += iterator.CurrentRow()->Height();
3478 				}
3479 			}
3480 		}
3481 		if (parentRow) {
3482 			if (parentRow->fIsExpanded)
3483 				fItemsHeight -= subTreeHeight + 1;
3484 		}
3485 		else {
3486 			fItemsHeight -= subTreeHeight + 1;
3487 		}
3488 		FixScrollBar(false);
3489 		if (parentRow)
3490 			parentRow->fChildList->RemoveItem(row);
3491 		else
3492 			fRows.RemoveItem(row);
3493 
3494 		if (parentRow != 0 && parentRow->fChildList->CountItems() == 0) {
3495 			delete parentRow->fChildList;
3496 			parentRow->fChildList = 0;
3497 			if (parentIsVisible)
3498 				Invalidate();	// xxx crude way of redrawing latch
3499 		}
3500 
3501 		if (parentIsVisible && (parentRow == 0 || parentRow->fIsExpanded))
3502 			Invalidate();	// xxx make me smarter.
3503 
3504 
3505 		// Adjust focus row if necessary.
3506 		if (fFocusRow && FindRect(fFocusRow, &fFocusRowRect) == false) {
3507 			// focus row is in a subtree that is gone, move it up to the parent.
3508 			fFocusRow = parentRow;
3509 			if (fFocusRow)
3510 				FindRect(fFocusRow, &fFocusRowRect);
3511 		}
3512 
3513 		// Remove this from the selection if necessary
3514 		if (row->fNextSelected != 0) {
3515 			row->fNextSelected->fPrevSelected = row->fPrevSelected;
3516 			row->fPrevSelected->fNextSelected = row->fNextSelected;
3517 			row->fPrevSelected = 0;
3518 			row->fNextSelected = 0;
3519 			fMasterView->SelectionChanged();
3520 		}
3521 
3522 		fCurrentColumn = 0;
3523 		fCurrentRow = 0;
3524 		fCurrentField = 0;
3525 	}
3526 }
3527 
3528 BRowContainer* OutlineView::RowList()
3529 {
3530 	return &fRows;
3531 }
3532 
3533 void OutlineView::UpdateRow(BRow *row)
3534 {
3535 	if (row) {
3536 		// Determine if this row has changed its sort order
3537 		BRow *parentRow = NULL;
3538 		bool parentIsVisible = false;
3539 		FindParent(row, &parentRow, &parentIsVisible);
3540 
3541 		BRowContainer* list = (parentRow == NULL) ? &fRows : parentRow->fChildList;
3542 
3543 		if(list) {
3544 			int32 rowIndex = list->IndexOf(row);
3545 			ASSERT(rowIndex >= 0);
3546 			ASSERT(list->ItemAt(rowIndex) == row);
3547 
3548 			bool rowMoved = false;
3549 			if (rowIndex > 0 && CompareRows(list->ItemAt(rowIndex - 1), row) > 0)
3550 				rowMoved = true;
3551 
3552 			if (rowIndex < list->CountItems() - 1 && CompareRows(list->ItemAt(rowIndex + 1),
3553 				row) < 0)
3554 				rowMoved = true;
3555 
3556 			if (rowMoved) {
3557 				// Sort location of this row has changed.
3558 				// Remove and re-add in the right spot
3559 				SortList(list, parentIsVisible && (parentRow == NULL || parentRow->fIsExpanded));
3560 			} else if (parentIsVisible && (parentRow == NULL || parentRow->fIsExpanded)) {
3561 				BRect invalidRect;
3562 				if (FindVisibleRect(row, &invalidRect))
3563 					Invalidate(invalidRect);
3564 			}
3565 		}
3566 	}
3567 }
3568 
3569 void OutlineView::AddRow(BRow* Row, int32 Index, BRow* ParentRow)
3570 {
3571 	if(Row) {
3572 		Row->fParent = ParentRow;
3573 
3574 		if (fMasterView->SortingEnabled()) {
3575 			// Ignore index here.
3576 			if (ParentRow) {
3577 				if (ParentRow->fChildList == 0)
3578 					ParentRow->fChildList = new BRowContainer;
3579 
3580 				AddSorted(ParentRow->fChildList, Row);
3581 			} else
3582 				AddSorted(&fRows, Row);
3583 		} else {
3584 			// Note, a -1 index implies add to end if sorting is not enabled
3585 			if (ParentRow) {
3586 				if (ParentRow->fChildList == 0)
3587 					ParentRow->fChildList = new BRowContainer;
3588 
3589 				if (Index < 0 || Index > ParentRow->fChildList->CountItems())
3590 					ParentRow->fChildList->AddItem(Row);
3591 				else
3592 					ParentRow->fChildList->AddItem(Row, Index);
3593 			} else {
3594 				if (Index < 0 || Index >= fRows.CountItems())
3595 					fRows.AddItem(Row);
3596 				else
3597 					fRows.AddItem(Row, Index);
3598 			}
3599 		}
3600 
3601 		if (ParentRow == 0 || ParentRow->fIsExpanded)
3602 			fItemsHeight += Row->Height() + 1;
3603 
3604 		FixScrollBar(false);
3605 
3606 		BRect newRowRect;
3607 		bool newRowIsInOpenBranch = FindRect(Row, &newRowRect);
3608 
3609 		if (fFocusRow && fFocusRowRect.top > newRowRect.bottom) {
3610 			// The focus row has moved.
3611 			Invalidate(fFocusRowRect);
3612 			FindRect(fFocusRow, &fFocusRowRect);
3613 			Invalidate(fFocusRowRect);
3614 		}
3615 
3616 		if (newRowIsInOpenBranch) {
3617 			if (fCurrentState == INACTIVE) {
3618 				if (newRowRect.bottom < fVisibleRect.top) {
3619 					// The new row is totally above the current viewport, move
3620 					// everything down and redraw the first line.
3621 					BRect source(fVisibleRect);
3622 					BRect dest(fVisibleRect);
3623 					source.bottom -= Row->Height() + 1;
3624 					dest.top += Row->Height() + 1;
3625 					CopyBits(source, dest);
3626 					Invalidate(BRect(fVisibleRect.left, fVisibleRect.top, fVisibleRect.right,
3627 						fVisibleRect.top + newRowRect.Height()));
3628 				} else if (newRowRect.top < fVisibleRect.bottom) {
3629 					// New item is somewhere in the current region.  Scroll everything
3630 					// beneath it down and invalidate just the new row rect.
3631 					BRect source(fVisibleRect.left, newRowRect.top, fVisibleRect.right,
3632 						fVisibleRect.bottom - newRowRect.Height());
3633 					BRect dest(source);
3634 					dest.OffsetBy(0, newRowRect.Height() + 1);
3635 					CopyBits(source, dest);
3636 					Invalidate(newRowRect);
3637 				} // otherwise, this is below the currently visible region
3638 			} else {
3639 				// Adding the item may have caused the item that the user is currently
3640 				// selected to move.  This would cause annoying drawing and interaction
3641 				// bugs, as the position of that item is cached.  If this happens, resize
3642 				// the scroll bar, then scroll back so the selected item is in view.
3643 				BRect targetRect;
3644 				if (FindRect(fTargetRow, &targetRect)) {
3645 					float delta = targetRect.top - fTargetRowTop;
3646 					if (delta != 0) {
3647 						// This causes a jump because ScrollBy will copy a chunk of the view.
3648 						// Since the actual contents of the view have been offset, we don't
3649 						// want this, we just want to change the virtual origin of the window.
3650 						// Constrain the clipping region so everything is clipped out so no
3651 						// copy occurs.
3652 						//
3653 						//	xxx this currently doesn't work if the scroll bars aren't enabled.
3654 						//  everything will still move anyway.  A minor annoyance.
3655 						BRegion emptyRegion;
3656 						ConstrainClippingRegion(&emptyRegion);
3657 						PushState();
3658 						ScrollBy(0, delta);
3659 						PopState();
3660 						ConstrainClippingRegion(NULL);
3661 
3662 						fTargetRowTop += delta;
3663 						fClickPoint.y += delta;
3664 						fLatchRect.OffsetBy(0, delta);
3665 					}
3666 				}
3667 			}
3668 		}
3669 
3670 		// If the parent was previously childless, it will need to have a latch drawn.
3671 		BRect parentRect;
3672 		if (ParentRow && ParentRow->fChildList->CountItems() == 1
3673 			&& FindVisibleRect(ParentRow, &parentRect))
3674 			Invalidate(parentRect);
3675 	}
3676 }
3677 
3678 
3679 void OutlineView::FixScrollBar(bool scrollToFit)
3680 {
3681 	BScrollBar *vScrollBar = ScrollBar(B_VERTICAL);
3682 	if (vScrollBar) {
3683 		if (fItemsHeight > fVisibleRect.Height()) {
3684 			float maxScrollBarValue = (fItemsHeight + kBottomMargin) - fVisibleRect.Height();
3685 			vScrollBar->SetProportion(fVisibleRect.Height() / (fItemsHeight + kBottomMargin));
3686 
3687 			// If the user is scrolled down too far when makes the range smaller, the list
3688 			// will jump suddenly, which is undesirable.  In this case, don't fix the scroll
3689 			// bar here. In ScrollTo, it checks to see if this has occured, and will
3690 			// fix the scroll bars sneakily if the user has scrolled up far enough.
3691 			if (scrollToFit || vScrollBar->Value() <= maxScrollBarValue) {
3692 				vScrollBar->SetRange(0.0, maxScrollBarValue);
3693 				vScrollBar->SetSteps(20.0, fVisibleRect.Height());
3694 			}
3695 		} else if (vScrollBar->Value() == 0.0)
3696 			vScrollBar->SetRange(0.0, 0.0);		// disable scroll bar.
3697 	}
3698 }
3699 
3700 void OutlineView::AddSorted(BRowContainer *list, BRow *row)
3701 {
3702 	if (list && row) {
3703 		// Find general vicinity with binary search.
3704 		int32 lower = 0;
3705 		int32 upper = list->CountItems()-1;
3706 		while( lower < upper ) {
3707 			int32 middle = lower + (upper-lower+1)/2;
3708 			int32 cmp = CompareRows(row, list->ItemAt(middle));
3709 			if( cmp < 0 ) upper = middle-1;
3710 			else if( cmp > 0 ) lower = middle+1;
3711 			else lower = upper = middle;
3712 		}
3713 
3714 		// At this point, 'upper' and 'lower' at the last found item.
3715 		// Arbitrarily use 'upper' and determine the final insertion
3716 		// point -- either before or after this item.
3717 		if( upper < 0 ) upper = 0;
3718 		else if( upper < list->CountItems() ) {
3719 			if( CompareRows(row, list->ItemAt(upper)) > 0 ) upper++;
3720 		}
3721 
3722 		if (upper >= list->CountItems())
3723 			list->AddItem(row);				// Adding to end.
3724 		else
3725 			list->AddItem(row, upper);		// Insert
3726 	}
3727 }
3728 
3729 int32 OutlineView::CompareRows(BRow *row1, BRow *row2)
3730 {
3731 	int32 itemCount (fSortColumns->CountItems());
3732 	if (row1 && row2) {
3733 		for (int32 index = 0; index < itemCount; index++) {
3734 			BColumn *column = (BColumn*) fSortColumns->ItemAt(index);
3735 			int comp = 0;
3736 			BField *field1 = (BField*) row1->GetField(column->fFieldID);
3737 			BField *field2 = (BField*) row2->GetField(column->fFieldID);
3738 			if (field1 && field2)
3739 				comp = column->CompareFields(field1, field2);
3740 
3741 			if (!column->fSortAscending)
3742 				comp = -comp;
3743 
3744 			if (comp != 0)
3745 				return comp;
3746 		}
3747 	}
3748 	return 0;
3749 }
3750 
3751 void OutlineView::FrameResized(float width, float height)
3752 {
3753 	fVisibleRect.right = fVisibleRect.left + width;
3754 	fVisibleRect.bottom = fVisibleRect.top + height;
3755 	FixScrollBar(true);
3756 	_inherited::FrameResized(width, height);
3757 }
3758 
3759 void OutlineView::ScrollTo(BPoint position)
3760 {
3761 	fVisibleRect.OffsetTo(position.x, position.y);
3762 
3763 	// In FixScrollBar, we might not have been able to change the size of
3764 	// the scroll bar because the user was scrolled down too far.  Take
3765 	// this opportunity to sneak it in if we can.
3766 	BScrollBar *vScrollBar = ScrollBar(B_VERTICAL);
3767 	float maxScrollBarValue = (fItemsHeight + kBottomMargin) - fVisibleRect.Height();
3768 	float min, max;
3769 	vScrollBar->GetRange(&min, &max);
3770 	if (max != maxScrollBarValue && position.y > maxScrollBarValue)
3771 		FixScrollBar(true);
3772 
3773 	_inherited::ScrollTo(position);
3774 }
3775 
3776 const BRect& OutlineView::VisibleRect() const
3777 {
3778 	return fVisibleRect;
3779 }
3780 
3781 
3782 bool OutlineView::FindVisibleRect(BRow *row, BRect *out_rect)
3783 {
3784 	if (row && out_rect) {
3785 		float line = 0.0;
3786 		for (RecursiveOutlineIterator iterator(&fRows); iterator.CurrentRow();
3787 			iterator.GoToNext()) {
3788 			if (line > fVisibleRect.bottom)
3789 				break;
3790 
3791 			if (iterator.CurrentRow() == row) {
3792 				out_rect->Set(fVisibleRect.left, line, fVisibleRect.right, line + row->Height());
3793 				return true;
3794 			}
3795 
3796 			line += iterator.CurrentRow()->Height() + 1;
3797 		}
3798 	}
3799 	return false;
3800 }
3801 
3802 bool OutlineView::FindRect(const BRow *row, BRect *out_rect)
3803 {
3804 	float line = 0.0;
3805 	for (RecursiveOutlineIterator iterator(&fRows); iterator.CurrentRow();
3806 		iterator.GoToNext()) {
3807 		if (iterator.CurrentRow() == row) {
3808 			out_rect->Set(fVisibleRect.left, line, fVisibleRect.right, line + row->Height());
3809 			return true;
3810 		}
3811 
3812 		line += iterator.CurrentRow()->Height() + 1;
3813 	}
3814 
3815 	return false;
3816 }
3817 
3818 
3819 void OutlineView::ScrollTo(const BRow* Row)
3820 {
3821 	BRect rect;
3822 	if( true == FindRect(Row, &rect) )
3823 	{
3824 		ScrollTo(BPoint(rect.left, rect.top));
3825 	}
3826 }
3827 
3828 
3829 void OutlineView::DeselectAll()
3830 {
3831 	// Invalidate all selected rows
3832 	float line = 0.0;
3833 	for (RecursiveOutlineIterator iterator(&fRows); iterator.CurrentRow();
3834 		iterator.GoToNext()) {
3835 		if (line > fVisibleRect.bottom)
3836 			break;
3837 
3838 		BRow *row = iterator.CurrentRow();
3839 		if (line + row->Height() > fVisibleRect.top) {
3840 			if (row->fNextSelected != 0)
3841 				Invalidate(BRect(fVisibleRect.left, line, fVisibleRect.right,
3842 					line + row->Height()));
3843 		}
3844 
3845 		line += row->Height() + 1;
3846 	}
3847 
3848 	// Set items not selected
3849 	while (fSelectionListDummyHead.fNextSelected != &fSelectionListDummyHead) {
3850 		BRow *row = fSelectionListDummyHead.fNextSelected;
3851 		row->fNextSelected->fPrevSelected = row->fPrevSelected;
3852 		row->fPrevSelected->fNextSelected = row->fNextSelected;
3853 		row->fNextSelected = 0;
3854 		row->fPrevSelected = 0;
3855 	}
3856 }
3857 
3858 BRow* OutlineView::FocusRow() const
3859 {
3860 	return fFocusRow;
3861 }
3862 
3863 void OutlineView::SetFocusRow(BRow* Row, bool Select)
3864 {
3865 	if (Row)
3866 	{
3867 		if(Select)
3868 			AddToSelection(Row);
3869 
3870 		if(fFocusRow == Row)
3871 			return;
3872 
3873 		Invalidate(fFocusRowRect); // invalidate previous
3874 
3875 		fTargetRow = fFocusRow = Row;
3876 
3877 		FindVisibleRect(fFocusRow, &fFocusRowRect);
3878 		Invalidate(fFocusRowRect); // invalidate current
3879 
3880 		fFocusRowRect.right = 10000;
3881 		fMasterView->SelectionChanged();
3882 	}
3883 }
3884 
3885 bool OutlineView::SortList(BRowContainer *list, bool isVisible)
3886 {
3887 	if (list) {
3888 		// Shellsort
3889 		BRow **items = (BRow**) list->AsBList()->Items();
3890 		int32 numItems = list->CountItems();
3891 		int h;
3892 		for (h = 1; h < numItems / 9; h = 3 * h + 1)
3893 			;
3894 
3895 		for (;h > 0; h /= 3) {
3896 			for (int step = h; step < numItems; step++) {
3897 				BRow *temp = items[step];
3898 				int i;
3899 				for (i = step - h; i >= 0; i -= h) {
3900 					if (CompareRows(temp, items[i]) < 0)
3901 						items[i + h] = items[i];
3902 					else
3903 						break;
3904 				}
3905 
3906 				items[i + h] = temp;
3907 			}
3908 		}
3909 
3910 		if (isVisible) {
3911 			Invalidate();
3912 
3913 			InvalidateCachedPositions();
3914 			int lockCount = Window()->CountLocks();
3915 			for (int i = 0; i < lockCount; i++)
3916 				Window()->Unlock();
3917 
3918 			while (lockCount--)
3919 				if (!Window()->Lock())
3920 					return false;	// Window is gone...
3921 		}
3922 	}
3923 	return true;
3924 }
3925 
3926 int32 OutlineView::DeepSortThreadEntry(void *_outlineView)
3927 {
3928 	((OutlineView*) _outlineView)->DeepSort();
3929 	return 0;
3930 }
3931 
3932 void OutlineView::DeepSort()
3933 {
3934 	struct stack_entry {
3935 		bool isVisible;
3936 		BRowContainer *list;
3937 		int32 listIndex;
3938 	} stack[kMaxDepth];
3939 	int32 stackTop = 0;
3940 
3941 	stack[stackTop].list = &fRows;
3942 	stack[stackTop].isVisible = true;
3943 	stack[stackTop].listIndex = 0;
3944 	fNumSorted = 0;
3945 
3946 	if (Window()->Lock() == false)
3947 		return;
3948 
3949 	bool doneSorting = false;
3950 	while (!doneSorting && !fSortCancelled) {
3951 
3952 		stack_entry *currentEntry = &stack[stackTop];
3953 
3954 		// xxx Can make the invalidate area smaller by finding the rect for the
3955 		// parent item and using that as the top of the invalid rect.
3956 
3957 		bool haveLock = SortList(currentEntry->list, currentEntry->isVisible);
3958 		if (!haveLock)
3959 			return ;	// window is gone.
3960 
3961 		// Fix focus rect.
3962 		InvalidateCachedPositions();
3963 		if (fCurrentState != INACTIVE)
3964 			fCurrentState = INACTIVE;	// sorry...
3965 
3966 		// next list.
3967 		bool foundNextList = false;
3968 		while (!foundNextList && !fSortCancelled) {
3969 			for (int32 index = currentEntry->listIndex; index < currentEntry->list->CountItems();
3970 				index++) {
3971 				BRow *parentRow = currentEntry->list->ItemAt(index);
3972 				BRowContainer *childList = parentRow->fChildList;
3973 				if (childList != 0) {
3974 					currentEntry->listIndex = index + 1;
3975 					stackTop++;
3976 					ASSERT(stackTop < kMaxDepth);
3977 					stack[stackTop].listIndex = 0;
3978 					stack[stackTop].list = childList;
3979 					stack[stackTop].isVisible = (currentEntry->isVisible && parentRow->fIsExpanded);
3980 					foundNextList = true;
3981 					break;
3982 				}
3983 			}
3984 
3985 			if (!foundNextList) {
3986 				// back up
3987 				if (--stackTop < 0) {
3988 					doneSorting = true;
3989 					break;
3990 				}
3991 
3992 				currentEntry = &stack[stackTop];
3993 			}
3994 		}
3995 	}
3996 
3997 	Window()->Unlock();
3998 }
3999 
4000 void OutlineView::StartSorting()
4001 {
4002 	// If this view is not yet attached to a window, don't start a sort thread!
4003 	if (Window() == NULL)
4004 		return;
4005 
4006 	if (fSortThread != B_BAD_THREAD_ID) {
4007 		thread_info tinfo;
4008 		if (get_thread_info(fSortThread, &tinfo) == B_OK) {
4009 			// Unlock window so this won't deadlock (sort thread is probably
4010 			// waiting to lock window).
4011 
4012 			int lockCount = Window()->CountLocks();
4013 			for (int i = 0; i < lockCount; i++)
4014 				Window()->Unlock();
4015 
4016 			fSortCancelled = true;
4017 			int32 status;
4018 			wait_for_thread(fSortThread, &status);
4019 
4020 			while (lockCount--)
4021 				if (!Window()->Lock())
4022 					return ;	// Window is gone...
4023 		}
4024 	}
4025 
4026 	fSortCancelled = false;
4027 	fSortThread = spawn_thread(DeepSortThreadEntry, "sort_thread", B_NORMAL_PRIORITY, this);
4028 	resume_thread(fSortThread);
4029 }
4030 
4031 void OutlineView::SelectRange(BRow *start, BRow *end)
4032 {
4033 	if (!start || !end)
4034 		return;
4035 
4036 	if (start == end)	// start is always selected when this is called
4037 		return;
4038 
4039 	RecursiveOutlineIterator iterator(&fRows, false);
4040 	while (iterator.CurrentRow() != 0) {
4041 		if (iterator.CurrentRow() == end) {
4042 			// reverse selection, swap to fix special case
4043 			BRow *temp = start;
4044 			start = end;
4045 			end = temp;
4046 			break;
4047 		} else if (iterator.CurrentRow() == start)
4048 			break;
4049 
4050 		iterator.GoToNext();
4051 	}
4052 
4053 	while (true) {
4054 		BRow *row = iterator.CurrentRow();
4055 		if(row) {
4056 			if (row->fNextSelected == 0) {
4057 				row->fNextSelected = fSelectionListDummyHead.fNextSelected;
4058 				row->fPrevSelected = &fSelectionListDummyHead;
4059 				row->fNextSelected->fPrevSelected = row;
4060 				row->fPrevSelected->fNextSelected = row;
4061 			}
4062 		} else
4063 			break;
4064 
4065 		if (row == end)
4066 			break;
4067 
4068 		iterator.GoToNext();
4069 	}
4070 
4071 	Invalidate();  // xxx make invalidation smaller
4072 }
4073 
4074 bool OutlineView::FindParent(BRow *row, BRow **outParent, bool *out_parentIsVisible)
4075 {
4076 	bool result = false;
4077 	if (row && outParent) {
4078 		*outParent = row->fParent;
4079 
4080 		// Walk up the parent chain to determine if this row is visible
4081 		bool isVisible = true;
4082 		for (BRow *currentRow = row->fParent; currentRow; currentRow = currentRow->fParent) {
4083 			if (!currentRow->fIsExpanded) {
4084 				isVisible = false;
4085 				break;
4086 			}
4087 		}
4088 
4089 		if (out_parentIsVisible) *out_parentIsVisible = isVisible;
4090 		result = (NULL != *outParent);
4091 	}
4092 
4093 	return result;
4094 }
4095 
4096 int32 OutlineView::IndexOf(BRow *row)
4097 {
4098 	if (row) {
4099 		if (row->fParent == 0)
4100 			return fRows.IndexOf(row);
4101 
4102 		ASSERT(row->fParent->fChildList);
4103 		return row->fParent->fChildList->IndexOf(row);
4104 	}
4105 
4106 	return B_ERROR;
4107 }
4108 
4109 void OutlineView::InvalidateCachedPositions()
4110 {
4111 	if (fFocusRow)
4112 		FindRect(fFocusRow, &fFocusRowRect);
4113 }
4114 
4115 // #pragma mark -
4116 
4117 
4118 RecursiveOutlineIterator::RecursiveOutlineIterator(BRowContainer *list, bool openBranchesOnly)
4119 	:	fStackIndex(0),
4120 		fCurrentListIndex(0),
4121 		fCurrentListDepth(0),
4122 		fOpenBranchesOnly(openBranchesOnly)
4123 {
4124 	if (list == 0 || list->CountItems() == 0)
4125 		fCurrentList = 0;
4126 	else
4127 		fCurrentList = list;
4128 }
4129 
4130 BRow* RecursiveOutlineIterator::CurrentRow() const
4131 {
4132 	if (fCurrentList == 0)
4133 		return 0;
4134 
4135 	return fCurrentList->ItemAt(fCurrentListIndex);
4136 }
4137 
4138 void RecursiveOutlineIterator::GoToNext()
4139 {
4140 	if (fCurrentList == 0)
4141 		return;
4142 	if (fCurrentListIndex < 0 || fCurrentListIndex >= fCurrentList->CountItems()) {
4143 		fCurrentList = 0;
4144 		return;
4145 	}
4146 
4147 	BRow *currentRow = fCurrentList->ItemAt(fCurrentListIndex);
4148 	if(currentRow) {
4149 		if (currentRow->fChildList && (currentRow->fIsExpanded || !fOpenBranchesOnly)
4150 			&& currentRow->fChildList->CountItems() > 0) {
4151 			// Visit child.
4152 			// Put current list on the stack if it needs to be revisited.
4153 			if (fCurrentListIndex < fCurrentList->CountItems() - 1) {
4154 				fStack[fStackIndex].fRowSet = fCurrentList;
4155 				fStack[fStackIndex].fIndex = fCurrentListIndex + 1;
4156 				fStack[fStackIndex].fDepth = fCurrentListDepth;
4157 				fStackIndex++;
4158 			}
4159 
4160 			fCurrentList = currentRow->fChildList;
4161 			fCurrentListIndex = 0;
4162 			fCurrentListDepth++;
4163 		} else if (fCurrentListIndex < fCurrentList->CountItems() - 1)
4164 			fCurrentListIndex++; // next item in current list
4165 		else if (--fStackIndex >= 0) {
4166 			fCurrentList = fStack[fStackIndex].fRowSet;
4167 			fCurrentListIndex = fStack[fStackIndex].fIndex;
4168 			fCurrentListDepth = fStack[fStackIndex].fDepth;
4169 		} else
4170 			fCurrentList = 0;
4171 	}
4172 }
4173 
4174 int32 RecursiveOutlineIterator::CurrentLevel() const
4175 {
4176 	return fCurrentListDepth;
4177 }
4178