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