xref: /haiku/src/kits/interface/OutlineListView.cpp (revision d3d8b26997fac34a84981e6d2b649521de2cc45a)
1 /*
2  * Copyright 2001-2006, 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 - 1 <= 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 	fFullList.AddItem(item, FullListIndexOf(superitem) + 1);
183 
184 	item->fLevel = superitem->OutlineLevel() + 1;
185 	superitem->fHasSubitems = true;
186 
187 	if (superitem->IsItemVisible() && superitem->IsExpanded()) {
188 		item->SetItemVisible(true);
189 
190 		int32 index = BListView::IndexOf(superitem);
191 
192 		BListView::AddItem(item, index + 1);
193 		Invalidate(LatchRect(ItemFrame(index), superitem->OutlineLevel()));
194 	} else
195 		item->SetItemVisible(false);
196 
197 	return true;
198 }
199 
200 
201 bool
202 BOutlineListView::AddItem(BListItem* item)
203 {
204 	if (!fFullList.AddItem(item))
205 		return false;
206 
207 	return BListView::AddItem(item);
208 }
209 
210 
211 bool
212 BOutlineListView::AddItem(BListItem* item, int32 fullListIndex)
213 {
214 	if (fullListIndex < 0)
215 		fullListIndex = 0;
216 	else if (fullListIndex > CountItems())
217 		fullListIndex = CountItems();
218 
219 	fFullList.AddItem(item, fullListIndex);
220 
221 	if (item->fLevel > 0) {
222 		BListItem *super = SuperitemForIndex(fullListIndex, item->fLevel);
223 
224 		if (!super->IsItemVisible() || !super->IsExpanded())
225 			return true;
226 	}
227 
228 	int32 listIndex = FindPreviousVisibleIndex(fullListIndex);
229 
230 	return BListView::AddItem(item, IndexOf(FullListItemAt(listIndex)) + 1);
231 }
232 
233 
234 bool
235 BOutlineListView::AddList(BList* newItems)
236 {
237 	printf("BOutlineListView::AddList Not implemented\n");
238 	return false;
239 }
240 
241 
242 bool
243 BOutlineListView::AddList(BList* newItems, int32 fullListIndex)
244 {
245 	printf("BOutlineListView::AddList Not implemented\n");
246 	return false;
247 }
248 
249 
250 bool
251 BOutlineListView::RemoveItem(BListItem* item)
252 {
253 	return _RemoveItem(item, FullListIndexOf(item)) != NULL;
254 }
255 
256 
257 BListItem*
258 BOutlineListView::RemoveItem(int32 fullIndex)
259 {
260 	return _RemoveItem(FullListItemAt(fullIndex), fullIndex);
261 }
262 
263 
264 bool
265 BOutlineListView::RemoveItems(int32 fullListIndex, int32 count)
266 {
267 	printf("BOutlineListView::RemoveItems Not implemented\n");
268 
269 	return false;
270 }
271 
272 
273 BListItem *
274 BOutlineListView::FullListItemAt(int32 fullListIndex) const
275 {
276 	return (BListItem*)fFullList.ItemAt(fullListIndex);
277 }
278 
279 
280 int32
281 BOutlineListView::FullListIndexOf(BPoint point) const
282 {
283 	return BListView::IndexOf(point);
284 }
285 
286 
287 int32
288 BOutlineListView::FullListIndexOf(BListItem* item) const
289 {
290 	return fFullList.IndexOf(item);
291 }
292 
293 
294 BListItem *
295 BOutlineListView::FullListFirstItem() const
296 {
297 	return (BListItem*)fFullList.FirstItem();
298 }
299 
300 
301 BListItem *
302 BOutlineListView::FullListLastItem() const
303 {
304 	return (BListItem*)fFullList.LastItem();
305 }
306 
307 
308 bool
309 BOutlineListView::FullListHasItem(BListItem *item) const
310 {
311 	return fFullList.HasItem(item);
312 }
313 
314 
315 int32
316 BOutlineListView::FullListCountItems() const
317 {
318 	return fFullList.CountItems();
319 }
320 
321 
322 int32
323 BOutlineListView::FullListCurrentSelection(int32 index) const
324 {
325 	int32 i = BListView::CurrentSelection(index);
326 
327 	BListItem *item = BListView::ItemAt(i);
328 	if (item)
329 		return fFullList.IndexOf(item);
330 
331 	return -1;
332 }
333 
334 
335 void
336 BOutlineListView::MakeEmpty()
337 {
338 	fFullList.MakeEmpty();
339 	BListView::MakeEmpty();
340 }
341 
342 
343 bool
344 BOutlineListView::FullListIsEmpty() const
345 {
346 	return fFullList.IsEmpty();
347 }
348 
349 
350 void
351 BOutlineListView::FullListDoForEach(bool(*func)(BListItem* item))
352 {
353 	fFullList.DoForEach(reinterpret_cast<bool (*)(void*)>(func));
354 }
355 
356 
357 void
358 BOutlineListView::FullListDoForEach(bool (*func)(BListItem* item, void* arg),
359 	void* arg)
360 {
361 	fList.DoForEach(reinterpret_cast<bool (*)(void*, void*)>(func), arg);
362 }
363 
364 
365 BListItem *
366 BOutlineListView::Superitem(const BListItem* item)
367 {
368 	int32 index = FullListIndexOf((BListItem*)item);
369 	if (index == -1)
370 		return NULL;
371 
372 	return SuperitemForIndex(index, item->OutlineLevel());
373 }
374 
375 
376 void
377 BOutlineListView::Expand(BListItem* item)
378 {
379 	if (item->IsExpanded() || !FullListHasItem(item))
380 		return;
381 
382 	item->fExpanded = true;
383 
384 	uint32 level = item->fLevel;
385 	int32 fullIndex = FullListIndexOf(item);
386 	int32 index = IndexOf(item) + 1;
387 	int32 count = FullListCountItems() - fullIndex - 1;
388 	BListItem** items = (BListItem**)fFullList.Items() + fullIndex + 1;
389 
390 	BFont font;
391 	GetFont(&font);
392 
393 	while (count-- > 0) {
394 		item = items[0];
395 		if (item->fLevel <= level)
396 			break;
397 
398 		if (!item->IsItemVisible()) {
399 			// fix selection hints
400 			if (index <= fFirstSelected)
401 				fFirstSelected++;
402 			if (index <= fLastSelected)
403 				fLastSelected++;
404 
405 			fList.AddItem(item, index++);
406 			item->Update(this, &font);
407 			item->SetItemVisible(true);
408 		}
409 
410 		if (item->HasSubitems() && !item->IsExpanded()) {
411 			// Skip hidden children
412 			uint32 subLevel = item->fLevel;
413 			items++;
414 
415 			while (--count > 0 && items[0]->fLevel > subLevel)
416 				items++;
417 		} else
418 			items++;
419 	}
420 
421 	_FixupScrollBar();
422 	Invalidate();
423 }
424 
425 
426 void
427 BOutlineListView::Collapse(BListItem* item)
428 {
429 	if (!item->IsExpanded() || !FullListHasItem(item))
430 		return;
431 
432 	item->fExpanded = false;
433 
434 	uint32 level = item->fLevel;
435 	int32 fullIndex = FullListIndexOf(item);
436 	int32 index = IndexOf(item);
437 	int32 max = FullListCountItems() - fullIndex - 1;
438 	int32 count = 0;
439 	BListItem** items = (BListItem**)fFullList.Items() + fullIndex + 1;
440 
441 	while (max-- > 0) {
442 		item = items[0];
443 		if (item->fLevel <= level)
444 			break;
445 
446 		if (item->IsItemVisible()) {
447 			fList.RemoveItem(item);
448 			item->SetItemVisible(false);
449 			if (item->IsSelected())
450 				item->Deselect();
451 
452 			count++;
453 		}
454 
455 		items++;
456 	}
457 
458 	// fix selection hints
459 	// TODO: revise for multi selection lists
460 	if (index < fFirstSelected) {
461 		if (index + count < fFirstSelected) {
462 			fFirstSelected -= count;
463 			fLastSelected -= count;
464 		} else {
465 			// select top item
466 			//fFirstSelected = fLastSelected = index;
467 			//item->Select();
468 			Select(index);
469 		}
470 	}
471 
472 	_FixupScrollBar();
473 	Invalidate();
474 }
475 
476 
477 bool
478 BOutlineListView::IsExpanded(int32 fullListIndex)
479 {
480 	BListItem *item = FullListItemAt(fullListIndex);
481 	if (!item)
482 		return false;
483 
484 	return item->IsExpanded();
485 }
486 
487 
488 BHandler *
489 BOutlineListView::ResolveSpecifier(BMessage* msg, int32 index,
490 	BMessage* specifier, int32 what, const char* property)
491 {
492 	return BListView::ResolveSpecifier(msg, index, specifier, what, property);
493 }
494 
495 
496 status_t
497 BOutlineListView::GetSupportedSuites(BMessage* data)
498 {
499 	return BListView::GetSupportedSuites(data);
500 }
501 
502 
503 status_t
504 BOutlineListView::Perform(perform_code d, void* arg)
505 {
506 	return BListView::Perform(d, arg);
507 }
508 
509 
510 void
511 BOutlineListView::ResizeToPreferred()
512 {
513 	BListView::ResizeToPreferred();
514 }
515 
516 
517 void
518 BOutlineListView::GetPreferredSize(float* _width, float* _height)
519 {
520 	BListView::GetPreferredSize(_width, _height);
521 }
522 
523 
524 void
525 BOutlineListView::MakeFocus(bool state)
526 {
527 	BListView::MakeFocus(state);
528 }
529 
530 
531 void
532 BOutlineListView::AllAttached()
533 {
534 	BListView::AllAttached();
535 }
536 
537 
538 void
539 BOutlineListView::AllDetached()
540 {
541 	BListView::AllDetached();
542 }
543 
544 
545 void
546 BOutlineListView::DetachedFromWindow()
547 {
548 	BListView::DetachedFromWindow();
549 }
550 
551 
552 void
553 BOutlineListView::FullListSortItems(int (*compareFunc)(const BListItem* a,
554 	const BListItem* b))
555 {
556 	SortItemsUnder(NULL, false, compareFunc);
557 }
558 
559 
560 void
561 BOutlineListView::SortItemsUnder(BListItem* underItem, bool oneLevelOnly,
562 	int (*compareFunc)(const BListItem* a, const BListItem* b))
563 {
564 	// This method is quite complicated: basically, it creates a real tree
565 	// from the items of the full list, sorts them as needed, and then
566 	// populates the entries back into the full and display lists
567 
568 	int32 firstIndex = FullListIndexOf(underItem) + 1;
569 	int32 lastIndex = firstIndex;
570 	BList* tree = _BuildTree(underItem, lastIndex);
571 
572 	_SortTree(tree, oneLevelOnly, compareFunc);
573 
574 	// Populate to the full list
575 	_PopulateTree(tree, fFullList, firstIndex, false);
576 
577 	// Populate to BListView's list
578 	firstIndex = fList.IndexOf(underItem) + 1;
579 	_PopulateTree(tree, fList, firstIndex, true);
580 
581 	_DestructTree(tree);
582 }
583 
584 
585 void
586 BOutlineListView::_PopulateTree(BList* tree, BList& target,
587 	int32& firstIndex, bool onlyVisible)
588 {
589 	BListItem** items = (BListItem**)target.Items();
590 	int32 count = tree->CountItems();
591 
592 	for (int32 index = 0; index < count; index++) {
593 		BListItem* item = (BListItem*)tree->ItemAtFast(index);
594 
595 		items[firstIndex++] = item;
596 
597 		if (item->HasSubitems() && (!onlyVisible || item->IsExpanded()))
598 			_PopulateTree(item->fTemporaryList, target, firstIndex, onlyVisible);
599 	}
600 }
601 
602 
603 void
604 BOutlineListView::_SortTree(BList* tree, bool oneLevelOnly,
605 	int (*compareFunc)(const BListItem* a, const BListItem* b))
606 {
607 	// home-brewn quick sort for our compareFunc
608 	quick_sort_item_array((BListItem**)tree->Items(), 0, tree->CountItems() - 1,
609 		compareFunc);
610 
611 	if (oneLevelOnly)
612 		return;
613 
614 	for (int32 index = tree->CountItems(); index-- > 0;) {
615 		BListItem* item = (BListItem*)tree->ItemAt(index);
616 
617 		if (item->HasSubitems())
618 			_SortTree(item->fTemporaryList, false, compareFunc);
619 	}
620 }
621 
622 
623 void
624 BOutlineListView::_DestructTree(BList* tree)
625 {
626 	for (int32 index = tree->CountItems(); index-- > 0;) {
627 		BListItem* item = (BListItem*)tree->ItemAt(index);
628 
629 		if (item->HasSubitems())
630 			_DestructTree(item->fTemporaryList);
631 	}
632 
633 	delete tree;
634 }
635 
636 
637 BList*
638 BOutlineListView::_BuildTree(BListItem* underItem, int32& fullIndex)
639 {
640 	int32 fullCount = FullListCountItems();
641 	uint32 level = underItem != NULL ? underItem->OutlineLevel() + 1 : 0;
642 	BList* list = new BList;
643 	if (underItem != NULL)
644 		underItem->fTemporaryList = list;
645 
646 	while (fullIndex < fullCount) {
647 		BListItem *item = FullListItemAt(fullIndex);
648 
649 		// If we jump out of the subtree, break out
650 		if (item->fLevel < level)
651 			break;
652 
653 		if (item->fLevel == level) {
654 			// If the level matches, put them into the list
655 			list->AddItem(item);
656 		}
657 
658 		fullIndex++;
659 
660 		if (item->HasSubitems()) {
661 			// we're going deeper
662 			_BuildTree(item, fullIndex);
663 		}
664 	}
665 
666 	return list;
667 }
668 
669 
670 int32
671 BOutlineListView::CountItemsUnder(BListItem* underItem,
672 	bool oneLevelOnly) const
673 {
674 	int32 i = IndexOf(underItem);
675 	if (i == -1)
676 		return 0;
677 
678 	int32 count = 0;
679 
680 	while (i < FullListCountItems()) {
681 		BListItem *item = FullListItemAt(i);
682 
683 		// If we jump out of the subtree, return count
684 		if (item->fLevel < underItem->OutlineLevel())
685 			return count;
686 
687 		// If the level matches, increase count
688 		if (!oneLevelOnly || item->fLevel == underItem->OutlineLevel() + 1)
689 			count++;
690 
691 		i++;
692 	}
693 
694 	return count;
695 }
696 
697 
698 BListItem *
699 BOutlineListView::EachItemUnder(BListItem *underItem, bool oneLevelOnly,
700 	BListItem *(*eachFunc)(BListItem* item, void* arg), void* arg)
701 {
702 	int32 i = IndexOf(underItem);
703 	if (i == -1)
704 		return NULL;
705 
706 	while (i < FullListCountItems()) {
707 		BListItem *item = FullListItemAt(i);
708 
709 		// If we jump out of the subtree, return NULL
710 		if (item->fLevel < underItem->OutlineLevel())
711 			return NULL;
712 
713 		// If the level matches, check the index
714 		if (!oneLevelOnly || item->fLevel == underItem->OutlineLevel() + 1) {
715 			item = eachFunc(item, arg);
716 			if (item != NULL)
717 				return item;
718 		}
719 
720 		i++;
721 	}
722 
723 	return NULL;
724 }
725 
726 
727 BListItem *
728 BOutlineListView::ItemUnderAt(BListItem* underItem,
729 	bool oneLevelOnly, int32 index) const
730 {
731 	int32 i = IndexOf(underItem);
732 	if (i == -1)
733 		return NULL;
734 
735 	while (i < FullListCountItems()) {
736 		BListItem *item = FullListItemAt(i);
737 
738 		// If we jump out of the subtree, return NULL
739 		if (item->fLevel < underItem->OutlineLevel())
740 			return NULL;
741 
742 		// If the level matches, check the index
743 		if (!oneLevelOnly || item->fLevel == underItem->OutlineLevel() + 1) {
744 			if (index == 0)
745 				return item;
746 
747 			index--;
748 		}
749 
750 		i++;
751 	}
752 
753 	return NULL;
754 }
755 
756 
757 bool
758 BOutlineListView::DoMiscellaneous(MiscCode code, MiscData* data)
759 {
760 	return BListView::DoMiscellaneous(code, data);
761 }
762 
763 
764 void
765 BOutlineListView::MessageReceived(BMessage* msg)
766 {
767 	BListView::MessageReceived(msg);
768 }
769 
770 
771 void BOutlineListView::_ReservedOutlineListView1() {}
772 void BOutlineListView::_ReservedOutlineListView2() {}
773 void BOutlineListView::_ReservedOutlineListView3() {}
774 void BOutlineListView::_ReservedOutlineListView4() {}
775 
776 
777 int32
778 BOutlineListView::FullListIndex(int32 index) const
779 {
780 	BListItem *item = ItemAt(index);
781 
782 	if (item == NULL)
783 		return -1;
784 
785 	return FullListIndexOf(item);
786 }
787 
788 
789 int32
790 BOutlineListView::ListViewIndex(int32 index) const
791 {
792 	BListItem *item = ItemAt(index);
793 
794 	if (item == NULL)
795 		return -1;
796 
797 	return BListView::IndexOf(item);
798 }
799 
800 
801 void
802 BOutlineListView::ExpandOrCollapse(BListItem *underItem, bool expand)
803 {
804 }
805 
806 
807 BRect
808 BOutlineListView::LatchRect(BRect itemRect, int32 level) const
809 {
810 	return BRect(itemRect.left, itemRect.top, itemRect.left
811 		+ (level * 10.0f + 15.0f), itemRect.bottom);
812 }
813 
814 
815 void
816 BOutlineListView::DrawLatch(BRect itemRect, int32 level, bool collapsed,
817 	bool highlighted, bool misTracked)
818 {
819 	float left = level * 10.0f;
820 
821 	if (collapsed) {
822 		SetHighColor(192, 192, 192);
823 
824 		FillTriangle(itemRect.LeftTop() + BPoint(left + 4.0f, 2.0f),
825 			itemRect.LeftTop() + BPoint(left + 4.0f, 10.0f),
826 			itemRect.LeftTop() + BPoint(left + 8.0f, 6.0f));
827 
828 		SetHighColor(0, 0, 0);
829 
830 		StrokeTriangle(itemRect.LeftTop() + BPoint(left + 4.0f, 2.0f),
831 			itemRect.LeftTop() + BPoint(left + 4.0f, 10.0f),
832 			itemRect.LeftTop() + BPoint(left + 8.0f, 6.0f));
833 	} else {
834 		SetHighColor(192, 192, 192);
835 
836 		FillTriangle(itemRect.LeftTop() + BPoint(left + 2.0f, 4.0f),
837 			itemRect.LeftTop() + BPoint(left + 10.0f, 4.0f),
838 			itemRect.LeftTop() + BPoint(left + 6.0f, 8.0f));
839 
840 		SetHighColor(0, 0, 0);
841 
842 		StrokeTriangle(itemRect.LeftTop() + BPoint(left + 2.0f, 4.0f),
843 			itemRect.LeftTop() + BPoint(left + 10.0f, 4.0f),
844 			itemRect.LeftTop() + BPoint(left + 6.0f, 8.0f));
845 	}
846 }
847 
848 
849 void
850 BOutlineListView::DrawItem(BListItem* item, BRect itemRect, bool complete)
851 {
852 	if (item->fHasSubitems)
853 		DrawLatch(itemRect, item->fLevel, !item->IsExpanded(), false, false);
854 
855 	itemRect.left += (item->fLevel) * 10.0f + 15.0f;
856 	item->DrawItem(this, itemRect, complete);
857 }
858 
859 
860 /*!
861 	\brief Removes a single item from the list and all of its children.
862 
863 	Unlike the BeOS version, this one will actually delete the children, too,
864 	as there should be no reference left to them. This may cause problems for
865 	applications that actually take the misbehaviour of the Be classes into
866 	account.
867 */
868 BListItem *
869 BOutlineListView::_RemoveItem(BListItem* item, int32 fullIndex)
870 {
871 	if (item == NULL || fullIndex < 0 || fullIndex >= FullListCountItems())
872 		return NULL;
873 
874 	if (item->IsItemVisible()) {
875 		// remove children, too
876 		uint32 level = item->OutlineLevel();
877 		int32 max = FullListCountItems() - fullIndex - 1;
878 		BListItem** items = (BListItem**)fFullList.Items() + fullIndex + 1;
879 
880 		while (max-- > 0) {
881 			BListItem* subItem = items[0];
882 			if (subItem->fLevel <= level)
883 				break;
884 
885 			if (subItem->IsItemVisible())
886 				BListView::RemoveItem(subItem);
887 
888 			fFullList.RemoveItem(fullIndex + 1);
889 
890 			// TODO: this might be problematic, see comment above
891 			delete subItem;
892 		}
893 
894 		BListView::RemoveItem(item);
895 	}
896 
897 	fFullList.RemoveItem(fullIndex);
898 	return item;
899 }
900 
901 
902 BListItem *
903 BOutlineListView::RemoveOne(int32 fullListIndex)
904 {
905 	return NULL;
906 }
907 
908 
909 void
910 BOutlineListView::TrackInLatchItem(void *)
911 {
912 }
913 
914 
915 void
916 BOutlineListView::TrackOutLatchItem(void *)
917 {
918 }
919 
920 
921 bool
922 BOutlineListView::OutlineSwapItems(int32 a, int32 b)
923 {
924 	return false;
925 }
926 
927 
928 bool
929 BOutlineListView::OutlineMoveItem(int32 from, int32 to)
930 {
931 	return false;
932 }
933 
934 
935 bool
936 BOutlineListView::OutlineReplaceItem(int32 index, BListItem *item)
937 {
938 	return false;
939 }
940 
941 
942 void
943 BOutlineListView::CommonMoveItems(int32 from, int32 count, int32 to)
944 {
945 }
946 
947 
948 BListItem *
949 BOutlineListView::SuperitemForIndex(int32 fullListIndex, int32 level)
950 {
951 	BListItem *item;
952 	fullListIndex--;
953 
954 	while (fullListIndex >= 0) {
955 		if ((item = FullListItemAt(fullListIndex))->OutlineLevel() <
956 			(uint32)level)
957 			return item;
958 
959 		fullListIndex--;
960 	}
961 
962 	return NULL;
963 }
964 
965 
966 int32
967 BOutlineListView::FindPreviousVisibleIndex(int32 fullListIndex)
968 {
969 	fullListIndex--;
970 
971 	while (fullListIndex >= 0) {
972 		if (FullListItemAt(fullListIndex)->fVisible)
973 			return fullListIndex;
974 
975 		fullListIndex--;
976 	}
977 
978 	return -1;
979 }
980 
981