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