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