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