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