xref: /haiku/src/kits/interface/OutlineListView.cpp (revision 2f470aec1c92ce6917b8a903e343795dc77af41f)
1 /*
2  * Copyright 2001-2007, Haiku Inc.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Marc Flerackers (mflerackers@androme.be)
7  *		Axel Dörfler, axeld@pinc-software.de
8  */
9 
10 //! BOutlineListView represents a "nestable" list view.
11 
12 #include <OutlineListView.h>
13 
14 #include <stdio.h>
15 #include <stdlib.h>
16 
17 
18 /*!
19 	\brief A simple recursive quick sort implementations for BListItem arrays.
20 
21 	We need to implement it ourselves, since the BOutlineListView sort methods
22 	use a different comparison function than the standard functions.
23 */
24 static void
25 quick_sort_item_array(BListItem** items, int32 first, int32 last,
26 	int (*compareFunc)(const BListItem* a, const BListItem* b))
27 {
28 	if (last <= first)
29 		return;
30 
31 	BListItem* pivot = items[first + (rand() % (last - first))];
32 		// choose an arbitrary pivot element
33 
34 	int32 left = first;
35 	int32 right = last;
36 
37 	do {
38 		while (compareFunc(items[left], pivot) < 0) {
39 			left++;
40 		}
41 		while (compareFunc(items[right], pivot) > 0) {
42 			right--;
43 		}
44 
45 		if (left <= right) {
46 			// swap entries
47 			BListItem* temp = items[left];
48 			items[left] = items[right];
49 			items[right] = temp;
50 
51 			left++;
52 			right--;
53 		}
54 	} while (left <= right);
55 
56 	// At this point, the elements in the left half are all smaller
57 	// than the elements in the right half
58 
59 	if (first < right) {
60 		// sort left half
61 		quick_sort_item_array(items, first, right, compareFunc);
62 	}
63 	if (left < last) {
64 		// sort right half
65 		quick_sort_item_array(items, left, last, compareFunc);
66 	}
67 }
68 
69 
70 //	#pragma mark -
71 
72 
73 BOutlineListView::BOutlineListView(BRect frame, const char* name,
74 		list_view_type type, uint32 resizeMode, uint32 flags)
75 	: BListView(frame, name, type, resizeMode, flags)
76 {
77 }
78 
79 
80 BOutlineListView::BOutlineListView(BMessage* archive)
81 	: BListView(archive)
82 {
83 }
84 
85 
86 BOutlineListView::~BOutlineListView()
87 {
88 	fFullList.MakeEmpty();
89 }
90 
91 
92 BArchivable *
93 BOutlineListView::Instantiate(BMessage* archive)
94 {
95 	if (validate_instantiation(archive, "BOutlineListView"))
96 		return new BOutlineListView(archive);
97 
98 	return NULL;
99 }
100 
101 
102 status_t
103 BOutlineListView::Archive(BMessage* archive, bool deep) const
104 {
105 	return BListView::Archive(archive, deep);
106 }
107 
108 
109 void
110 BOutlineListView::MouseDown(BPoint point)
111 {
112 	MakeFocus();
113 
114 	int32 index = IndexOf(point);
115 
116 	if (index != -1) {
117 		BListItem *item = ItemAt(index);
118 
119 		if (item->fHasSubitems
120 			&& LatchRect(ItemFrame(index), item->fLevel).Contains(point)) {
121 			if (item->IsExpanded())
122 				Collapse(item);
123 			else
124 				Expand(item);
125 		} else
126 			BListView::MouseDown(point);
127 	}
128 }
129 
130 
131 void
132 BOutlineListView::KeyDown(const char* bytes, int32 numBytes)
133 {
134 	if (numBytes == 1) {
135 		switch (bytes[0]) {
136 			case B_RIGHT_ARROW:
137 			{
138 				BListItem *item = ItemAt(CurrentSelection());
139 				if (item && item->fHasSubitems)
140 					Expand(item);
141 				return;
142 			}
143 
144 			case B_LEFT_ARROW:
145 			{
146 				BListItem *item = ItemAt(CurrentSelection());
147 				if (item && item->fHasSubitems)
148 					Collapse(item);
149 				return;
150 			}
151 		}
152 	}
153 
154 	BListView::KeyDown(bytes, numBytes);
155 }
156 
157 
158 void
159 BOutlineListView::FrameMoved(BPoint newPosition)
160 {
161 	BListView::FrameMoved(newPosition);
162 }
163 
164 
165 void
166 BOutlineListView::FrameResized(float newWidth, float newHeight)
167 {
168 	BListView::FrameResized(newWidth, newHeight);
169 }
170 
171 
172 void
173 BOutlineListView::MouseUp(BPoint where)
174 {
175 	BListView::MouseUp(where);
176 }
177 
178 
179 bool
180 BOutlineListView::AddUnder(BListItem* item, BListItem* superitem)
181 {
182 	if (superitem == NULL)
183 		return AddItem(item);
184 
185 	fFullList.AddItem(item, FullListIndexOf(superitem) + 1);
186 
187 	item->fLevel = superitem->OutlineLevel() + 1;
188 	superitem->fHasSubitems = true;
189 
190 	if (superitem->IsItemVisible() && superitem->IsExpanded()) {
191 		item->SetItemVisible(true);
192 
193 		int32 index = BListView::IndexOf(superitem);
194 
195 		BListView::AddItem(item, index + 1);
196 		Invalidate(LatchRect(ItemFrame(index), superitem->OutlineLevel()));
197 	} else
198 		item->SetItemVisible(false);
199 
200 	return true;
201 }
202 
203 
204 bool
205 BOutlineListView::AddItem(BListItem* item)
206 {
207 	return AddItem(item, FullListCountItems());
208 }
209 
210 
211 bool
212 BOutlineListView::AddItem(BListItem* item, int32 fullListIndex)
213 {
214 	if (fullListIndex < 0)
215 		fullListIndex = 0;
216 	else if (fullListIndex > FullListCountItems())
217 		fullListIndex = FullListCountItems();
218 
219 	if (!fFullList.AddItem(item, fullListIndex))
220 		return false;
221 
222 	// Check if this item is visible, and if it is, add it to the
223 	// other list, too
224 
225 	if (item->fLevel > 0) {
226 		BListItem *super = _SuperitemForIndex(fullListIndex, item->fLevel);
227 		if (super == NULL)
228 			return true;
229 
230 		bool hadSubitems = super->fHasSubitems;
231 		super->fHasSubitems = true;
232 
233 		if (!super->IsItemVisible() || !super->IsExpanded()) {
234 			item->SetItemVisible(false);
235 			return true;
236 		}
237 
238 		if (!hadSubitems)
239 			Invalidate(LatchRect(ItemFrame(IndexOf(super)), super->OutlineLevel()));
240 	}
241 
242 	int32 listIndex = _FindPreviousVisibleIndex(fullListIndex);
243 
244 	if (!BListView::AddItem(item, IndexOf(FullListItemAt(listIndex)) + 1)) {
245 		// adding didn't work out, we need to remove it from the main list again
246 		fFullList.RemoveItem(fullListIndex);
247 		return false;
248 	}
249 
250 	return true;
251 }
252 
253 
254 bool
255 BOutlineListView::AddList(BList* newItems)
256 {
257 	printf("BOutlineListView::AddList Not implemented\n");
258 	return false;
259 }
260 
261 
262 bool
263 BOutlineListView::AddList(BList* newItems, int32 fullListIndex)
264 {
265 	printf("BOutlineListView::AddList Not implemented\n");
266 	return false;
267 }
268 
269 
270 bool
271 BOutlineListView::RemoveItem(BListItem* item)
272 {
273 	return _RemoveItem(item, FullListIndexOf(item)) != NULL;
274 }
275 
276 
277 BListItem*
278 BOutlineListView::RemoveItem(int32 fullIndex)
279 {
280 	return _RemoveItem(FullListItemAt(fullIndex), fullIndex);
281 }
282 
283 
284 bool
285 BOutlineListView::RemoveItems(int32 fullIndex, int32 count)
286 {
287 	if (fullIndex >= FullListCountItems())
288 		fullIndex = -1;
289 	if (fullIndex < 0)
290 		return false;
291 
292 	// TODO: very bad for performance!!
293 	while (count--) {
294 		BOutlineListView::RemoveItem(fullIndex);
295 	}
296 
297 	return true;
298 }
299 
300 
301 BListItem *
302 BOutlineListView::FullListItemAt(int32 fullListIndex) const
303 {
304 	return (BListItem*)fFullList.ItemAt(fullListIndex);
305 }
306 
307 
308 int32
309 BOutlineListView::FullListIndexOf(BPoint point) const
310 {
311 	return BListView::IndexOf(point);
312 }
313 
314 
315 int32
316 BOutlineListView::FullListIndexOf(BListItem* item) const
317 {
318 	return fFullList.IndexOf(item);
319 }
320 
321 
322 BListItem *
323 BOutlineListView::FullListFirstItem() const
324 {
325 	return (BListItem*)fFullList.FirstItem();
326 }
327 
328 
329 BListItem *
330 BOutlineListView::FullListLastItem() const
331 {
332 	return (BListItem*)fFullList.LastItem();
333 }
334 
335 
336 bool
337 BOutlineListView::FullListHasItem(BListItem *item) const
338 {
339 	return fFullList.HasItem(item);
340 }
341 
342 
343 int32
344 BOutlineListView::FullListCountItems() const
345 {
346 	return fFullList.CountItems();
347 }
348 
349 
350 int32
351 BOutlineListView::FullListCurrentSelection(int32 index) const
352 {
353 	int32 i = BListView::CurrentSelection(index);
354 
355 	BListItem *item = BListView::ItemAt(i);
356 	if (item)
357 		return fFullList.IndexOf(item);
358 
359 	return -1;
360 }
361 
362 
363 void
364 BOutlineListView::MakeEmpty()
365 {
366 	fFullList.MakeEmpty();
367 	BListView::MakeEmpty();
368 }
369 
370 
371 bool
372 BOutlineListView::FullListIsEmpty() const
373 {
374 	return fFullList.IsEmpty();
375 }
376 
377 
378 void
379 BOutlineListView::FullListDoForEach(bool(*func)(BListItem* item))
380 {
381 	fFullList.DoForEach(reinterpret_cast<bool (*)(void*)>(func));
382 }
383 
384 
385 void
386 BOutlineListView::FullListDoForEach(bool (*func)(BListItem* item, void* arg),
387 	void* arg)
388 {
389 	fList.DoForEach(reinterpret_cast<bool (*)(void*, void*)>(func), arg);
390 }
391 
392 
393 BListItem *
394 BOutlineListView::Superitem(const BListItem* item)
395 {
396 	int32 index = FullListIndexOf((BListItem*)item);
397 	if (index == -1)
398 		return NULL;
399 
400 	return _SuperitemForIndex(index, item->OutlineLevel());
401 }
402 
403 
404 void
405 BOutlineListView::Expand(BListItem* item)
406 {
407 	if (item->IsExpanded() || !FullListHasItem(item))
408 		return;
409 
410 	item->fExpanded = true;
411 
412 	uint32 level = item->fLevel;
413 	int32 fullIndex = FullListIndexOf(item);
414 	int32 index = IndexOf(item) + 1;
415 	int32 count = FullListCountItems() - fullIndex - 1;
416 	BListItem** items = (BListItem**)fFullList.Items() + fullIndex + 1;
417 
418 	BFont font;
419 	GetFont(&font);
420 
421 	while (count-- > 0) {
422 		item = items[0];
423 		if (item->fLevel <= level)
424 			break;
425 
426 		if (!item->IsItemVisible()) {
427 			// fix selection hints
428 			if (index <= fFirstSelected)
429 				fFirstSelected++;
430 			if (index <= fLastSelected)
431 				fLastSelected++;
432 
433 			fList.AddItem(item, index++);
434 			item->Update(this, &font);
435 			item->SetItemVisible(true);
436 		}
437 
438 		if (item->HasSubitems() && !item->IsExpanded()) {
439 			// Skip hidden children
440 			uint32 subLevel = item->fLevel;
441 			items++;
442 
443 			while (--count > 0 && items[0]->fLevel > subLevel)
444 				items++;
445 		} else
446 			items++;
447 	}
448 
449 	_FixupScrollBar();
450 	Invalidate();
451 }
452 
453 
454 void
455 BOutlineListView::Collapse(BListItem* item)
456 {
457 	if (!item->IsExpanded() || !FullListHasItem(item))
458 		return;
459 
460 	item->fExpanded = false;
461 
462 	uint32 level = item->fLevel;
463 	int32 fullIndex = FullListIndexOf(item);
464 	int32 index = IndexOf(item);
465 	int32 max = FullListCountItems() - fullIndex - 1;
466 	int32 count = 0;
467 	BListItem** items = (BListItem**)fFullList.Items() + fullIndex + 1;
468 
469 	while (max-- > 0) {
470 		item = items[0];
471 		if (item->fLevel <= level)
472 			break;
473 
474 		if (item->IsItemVisible()) {
475 			fList.RemoveItem(item);
476 			item->SetItemVisible(false);
477 			if (item->IsSelected())
478 				item->Deselect();
479 
480 			count++;
481 		}
482 
483 		items++;
484 	}
485 
486 	// fix selection hints
487 	// TODO: revise for multi selection lists
488 	if (index < fFirstSelected) {
489 		if (index + count < fFirstSelected) {
490 			fFirstSelected -= count;
491 			fLastSelected -= count;
492 		} else {
493 			// select top item
494 			//fFirstSelected = fLastSelected = index;
495 			//item->Select();
496 			Select(index);
497 		}
498 	}
499 
500 	_FixupScrollBar();
501 	Invalidate();
502 }
503 
504 
505 bool
506 BOutlineListView::IsExpanded(int32 fullListIndex)
507 {
508 	BListItem *item = FullListItemAt(fullListIndex);
509 	if (!item)
510 		return false;
511 
512 	return item->IsExpanded();
513 }
514 
515 
516 BHandler *
517 BOutlineListView::ResolveSpecifier(BMessage* msg, int32 index,
518 	BMessage* specifier, int32 what, const char* property)
519 {
520 	return BListView::ResolveSpecifier(msg, index, specifier, what, property);
521 }
522 
523 
524 status_t
525 BOutlineListView::GetSupportedSuites(BMessage* data)
526 {
527 	return BListView::GetSupportedSuites(data);
528 }
529 
530 
531 status_t
532 BOutlineListView::Perform(perform_code d, void* arg)
533 {
534 	return BListView::Perform(d, arg);
535 }
536 
537 
538 void
539 BOutlineListView::ResizeToPreferred()
540 {
541 	BListView::ResizeToPreferred();
542 }
543 
544 
545 void
546 BOutlineListView::GetPreferredSize(float* _width, float* _height)
547 {
548 	BListView::GetPreferredSize(_width, _height);
549 }
550 
551 
552 void
553 BOutlineListView::MakeFocus(bool state)
554 {
555 	BListView::MakeFocus(state);
556 }
557 
558 
559 void
560 BOutlineListView::AllAttached()
561 {
562 	BListView::AllAttached();
563 }
564 
565 
566 void
567 BOutlineListView::AllDetached()
568 {
569 	BListView::AllDetached();
570 }
571 
572 
573 void
574 BOutlineListView::DetachedFromWindow()
575 {
576 	BListView::DetachedFromWindow();
577 }
578 
579 
580 void
581 BOutlineListView::FullListSortItems(int (*compareFunc)(const BListItem* a,
582 	const BListItem* b))
583 {
584 	SortItemsUnder(NULL, false, compareFunc);
585 }
586 
587 
588 void
589 BOutlineListView::SortItemsUnder(BListItem* underItem, bool oneLevelOnly,
590 	int (*compareFunc)(const BListItem* a, const BListItem* b))
591 {
592 	// This method is quite complicated: basically, it creates a real tree
593 	// from the items of the full list, sorts them as needed, and then
594 	// populates the entries back into the full and display lists
595 
596 	int32 firstIndex = FullListIndexOf(underItem) + 1;
597 	int32 lastIndex = firstIndex;
598 	BList* tree = _BuildTree(underItem, lastIndex);
599 
600 	_SortTree(tree, oneLevelOnly, compareFunc);
601 
602 	// Populate to the full list
603 	_PopulateTree(tree, fFullList, firstIndex, false);
604 
605 	if (underItem == NULL || (underItem->IsItemVisible() && underItem->IsExpanded())) {
606 		// Populate to BListView's list
607 		firstIndex = fList.IndexOf(underItem) + 1;
608 		lastIndex = firstIndex;
609 		_PopulateTree(tree, fList, lastIndex, true);
610 
611 		if (fFirstSelected != -1) {
612 			// update selection hints
613 			fFirstSelected = _CalcFirstSelected(0);
614 			fLastSelected = _CalcLastSelected(CountItems());
615 		}
616 
617 		// only invalidate what may have changed
618 		BRect top = ItemFrame(firstIndex);
619 		BRect bottom = ItemFrame(lastIndex);
620 		BRect update(top.left, top.top, bottom.right, bottom.bottom);
621 		Invalidate(update);
622 	}
623 
624 	_DestructTree(tree);
625 }
626 
627 
628 void
629 BOutlineListView::_PopulateTree(BList* tree, BList& target,
630 	int32& firstIndex, bool onlyVisible)
631 {
632 	BListItem** items = (BListItem**)target.Items();
633 	int32 count = tree->CountItems();
634 
635 	for (int32 index = 0; index < count; index++) {
636 		BListItem* item = (BListItem*)tree->ItemAtFast(index);
637 
638 		items[firstIndex++] = item;
639 
640 		if (item->HasSubitems() && (!onlyVisible || item->IsExpanded()))
641 			_PopulateTree(item->fTemporaryList, target, firstIndex, onlyVisible);
642 	}
643 }
644 
645 
646 void
647 BOutlineListView::_SortTree(BList* tree, bool oneLevelOnly,
648 	int (*compareFunc)(const BListItem* a, const BListItem* b))
649 {
650 	// home-brewn quick sort for our compareFunc
651 	quick_sort_item_array((BListItem**)tree->Items(), 0, tree->CountItems() - 1,
652 		compareFunc);
653 
654 	if (oneLevelOnly)
655 		return;
656 
657 	for (int32 index = tree->CountItems(); index-- > 0;) {
658 		BListItem* item = (BListItem*)tree->ItemAt(index);
659 
660 		if (item->HasSubitems())
661 			_SortTree(item->fTemporaryList, false, compareFunc);
662 	}
663 }
664 
665 
666 void
667 BOutlineListView::_DestructTree(BList* tree)
668 {
669 	for (int32 index = tree->CountItems(); index-- > 0;) {
670 		BListItem* item = (BListItem*)tree->ItemAt(index);
671 
672 		if (item->HasSubitems())
673 			_DestructTree(item->fTemporaryList);
674 	}
675 
676 	delete tree;
677 }
678 
679 
680 BList*
681 BOutlineListView::_BuildTree(BListItem* underItem, int32& fullIndex)
682 {
683 	int32 fullCount = FullListCountItems();
684 	uint32 level = underItem != NULL ? underItem->OutlineLevel() + 1 : 0;
685 	BList* list = new BList;
686 	if (underItem != NULL)
687 		underItem->fTemporaryList = list;
688 
689 	while (fullIndex < fullCount) {
690 		BListItem *item = FullListItemAt(fullIndex);
691 
692 		// If we jump out of the subtree, break out
693 		if (item->fLevel < level)
694 			break;
695 
696 		// If the level matches, put them into the list
697 		// (we handle the case of a missing sublevel gracefully)
698 		list->AddItem(item);
699 		fullIndex++;
700 
701 		if (item->HasSubitems()) {
702 			// we're going deeper
703 			_BuildTree(item, fullIndex);
704 		}
705 	}
706 
707 	return list;
708 }
709 
710 
711 int32
712 BOutlineListView::CountItemsUnder(BListItem* underItem,
713 	bool oneLevelOnly) const
714 {
715 	int32 i = IndexOf(underItem);
716 	if (i == -1)
717 		return 0;
718 
719 	int32 count = 0;
720 
721 	while (i < FullListCountItems()) {
722 		BListItem *item = FullListItemAt(i);
723 
724 		// If we jump out of the subtree, return count
725 		if (item->fLevel < underItem->OutlineLevel())
726 			return count;
727 
728 		// If the level matches, increase count
729 		if (!oneLevelOnly || item->fLevel == underItem->OutlineLevel() + 1)
730 			count++;
731 
732 		i++;
733 	}
734 
735 	return count;
736 }
737 
738 
739 BListItem *
740 BOutlineListView::EachItemUnder(BListItem *underItem, bool oneLevelOnly,
741 	BListItem *(*eachFunc)(BListItem* item, void* arg), void* arg)
742 {
743 	int32 i = IndexOf(underItem);
744 	if (i == -1)
745 		return NULL;
746 
747 	while (i < FullListCountItems()) {
748 		BListItem *item = FullListItemAt(i);
749 
750 		// If we jump out of the subtree, return NULL
751 		if (item->fLevel < underItem->OutlineLevel())
752 			return NULL;
753 
754 		// If the level matches, check the index
755 		if (!oneLevelOnly || item->fLevel == underItem->OutlineLevel() + 1) {
756 			item = eachFunc(item, arg);
757 			if (item != NULL)
758 				return item;
759 		}
760 
761 		i++;
762 	}
763 
764 	return NULL;
765 }
766 
767 
768 BListItem *
769 BOutlineListView::ItemUnderAt(BListItem* underItem,
770 	bool oneLevelOnly, int32 index) const
771 {
772 	int32 i = IndexOf(underItem);
773 	if (i == -1)
774 		return NULL;
775 
776 	while (i < FullListCountItems()) {
777 		BListItem *item = FullListItemAt(i);
778 
779 		// If we jump out of the subtree, return NULL
780 		if (item->fLevel < underItem->OutlineLevel())
781 			return NULL;
782 
783 		// If the level matches, check the index
784 		if (!oneLevelOnly || item->fLevel == underItem->OutlineLevel() + 1) {
785 			if (index == 0)
786 				return item;
787 
788 			index--;
789 		}
790 
791 		i++;
792 	}
793 
794 	return NULL;
795 }
796 
797 
798 bool
799 BOutlineListView::DoMiscellaneous(MiscCode code, MiscData* data)
800 {
801 	return BListView::DoMiscellaneous(code, data);
802 }
803 
804 
805 void
806 BOutlineListView::MessageReceived(BMessage* msg)
807 {
808 	BListView::MessageReceived(msg);
809 }
810 
811 
812 void BOutlineListView::_ReservedOutlineListView1() {}
813 void BOutlineListView::_ReservedOutlineListView2() {}
814 void BOutlineListView::_ReservedOutlineListView3() {}
815 void BOutlineListView::_ReservedOutlineListView4() {}
816 
817 
818 int32
819 BOutlineListView::FullListIndex(int32 index) const
820 {
821 	BListItem *item = ItemAt(index);
822 
823 	if (item == NULL)
824 		return -1;
825 
826 	return FullListIndexOf(item);
827 }
828 
829 
830 int32
831 BOutlineListView::ListViewIndex(int32 index) const
832 {
833 	BListItem *item = ItemAt(index);
834 
835 	if (item == NULL)
836 		return -1;
837 
838 	return BListView::IndexOf(item);
839 }
840 
841 
842 void
843 BOutlineListView::ExpandOrCollapse(BListItem *underItem, bool expand)
844 {
845 }
846 
847 
848 BRect
849 BOutlineListView::LatchRect(BRect itemRect, int32 level) const
850 {
851 	return BRect(itemRect.left, itemRect.top, itemRect.left
852 		+ (level * 10.0f + 15.0f), itemRect.bottom);
853 }
854 
855 
856 void
857 BOutlineListView::DrawLatch(BRect itemRect, int32 level, bool collapsed,
858 	bool highlighted, bool misTracked)
859 {
860 	float left = level * 10.0f;
861 
862 	if (collapsed) {
863 		SetHighColor(192, 192, 192);
864 
865 		FillTriangle(itemRect.LeftTop() + BPoint(left + 4.0f, 2.0f),
866 			itemRect.LeftTop() + BPoint(left + 4.0f, 10.0f),
867 			itemRect.LeftTop() + BPoint(left + 8.0f, 6.0f));
868 
869 		SetHighColor(0, 0, 0);
870 
871 		StrokeTriangle(itemRect.LeftTop() + BPoint(left + 4.0f, 2.0f),
872 			itemRect.LeftTop() + BPoint(left + 4.0f, 10.0f),
873 			itemRect.LeftTop() + BPoint(left + 8.0f, 6.0f));
874 	} else {
875 		SetHighColor(192, 192, 192);
876 
877 		FillTriangle(itemRect.LeftTop() + BPoint(left + 2.0f, 4.0f),
878 			itemRect.LeftTop() + BPoint(left + 10.0f, 4.0f),
879 			itemRect.LeftTop() + BPoint(left + 6.0f, 8.0f));
880 
881 		SetHighColor(0, 0, 0);
882 
883 		StrokeTriangle(itemRect.LeftTop() + BPoint(left + 2.0f, 4.0f),
884 			itemRect.LeftTop() + BPoint(left + 10.0f, 4.0f),
885 			itemRect.LeftTop() + BPoint(left + 6.0f, 8.0f));
886 	}
887 }
888 
889 
890 void
891 BOutlineListView::DrawItem(BListItem* item, BRect itemRect, bool complete)
892 {
893 	if (item->fHasSubitems)
894 		DrawLatch(itemRect, item->fLevel, !item->IsExpanded(), false, false);
895 
896 	itemRect.left += (item->fLevel) * 10.0f + 15.0f;
897 	item->DrawItem(this, itemRect, complete);
898 }
899 
900 
901 /*!
902 	\brief Removes a single item from the list and all of its children.
903 
904 	Unlike the BeOS version, this one will actually delete the children, too,
905 	as there should be no reference left to them. This may cause problems for
906 	applications that actually take the misbehaviour of the Be classes into
907 	account.
908 */
909 BListItem *
910 BOutlineListView::_RemoveItem(BListItem* item, int32 fullIndex)
911 {
912 	if (item == NULL || fullIndex < 0 || fullIndex >= FullListCountItems())
913 		return NULL;
914 
915 	uint32 level = item->OutlineLevel();
916 	int32 superIndex;
917 	BListItem* super = _SuperitemForIndex(fullIndex, level, &superIndex);
918 
919 	if (item->IsItemVisible()) {
920 		// remove children, too
921 		int32 max = FullListCountItems() - fullIndex - 1;
922 		BListItem** items = (BListItem**)fFullList.Items() + fullIndex + 1;
923 
924 		while (max-- > 0) {
925 			BListItem* subItem = items[0];
926 			if (subItem->fLevel <= level)
927 				break;
928 
929 			if (subItem->IsItemVisible())
930 				BListView::RemoveItem(subItem);
931 
932 			fFullList.RemoveItem(fullIndex + 1);
933 
934 			// TODO: this might be problematic, see comment above
935 			delete subItem;
936 		}
937 
938 		BListView::RemoveItem(item);
939 	}
940 
941 	fFullList.RemoveItem(fullIndex);
942 
943 	if (super != NULL) {
944 		// we might need to change the fHasSubitems field of the parent
945 		BListItem* child = FullListItemAt(superIndex + 1);
946 		if (child == NULL || child->OutlineLevel() <= super->OutlineLevel())
947 			super->fHasSubitems = false;
948 	}
949 
950 	return item;
951 }
952 
953 
954 BListItem *
955 BOutlineListView::RemoveOne(int32 fullListIndex)
956 {
957 	return NULL;
958 }
959 
960 
961 void
962 BOutlineListView::TrackInLatchItem(void *)
963 {
964 }
965 
966 
967 void
968 BOutlineListView::TrackOutLatchItem(void *)
969 {
970 }
971 
972 
973 bool
974 BOutlineListView::OutlineSwapItems(int32 a, int32 b)
975 {
976 	return false;
977 }
978 
979 
980 bool
981 BOutlineListView::OutlineMoveItem(int32 from, int32 to)
982 {
983 	return false;
984 }
985 
986 
987 bool
988 BOutlineListView::OutlineReplaceItem(int32 index, BListItem *item)
989 {
990 	return false;
991 }
992 
993 
994 void
995 BOutlineListView::CommonMoveItems(int32 from, int32 count, int32 to)
996 {
997 }
998 
999 
1000 /*!
1001 	Returns the super item before the item specified by \a fullListIndex
1002 	and \a level.
1003 */
1004 BListItem *
1005 BOutlineListView::_SuperitemForIndex(int32 fullListIndex, int32 level,
1006 	int32* _superIndex)
1007 {
1008 	BListItem *item;
1009 	fullListIndex--;
1010 
1011 	while (fullListIndex >= 0) {
1012 		if ((item = FullListItemAt(fullListIndex))->OutlineLevel()
1013 				< (uint32)level) {
1014 			if (_superIndex != NULL)
1015 				*_superIndex = fullListIndex;
1016 			return item;
1017 		}
1018 
1019 		fullListIndex--;
1020 	}
1021 
1022 	return NULL;
1023 }
1024 
1025 
1026 int32
1027 BOutlineListView::_FindPreviousVisibleIndex(int32 fullListIndex)
1028 {
1029 	fullListIndex--;
1030 
1031 	while (fullListIndex >= 0) {
1032 		if (FullListItemAt(fullListIndex)->fVisible)
1033 			return fullListIndex;
1034 
1035 		fullListIndex--;
1036 	}
1037 
1038 	return -1;
1039 }
1040 
1041