xref: /haiku/src/kits/interface/OutlineListView.cpp (revision cfc3fa87da824bdf593eb8b817a83b6376e77935)
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 void
645 BOutlineListView::_PopulateTree(BList* tree, BList& target,
646 	int32& firstIndex, bool onlyVisible)
647 {
648 	BListItem** items = (BListItem**)target.Items();
649 	int32 count = tree->CountItems();
650 
651 	for (int32 index = 0; index < count; index++) {
652 		BListItem* item = (BListItem*)tree->ItemAtFast(index);
653 
654 		items[firstIndex++] = item;
655 
656 		if (item->HasSubitems() && (!onlyVisible || item->IsExpanded()))
657 			_PopulateTree(item->fTemporaryList, target, firstIndex, onlyVisible);
658 	}
659 }
660 
661 
662 void
663 BOutlineListView::_SortTree(BList* tree, bool oneLevelOnly,
664 	int (*compareFunc)(const BListItem* a, const BListItem* b))
665 {
666 	// home-brewn quick sort for our compareFunc
667 	quick_sort_item_array((BListItem**)tree->Items(), 0, tree->CountItems() - 1,
668 		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 = IndexOf(underItem);
732 	if (i == -1)
733 		return 0;
734 
735 	int32 count = 0;
736 
737 	while (i < FullListCountItems()) {
738 		BListItem *item = FullListItemAt(i);
739 
740 		// If we jump out of the subtree, return count
741 		if (item->fLevel < underItem->OutlineLevel())
742 			return count;
743 
744 		// If the level matches, increase count
745 		if (!oneLevelOnly || item->fLevel == underItem->OutlineLevel() + 1)
746 			count++;
747 
748 		i++;
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 int32
814 BOutlineListView::_GetSubitemCount(BList &list, int32 itemIndex)
815 {
816 	uint32 level = ((BListItem *)list.ItemAt(itemIndex))->OutlineLevel();
817 	int32 count = 1; // the count we return includes the parent
818 	for (int32 i = itemIndex + 1; i < fFullList.CountItems(); i++, count++) {
819 		if (((BListItem *)list.ItemAt(i))->OutlineLevel() <= level)
820 			break;
821 	}
822 
823 	return count;
824 }
825 
826 void
827 BOutlineListView::_DoSwap(BList &list, int32 firstIndex, int32 secondIndex, int32 firstCount,
828 	int32 secondCount, int32 swapCount)
829 {
830 	if (firstCount < secondCount) {
831 		for (int32 i = swapCount + 1; i < secondCount; i++)
832 			list.MoveItem(secondIndex + swapCount + i, firstIndex + i);
833 	} else {
834 		for (int32 i = swapCount + 1; i < firstCount; i++)
835 			list.MoveItem(firstIndex + swapCount + 1, secondIndex + swapCount + 1);
836 	}
837 }
838 
839 bool
840 BOutlineListView::_SwapItems(int32 first, int32 second)
841 {
842 	// same item, do nothing
843 	if (first == second)
844 		return true;
845 
846 	// fail, first item out of bounds
847 	if ((first < 0) || (first >= CountItems()))
848 		return false;
849 
850 	// fail, second item out of bounds
851 	if ((second < 0) || (second >= CountItems()))
852 		return false;
853 
854 	int32 firstIndex = min_c(first, second);
855 	int32 secondIndex = max_c(first, second);
856 	BListItem *firstItem = ItemAt(firstIndex);
857 	BListItem *secondItem = ItemAt(secondIndex);
858 
859 	if (Superitem(firstItem) != Superitem(secondItem))
860 		return false;
861 
862 	if (!firstItem->IsItemVisible() || !secondItem->IsItemVisible())
863 		return false;
864 
865 	int32 fullFirstIndex = FullListIndex(firstIndex);
866 	int32 fullSecondIndex = FullListIndex(secondIndex);
867 	int32 firstCount = _GetSubitemCount(fFullList, fullFirstIndex);
868 	int32 secondCount = _GetSubitemCount(fFullList, fullSecondIndex);
869 
870 	int32 index = (firstCount < secondCount) ? firstCount : secondCount;
871 	for (int32 i = 0; i < index; i++)
872 		fFullList.SwapItems(fullFirstIndex + i, fullSecondIndex + i);
873 	_DoSwap(fFullList, fullFirstIndex, fullSecondIndex, firstCount, secondCount, index);
874 
875 	firstCount = _GetSubitemCount(fList, firstIndex);
876 	secondCount = _GetSubitemCount(fList, secondIndex);
877 	index = (firstCount < secondCount) ? firstCount : secondCount;
878 	for (int32 i = 0; i < index; i++)
879 		fList.SwapItems(firstIndex + i, secondIndex + i);
880 	_DoSwap(fList, firstIndex, secondIndex, firstCount, secondCount, index);
881 
882 	_RecalcItemTops(firstIndex);
883 	_RescanSelection(firstIndex, secondIndex + secondCount);
884 	Invalidate(Bounds());
885 	return true;
886 }
887 
888 bool
889 BOutlineListView::DoMiscellaneous(MiscCode code, MiscData* data)
890 {
891 	if (code == B_SWAP_OP)
892 		return _SwapItems(data->swap.a, data->swap.b);
893 
894 	return BListView::DoMiscellaneous(code, data);
895 }
896 
897 
898 void
899 BOutlineListView::MessageReceived(BMessage* msg)
900 {
901 	BListView::MessageReceived(msg);
902 }
903 
904 
905 void BOutlineListView::_ReservedOutlineListView1() {}
906 void BOutlineListView::_ReservedOutlineListView2() {}
907 void BOutlineListView::_ReservedOutlineListView3() {}
908 void BOutlineListView::_ReservedOutlineListView4() {}
909 
910 
911 int32
912 BOutlineListView::FullListIndex(int32 index) const
913 {
914 	BListItem *item = ItemAt(index);
915 
916 	if (item == NULL)
917 		return -1;
918 
919 	return FullListIndexOf(item);
920 }
921 
922 
923 int32
924 BOutlineListView::ListViewIndex(int32 index) const
925 {
926 	BListItem *item = ItemAt(index);
927 
928 	if (item == NULL)
929 		return -1;
930 
931 	return BListView::IndexOf(item);
932 }
933 
934 
935 void
936 BOutlineListView::ExpandOrCollapse(BListItem *underItem, bool expand)
937 {
938 }
939 
940 
941 BRect
942 BOutlineListView::LatchRect(BRect itemRect, int32 level) const
943 {
944 	return BRect(itemRect.left, itemRect.top, itemRect.left
945 		+ (level * 10.0f + 15.0f), itemRect.bottom);
946 }
947 
948 
949 void
950 BOutlineListView::DrawLatch(BRect itemRect, int32 level, bool collapsed,
951 	bool highlighted, bool misTracked)
952 {
953 	float left = level * 10.0f;
954 
955 	if (collapsed) {
956 		SetHighColor(192, 192, 192);
957 
958 		FillTriangle(itemRect.LeftTop() + BPoint(left + 4.0f, 2.0f),
959 			itemRect.LeftTop() + BPoint(left + 4.0f, 10.0f),
960 			itemRect.LeftTop() + BPoint(left + 8.0f, 6.0f));
961 
962 		SetHighColor(0, 0, 0);
963 
964 		StrokeTriangle(itemRect.LeftTop() + BPoint(left + 4.0f, 2.0f),
965 			itemRect.LeftTop() + BPoint(left + 4.0f, 10.0f),
966 			itemRect.LeftTop() + BPoint(left + 8.0f, 6.0f));
967 	} else {
968 		SetHighColor(192, 192, 192);
969 
970 		FillTriangle(itemRect.LeftTop() + BPoint(left + 2.0f, 4.0f),
971 			itemRect.LeftTop() + BPoint(left + 10.0f, 4.0f),
972 			itemRect.LeftTop() + BPoint(left + 6.0f, 8.0f));
973 
974 		SetHighColor(0, 0, 0);
975 
976 		StrokeTriangle(itemRect.LeftTop() + BPoint(left + 2.0f, 4.0f),
977 			itemRect.LeftTop() + BPoint(left + 10.0f, 4.0f),
978 			itemRect.LeftTop() + BPoint(left + 6.0f, 8.0f));
979 	}
980 }
981 
982 
983 void
984 BOutlineListView::DrawItem(BListItem* item, BRect itemRect, bool complete)
985 {
986 	if (item->fHasSubitems)
987 		DrawLatch(itemRect, item->fLevel, !item->IsExpanded(), false, false);
988 
989 	itemRect.left += (item->fLevel) * 10.0f + 15.0f;
990 	item->DrawItem(this, itemRect, complete);
991 }
992 
993 
994 /*!
995 	\brief Removes a single item from the list and all of its children.
996 
997 	Unlike the BeOS version, this one will actually delete the children, too,
998 	as there should be no reference left to them. This may cause problems for
999 	applications that actually take the misbehaviour of the Be classes into
1000 	account.
1001 */
1002 BListItem *
1003 BOutlineListView::_RemoveItem(BListItem* item, int32 fullIndex)
1004 {
1005 	if (item == NULL || fullIndex < 0 || fullIndex >= FullListCountItems())
1006 		return NULL;
1007 
1008 	uint32 level = item->OutlineLevel();
1009 	int32 superIndex;
1010 	BListItem* super = _SuperitemForIndex(fullIndex, level, &superIndex);
1011 
1012 	if (item->IsItemVisible()) {
1013 		// remove children, too
1014 		while (fullIndex + 1 < CountItems()) {
1015 			BListItem *subItem = ItemAt(fullIndex + 1);
1016 
1017 			if (subItem->OutlineLevel() <= level)
1018 				break;
1019 
1020 			if (subItem->IsItemVisible())
1021 				BListView::RemoveItem(subItem);
1022 
1023 			fFullList.RemoveItem(fullIndex + 1);
1024 			delete subItem;
1025 		}
1026 		BListView::RemoveItem(item);
1027 	}
1028 
1029 	fFullList.RemoveItem(fullIndex);
1030 
1031 	if (super != NULL) {
1032 		// we might need to change the fHasSubitems field of the parent
1033 		BListItem* child = FullListItemAt(superIndex + 1);
1034 		if (child == NULL || child->OutlineLevel() <= super->OutlineLevel())
1035 			super->fHasSubitems = false;
1036 	}
1037 	return item;
1038 }
1039 
1040 
1041 BListItem *
1042 BOutlineListView::RemoveOne(int32 fullListIndex)
1043 {
1044 	return NULL;
1045 }
1046 
1047 
1048 void
1049 BOutlineListView::TrackInLatchItem(void *)
1050 {
1051 }
1052 
1053 
1054 void
1055 BOutlineListView::TrackOutLatchItem(void *)
1056 {
1057 }
1058 
1059 
1060 bool
1061 BOutlineListView::OutlineSwapItems(int32 a, int32 b)
1062 {
1063 	return false;
1064 }
1065 
1066 
1067 bool
1068 BOutlineListView::OutlineMoveItem(int32 from, int32 to)
1069 {
1070 	return false;
1071 }
1072 
1073 
1074 bool
1075 BOutlineListView::OutlineReplaceItem(int32 index, BListItem *item)
1076 {
1077 	return false;
1078 }
1079 
1080 
1081 void
1082 BOutlineListView::CommonMoveItems(int32 from, int32 count, int32 to)
1083 {
1084 }
1085 
1086 
1087 /*!
1088 	Returns the super item before the item specified by \a fullListIndex
1089 	and \a level.
1090 */
1091 BListItem *
1092 BOutlineListView::_SuperitemForIndex(int32 fullListIndex, int32 level,
1093 	int32* _superIndex)
1094 {
1095 	BListItem *item;
1096 	fullListIndex--;
1097 
1098 	while (fullListIndex >= 0) {
1099 		if ((item = FullListItemAt(fullListIndex))->OutlineLevel()
1100 				< (uint32)level) {
1101 			if (_superIndex != NULL)
1102 				*_superIndex = fullListIndex;
1103 			return item;
1104 		}
1105 
1106 		fullListIndex--;
1107 	}
1108 
1109 	return NULL;
1110 }
1111 
1112 
1113 int32
1114 BOutlineListView::_FindPreviousVisibleIndex(int32 fullListIndex)
1115 {
1116 	fullListIndex--;
1117 
1118 	while (fullListIndex >= 0) {
1119 		if (FullListItemAt(fullListIndex)->fVisible)
1120 			return fullListIndex;
1121 
1122 		fullListIndex--;
1123 	}
1124 
1125 	return -1;
1126 }
1127 
1128