xref: /haiku/src/kits/interface/ListView.cpp (revision 4f00613311d0bd6b70fa82ce19931c41f071ea4e)
1 //------------------------------------------------------------------------------
2 //	Copyright (c) 2001-2005, Haiku, Inc.
3 //
4 //	Permission is hereby granted, free of charge, to any person obtaining a
5 //	copy of this software and associated documentation files (the "Software"),
6 //	to deal in the Software without restriction, including without limitation
7 //	the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 //	and/or sell copies of the Software, and to permit persons to whom the
9 //	Software is furnished to do so, subject to the following conditions:
10 //
11 //	The above copyright notice and this permission notice shall be included in
12 //	all copies or substantial portions of the Software.
13 //
14 //	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 //	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 //	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 //	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 //	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 //	FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 //	DEALINGS IN THE SOFTWARE.
21 //
22 //	File Name:		ListView.cpp
23 //	Author:			Ulrich Wimboeck
24 //					Marc Flerackers (mflerackers@androme.be)
25 //	Description:	BListView represents a one-dimensional list view.
26 //------------------------------------------------------------------------------
27 
28 // Standard Includes -----------------------------------------------------------
29 
30 // System Includes -------------------------------------------------------------
31 #include <stdio.h>
32 
33 #include <ListView.h>
34 #include <ScrollBar.h>
35 #include <ScrollView.h>
36 #include <support/Errors.h>
37 #include <PropertyInfo.h>
38 #include <Window.h>
39 
40 // Project Includes ------------------------------------------------------------
41 
42 // Local Includes --------------------------------------------------------------
43 
44 // Local Defines ---------------------------------------------------------------
45 
46 // Globals ---------------------------------------------------------------------
47 static property_info prop_list[] =
48 {
49 	{ "Item", { B_COUNT_PROPERTIES, 0 }, { B_DIRECT_SPECIFIER, 0 },
50 		"Returns the number of BListItems currently in the list." },
51 	{ "Item", { B_EXECUTE_PROPERTY, 0 }, { B_INDEX_SPECIFIER, B_REVERSE_INDEX_SPECIFIER,
52 		B_RANGE_SPECIFIER, B_REVERSE_RANGE_SPECIFIER, 0 },
53 		"Select and invoke the specified items, first removing any existing selection." },
54 	{ "Selection", { B_COUNT_PROPERTIES, 0 }, { B_DIRECT_SPECIFIER, 0 },
55 		"Returns int32 count of items in the selection." },
56 	{ "Selection", { B_EXECUTE_PROPERTY, 0 }, { B_DIRECT_SPECIFIER, 0 },
57 		"Invoke items in selection." },
58 	{ "Selection", { B_GET_PROPERTY, 0 }, { B_DIRECT_SPECIFIER, 0 },
59 		"Returns int32 indices of all items in the selection." },
60 	{ "Selection", { B_SET_PROPERTY, 0 }, { B_INDEX_SPECIFIER, B_REVERSE_INDEX_SPECIFIER,
61 		B_RANGE_SPECIFIER, B_REVERSE_RANGE_SPECIFIER, 0 },
62 		"Extends current selection or deselects specified items. Boolean field \"data\" "
63 		"chooses selection or deselection." },
64 	{ "Selection", { B_SET_PROPERTY, 0 }, { B_DIRECT_SPECIFIER, 0 },
65 		"Select or deselect all items in the selection. Boolean field \"data\" chooses "
66 		"selection or deselection." },
67 };
68 
69 //------------------------------------------------------------------------------
70 BListView::BListView(BRect frame, const char *name, list_view_type type,
71 					 uint32 resizingMode, uint32 flags)
72 	:	BView(frame, name, resizingMode, flags)
73 {
74 	_InitObject(type);
75 }
76 //------------------------------------------------------------------------------
77 BListView::BListView(BMessage *archive)
78 	:	BView(archive)
79 {
80 	int32 listType;
81 
82 	archive->FindInt32("_lv_type", &listType);
83 	fListType = (list_view_type)listType;
84 
85 	fFirstSelected = -1;
86 	fLastSelected = -1;
87 	fAnchorIndex = -1;
88 
89 	fSelectMessage = NULL;
90 	fScrollView = NULL;
91 	fTrack = NULL;
92 
93 	fWidth = Bounds().Width();
94 
95 	int32 i = 0;
96 	BMessage subData;
97 
98 	while (archive->FindMessage("_l_items", i++, &subData))
99 	{
100 		BArchivable *object = instantiate_object(&subData);
101 
102 		if (!object)
103 			continue;
104 
105 		BListItem *item = dynamic_cast<BListItem*>(object);
106 
107 		if (!item)
108 			continue;
109 
110 		AddItem(item);
111 	}
112 
113 	if (archive->HasMessage("_msg"))
114 	{
115 		BMessage *invokationMessage = new BMessage;
116 
117 		archive->FindMessage("_msg", invokationMessage);
118 		SetInvocationMessage(invokationMessage);
119 	}
120 
121 	if (archive->HasMessage("_2nd_msg"))
122 	{
123 		BMessage *selectionMessage = new BMessage;
124 
125 		archive->FindMessage("_2nd_msg", selectionMessage);
126 		SetSelectionMessage(selectionMessage);
127 	}
128 }
129 //------------------------------------------------------------------------------
130 BListView::~BListView()
131 {
132 	SetSelectionMessage(NULL);
133 
134 	if (fSelectMessage)
135 		delete fSelectMessage;
136 }
137 //------------------------------------------------------------------------------
138 BArchivable *BListView::Instantiate(BMessage *archive)
139 {
140 	if (validate_instantiation(archive, "BListView"))
141 		return new BListView(archive);
142 	else
143 		return NULL;
144 }
145 //------------------------------------------------------------------------------
146 status_t BListView::Archive(BMessage *archive, bool deep) const
147 {
148 	BView::Archive ( archive, deep );
149 
150 	archive->AddInt32("_lv_type", fListType);
151 
152 	if (deep)
153 	{
154 		int32 i = 0;
155 		BListItem *item;
156 
157 		while ((item = ItemAt(i++)))
158 		{
159 			BMessage subData;
160 
161 			if (item->Archive(&subData, true) != B_OK)
162 				continue;
163 
164 			archive->AddMessage("_l_items", &subData);
165 		}
166 	}
167 
168 	if (InvocationMessage())
169 		archive->AddMessage("_msg", InvocationMessage());
170 
171 	if (fSelectMessage)
172 		archive->AddMessage("_2nd_msg", fSelectMessage);
173 
174 	return B_OK;
175 }
176 
177 // Draw
178 void
179 BListView::Draw(BRect updateRect)
180 {
181 	for (int i = 0; i < CountItems(); i++) {
182 		BRect item_frame = ItemFrame(i);
183 
184 		if (item_frame.Intersects(updateRect))
185 			DrawItem(((BListItem*)ItemAt(i)), item_frame);
186 	}
187 }
188 
189 // MessageReceived
190 void
191 BListView::MessageReceived(BMessage* msg)
192 {
193 	switch (msg->what) {
194 		case B_COUNT_PROPERTIES:
195 		case B_EXECUTE_PROPERTY:
196 		case B_GET_PROPERTY:
197 		case B_SET_PROPERTY:
198 		{
199 			BPropertyInfo propInfo ( prop_list );
200 			BMessage specifier;
201 			const char *property;
202 
203 			if ( msg->GetCurrentSpecifier ( NULL, &specifier ) != B_OK ||
204 				specifier.FindString ( "property", &property ) != B_OK )
205 				return;
206 
207 			switch ( propInfo.FindMatch ( msg, 0, &specifier, msg->what, property ) )
208 			{
209 				case B_ERROR:
210 					BView::MessageReceived ( msg );
211 					break;
212 
213 				case 0:
214 				{
215 					BMessage reply ( B_REPLY );
216 
217 					reply.AddInt32 ( "result", CountItems () );
218 					reply.AddInt32 ( "error", B_OK );
219 
220 					msg->SendReply ( &reply );
221 					break;
222 				}
223 				case 1:
224 					break;
225 				case 2:
226 				{
227 					BMessage reply ( B_REPLY );
228 
229 					int32 count = 0;
230 
231 					for ( int32 i = 0; i < CountItems (); i++ )
232 						if ( ItemAt ( i )->IsSelected () )
233 							count++;
234 
235 					reply.AddInt32 ( "result", count );
236 					reply.AddInt32 ( "error", B_OK );
237 
238 					msg->SendReply ( &reply );
239 					break;
240 				}
241 				case 3:
242 					break;
243 				case 4:
244 				{
245 					BMessage reply ( B_REPLY );
246 
247 					for ( int32 i = 0; i < CountItems (); i++ )
248 						if ( ItemAt ( i )->IsSelected () )
249 							reply.AddInt32 ( "result", i );
250 
251 					reply.AddInt32 ( "error", B_OK );
252 
253 					msg->SendReply ( &reply );
254 					break;
255 				}
256 				case 5:
257 					break;
258 				case 6:
259 				{
260 					BMessage reply ( B_REPLY );
261 
262 					bool select;
263 
264 					msg->FindBool ( "data", &select );
265 
266 					if ( select )
267 						Select ( 0, CountItems () - 1, false );
268 					else
269 						DeselectAll ();
270 
271 					reply.AddInt32 ( "error", B_OK );
272 
273 					msg->SendReply ( &reply );
274 					break;
275 				}
276 			}
277 
278 			break;
279 		}
280 		case B_SELECT_ALL:
281 		{
282 			Select(0, CountItems() - 1, false);
283 			break;
284 		}
285 		default:
286 			BView::MessageReceived(msg);
287 	}
288 }
289 
290 // MouseDown
291 void
292 BListView::MouseDown(BPoint point)
293 {
294 	if (!IsFocus()) {
295 		MakeFocus();
296 		Sync();
297 		Window()->UpdateIfNeeded();
298 	}
299 
300 	BMessage *message = Looper()->CurrentMessage();
301 	int32 clicks;
302 	int32 modifiers;
303 
304 	message->FindInt32("clicks", &clicks);
305 	message->FindInt32("modifiers", &modifiers);
306 
307 	int index = IndexOf(point);
308 	if (index > -1) {
309 		if (fListType == B_MULTIPLE_SELECTION_LIST) {
310 			if (modifiers & B_CONTROL_KEY) {
311 				// select entire block
312 				// TODO: maybe review if we want it like in Tracker (anchor item)
313 				Select(min_c(index, fFirstSelected), max_c(index, fLastSelected));
314 			} else {
315 				if (modifiers & B_SHIFT_KEY) {
316 					// toggle selection state of clicked item (like in Tracker)
317 					// toggle selection state of clicked item
318 					if (clicks == 1) {
319 						if (ItemAt(index)->IsSelected())
320 							Deselect(index);
321 						else
322 							Select(index, true);
323 					}
324 				} else {
325 					Select(index);
326 				}
327 			}
328 		} else {
329 			// toggle selection state of clicked item
330 			if ((modifiers & B_SHIFT_KEY) && ItemAt(index)->IsSelected() && clicks == 1)
331 				Deselect(index);
332 			else
333 				Select(index);
334 		}
335 	} else {
336 		if (!(modifiers & B_SHIFT_KEY))
337 			DeselectAll();
338 	}
339 }
340 
341 // KeyDown
342 void
343 BListView::KeyDown(const char *bytes, int32 numBytes)
344 {
345 	switch (bytes[0]) {
346 		case B_UP_ARROW:
347 		{
348 			if (fFirstSelected == -1)
349 				break;
350 
351 			bool extend = false;
352 
353 			if (fListType == B_MULTIPLE_SELECTION_LIST && (modifiers() & B_SHIFT_KEY))
354 				extend = true;
355 
356 			Select (fFirstSelected - 1, extend);
357 
358 			ScrollToSelection ();
359 
360 			break;
361 		}
362 		case B_DOWN_ARROW:
363 		{
364 			if (fFirstSelected == -1)
365 				break;
366 
367 			bool extend = false;
368 
369 			if (fListType == B_MULTIPLE_SELECTION_LIST && (modifiers() & B_SHIFT_KEY))
370 				extend = true;
371 
372 			Select (fLastSelected + 1, extend);
373 
374 			ScrollToSelection ();
375 
376 			break;
377 		}
378 		case B_HOME:
379 		{
380 			Select ( 0, fListType == B_MULTIPLE_SELECTION_LIST );
381 
382 			ScrollToSelection ();
383 
384 			break;
385 		}
386 		case B_END:
387 		{
388 			Select ( CountItems () - 1, fListType == B_MULTIPLE_SELECTION_LIST );
389 
390 			ScrollToSelection ();
391 
392 			break;
393 		}
394 		case B_RETURN:
395 		case B_SPACE:
396 		{
397 			Invoke ();
398 
399 			break;
400 		}
401 		default:
402 			BView::KeyDown ( bytes, numBytes );
403 	}
404 }
405 
406 // MakeFocus
407 void
408 BListView::MakeFocus(bool focused)
409 {
410 	if (IsFocus() == focused)
411 		return;
412 
413 	BView::MakeFocus(focused);
414 
415 	if (fScrollView)
416 		fScrollView->SetBorderHighlighted(focused);
417 }
418 
419 // FrameResized
420 void
421 BListView::FrameResized(float width, float height)
422 {
423 	// TODO
424 	_FixupScrollBar();
425 }
426 
427 // TargetedByScrollView
428 void
429 BListView::TargetedByScrollView(BScrollView *view)
430 {
431 	fScrollView = view;
432 }
433 
434 // ScrollTo
435 void
436 BListView::ScrollTo(BPoint point)
437 {
438 	BView::ScrollTo(point);
439 	fWidth = Bounds().right;
440 }
441 
442 // AddItem
443 bool
444 BListView::AddItem(BListItem *item, int32 index)
445 {
446 	if (!fList.AddItem(item, index))
447 		return false;
448 
449 	if (fFirstSelected != -1 && index < fFirstSelected)
450 		fFirstSelected++;
451 
452 	if (fLastSelected != -1 && index < fLastSelected)
453 		fLastSelected++;
454 
455 	if (Window()) {
456 		BFont font;
457 		GetFont(&font);
458 
459 		item->Update(this, &font);
460 
461 		_FixupScrollBar();
462 		_InvalidateFrom(index);
463 	}
464 
465 	return true;
466 }
467 
468 // AddItem
469 bool
470 BListView::AddItem(BListItem* item)
471 {
472 	if (!fList.AddItem(item))
473 		return false;
474 
475 	if (Window()) {
476 		BFont font;
477 		GetFont(&font);
478 
479 		item->Update(this, &font);
480 
481 		_FixupScrollBar();
482 		InvalidateItem(CountItems() - 1);
483 	}
484 
485 	return true;
486 }
487 
488 // AddList
489 bool
490 BListView::AddList(BList* list, int32 index)
491 {
492 	if (!fList.AddList(list, index))
493 		return false;
494 
495 	int32 count = fList.CountItems();
496 
497 	if (fFirstSelected != -1 && index < fFirstSelected)
498 		fFirstSelected += count;
499 
500 	if (fLastSelected != -1 && index < fLastSelected)
501 		fLastSelected += count;
502 
503 	if (Window()) {
504 		BFont font;
505 		GetFont(&font);
506 
507 		int32 i = 0;
508 		while(BListItem *item = (BListItem*)list->ItemAt(i)) {
509 			item->Update(this, &font);
510 			i++;
511 		}
512 
513 		_FixupScrollBar();
514 		Invalidate(); // TODO
515 	}
516 
517 	return true;
518 }
519 
520 // AddList
521 bool
522 BListView::AddList(BList* list)
523 {
524 	return AddList(list, CountItems());
525 }
526 
527 // RemoveItem
528 BListItem*
529 BListView::RemoveItem(int32 index)
530 {
531 	BListItem *item = ItemAt(index);
532 
533 	if (!item)
534 		return NULL;
535 
536 	if (item->IsSelected())
537 		Deselect(index);
538 
539 	if (!fList.RemoveItem(item))
540 		return NULL;
541 
542 	if (fFirstSelected != -1 && index < fFirstSelected)
543 		fFirstSelected--;
544 
545 	if (fLastSelected != -1 && index < fLastSelected)
546 		fLastSelected--;
547 
548 	_InvalidateFrom(index);
549 	_FixupScrollBar();
550 
551 	return item;
552 }
553 
554 // RemoveItem
555 bool
556 BListView::RemoveItem(BListItem *item)
557 {
558 	return RemoveItem(IndexOf(item)) != NULL;
559 }
560 
561 // RemoveItems
562 bool
563 BListView::RemoveItems(int32 index, int32 count)
564 {
565 	if (index >= CountItems())
566 		index = -1;
567 
568 	if (index < 0)
569 		return false;
570 
571 	// TODO: very bad for performance!!
572 	while (count--)
573 		RemoveItem(index);
574 
575 	return true;
576 }
577 
578 // SetSelectionMessage
579 void
580 BListView::SetSelectionMessage(BMessage* message)
581 {
582 	delete fSelectMessage;
583 	fSelectMessage = message;
584 }
585 
586 // SetInvocationMessage
587 void
588 BListView::SetInvocationMessage(BMessage* message)
589 {
590 	BInvoker::SetMessage(message);
591 }
592 
593 // InvocationMessage
594 BMessage*
595 BListView::InvocationMessage() const
596 {
597 	return BInvoker::Message();
598 }
599 
600 // InvocationCommand
601 uint32
602 BListView::InvocationCommand() const
603 {
604 	return BInvoker::Command();
605 }
606 
607 // SelectionMessage
608 BMessage*
609 BListView::SelectionMessage() const
610 {
611 	return fSelectMessage;
612 }
613 
614 // SelectionCommand
615 uint32
616 BListView::SelectionCommand() const
617 {
618 	if (fSelectMessage)
619 		return fSelectMessage->what;
620 	else
621 		return 0;
622 }
623 
624 // SetListType
625 void
626 BListView::SetListType(list_view_type type)
627 {
628 	if (fListType == B_MULTIPLE_SELECTION_LIST &&
629 		type == B_SINGLE_SELECTION_LIST)
630 		Select(CurrentSelection(0));
631 
632 	fListType = type;
633 }
634 //------------------------------------------------------------------------------
635 list_view_type BListView::ListType() const
636 {
637 	return fListType;
638 }
639 
640 // ItemAt
641 BListItem*
642 BListView::ItemAt(int32 index) const
643 {
644 	return (BListItem*)fList.ItemAt(index);
645 }
646 
647 // IndexOf
648 int32
649 BListView::IndexOf(BListItem *item) const
650 {
651 	return fList.IndexOf(item);
652 }
653 
654 // IndexOf
655 int32
656 BListView::IndexOf(BPoint point) const
657 {
658 	float y = 0.0f;
659 
660 	// TODO: somehow binary search, but items don't know their frame
661 	for (int i = 0; i < fList.CountItems(); i++) {
662 		y += ItemAt(i)->Height();
663 
664 		if (point.y < y)
665 			return i;
666 	}
667 
668 	return -1;
669 }
670 
671 // FirstItem
672 BListItem*
673 BListView::FirstItem() const
674 {
675 	return (BListItem*)fList.FirstItem();
676 }
677 
678 // LastItem
679 BListItem*
680 BListView::LastItem() const
681 {
682 	return (BListItem*)fList.LastItem();
683 }
684 
685 // HasItem
686 bool
687 BListView::HasItem(BListItem *item) const
688 {
689 	return fList.HasItem(item);
690 }
691 
692 // CountItems
693 int32
694 BListView::CountItems() const
695 {
696 	return fList.CountItems();
697 }
698 
699 // MakeEmpty
700 void
701 BListView::MakeEmpty()
702 {
703 	_DeselectAll(-1, -1);
704 	fList.MakeEmpty();
705 
706 	Invalidate();
707 }
708 
709 // IsEmpty
710 bool
711 BListView::IsEmpty() const
712 {
713 	return fList.IsEmpty();
714 }
715 
716 // DoForEach
717 void
718 BListView::DoForEach(bool (*func)(BListItem*))
719 {
720 	fList.DoForEach(reinterpret_cast<bool (*)(void*)>(func));
721 }
722 
723 // DoForEach
724 void
725 BListView::DoForEach(bool (*func)(BListItem*, void*), void* arg )
726 {
727 	fList.DoForEach(reinterpret_cast<bool (*)(void*, void*)>(func), arg);
728 }
729 
730 // Items
731 const BListItem**
732 BListView::Items() const
733 {
734 	return (const BListItem**)fList.Items();
735 }
736 
737 // InvalidateItem
738 void
739 BListView::InvalidateItem(int32 index)
740 {
741 	Invalidate(ItemFrame(index));
742 }
743 
744 // ScrollToSelection
745 void
746 BListView::ScrollToSelection()
747 {
748 	BRect item_frame = ItemFrame ( CurrentSelection ( 0 ) );
749 
750 	if ( Bounds ().Intersects ( item_frame.InsetByCopy ( 0.0f, 2.0f ) ) )
751 		return;
752 
753 	if ( item_frame.top < Bounds ().top )
754 		ScrollTo ( 0, item_frame.top );
755 	else
756 		ScrollTo ( 0, item_frame.bottom - Bounds ().Height () );
757 }
758 
759 // Select
760 void
761 BListView::Select(int32 index, bool extend)
762 {
763 	_Select(index, extend);
764 
765 	SelectionChanged();
766 	InvokeNotify(fSelectMessage, B_CONTROL_MODIFIED);
767 }
768 
769 // Select
770 void
771 BListView::Select(int32 start, int32 finish, bool extend)
772 {
773 	_Select(start, finish, extend);
774 
775 	SelectionChanged();
776 	InvokeNotify(fSelectMessage, B_CONTROL_MODIFIED);
777 }
778 
779 // IsItemSelected
780 bool
781 BListView::IsItemSelected(int32 index) const
782 {
783 	BListItem *item = ItemAt(index);
784 
785 	if (item)
786 		return item->IsSelected();
787 	else
788 		return false;
789 }
790 
791 // CurrentSelection
792 int32
793 BListView::CurrentSelection(int32 index) const
794 {
795 	if (fFirstSelected == -1)
796 		return -1;
797 
798 	if (index == 0)
799 		return fFirstSelected;
800 
801 	for (int32 i = fFirstSelected; i <= fLastSelected; i++) {
802 		if (ItemAt(i)->IsSelected()) {
803 			if (index == 0)
804 				return i;
805 
806 			index--;
807 		}
808 	}
809 
810 	return -1;
811 }
812 
813 // Invoke
814 status_t
815 BListView::Invoke(BMessage *message)
816 {
817 	bool notify = false;
818 	uint32 kind = InvokeKind(&notify);
819 
820 	BMessage clone(kind);
821 	status_t err = B_BAD_VALUE;
822 
823 	if (!message && !notify)
824 		message = Message();
825 
826 	if (!message) {
827 		if (!IsWatched())
828 			return err;
829 	} else
830 		clone = *message;
831 
832 	clone.AddInt64("when", (int64)system_time());
833 	clone.AddPointer("source", this);
834 	clone.AddMessenger("be:sender", BMessenger(this));
835 
836 	if (fListType == B_SINGLE_SELECTION_LIST)
837 		clone.AddInt32("index", fFirstSelected);
838 	else {
839 		for (int32 i = fFirstSelected; i <= fLastSelected; i++) {
840 			if (ItemAt(i)->IsSelected())
841 				clone.AddInt32("index", i);
842 		}
843 	}
844 
845 	if (message)
846 		err = BInvoker::Invoke(&clone);
847 
848 //	TODO: assynchronous messaging
849 //	SendNotices(kind, &clone);
850 
851 	return err;
852 }
853 
854 // DeselectAll
855 void
856 BListView::DeselectAll()
857 {
858 	if (fFirstSelected == -1)
859 		return;
860 
861     for (int32 index = fFirstSelected; index <= fLastSelected; index++) {
862 		if (ItemAt(index)->IsSelected()) {
863 			ItemAt(index)->Deselect();
864 			InvalidateItem(index);
865 		}
866 	}
867 	fFirstSelected = fLastSelected = -1;
868 
869 	SelectionChanged();
870 	InvokeNotify(fSelectMessage, B_CONTROL_MODIFIED);
871 }
872 
873 // DeselectExcept
874 void
875 BListView::DeselectExcept(int32 start, int32 finish)
876 {
877 	if (fFirstSelected == -1 || finish < start)
878 		return;
879 
880 	int32 index;
881 
882 	// TODO: check if the items from start to finish are
883 	// supposed to be selected if not already
884     for (index = fFirstSelected; index < start; index++) {
885 		if (ItemAt(index)->IsSelected()) {
886 			ItemAt(index)->Deselect();
887 			InvalidateItem(index);
888 		}
889     }
890     for (index = finish + 1; index <= fLastSelected; index++) {
891 		if (ItemAt(index)->IsSelected()) {
892 			ItemAt(index)->Deselect();
893 			InvalidateItem(index);
894 		}
895 	}
896 	fFirstSelected = max_c(fFirstSelected, start);
897 	fLastSelected = min_c(fLastSelected, finish);
898 
899 	SelectionChanged();
900 	InvokeNotify(fSelectMessage, B_CONTROL_MODIFIED);
901 }
902 
903 // Deselect
904 void
905 BListView::Deselect(int32 index)
906 {
907 	if (_Deselect(index)) {
908 		SelectionChanged();
909 		InvokeNotify(fSelectMessage, B_CONTROL_MODIFIED);
910 	}
911 }
912 
913 // SelectionChanged
914 void BListView::SelectionChanged()
915 {
916 }
917 
918 // SortItems
919 void
920 BListView::SortItems(int (*cmp)(const void *, const void *))
921 {
922 	if (_DeselectAll(-1, -1)) {
923 		SelectionChanged();
924 		InvokeNotify(fSelectMessage, B_CONTROL_MODIFIED);
925 	}
926 
927 	fList.SortItems(cmp);
928 	Invalidate();
929 }
930 
931 // SwapItems
932 bool
933 BListView::SwapItems(int32 a, int32 b)
934 {
935 	MiscData data;
936 
937 	data.swap.a = a;
938 	data.swap.b = b;
939 
940 	return DoMiscellaneous(B_SWAP_OP, &data);
941 }
942 
943 // MoveItem
944 bool
945 BListView::MoveItem(int32 from, int32 to)
946 {
947 	MiscData data;
948 
949 	data.move.from = from;
950 	data.move.to = to;
951 
952 	return DoMiscellaneous(B_MOVE_OP, &data);
953 }
954 
955 // ReplaceItem
956 bool
957 BListView::ReplaceItem(int32 index, BListItem *item)
958 {
959 	MiscData data;
960 
961 	data.replace.index = index;
962 	data.replace.item = item;
963 
964 	return DoMiscellaneous(B_REPLACE_OP, &data);
965 }
966 
967 // AttachedToWindow
968 void
969 BListView::AttachedToWindow()
970 {
971 	BView::AttachedToWindow();
972 	_FontChanged();
973 
974 	if (!Messenger().IsValid())
975 		SetTarget(Window(), NULL);
976 
977 	_FixupScrollBar();
978 }
979 
980 // FrameMoved
981 void
982 BListView::FrameMoved(BPoint new_position)
983 {
984 	BView::FrameMoved(new_position);
985 }
986 
987 // ItemFrame
988 BRect
989 BListView::ItemFrame(int32 index)
990 {
991 	BRect frame(0, 0, Bounds().Width(), -1);
992 
993 	if (index < 0 || index >= CountItems())
994 		return frame;
995 
996 	for (int32 i = 0; i <= index; i++) {
997 		frame.top = frame.bottom + 1;
998 		frame.bottom += (float)ceil(ItemAt(i)->Height());
999 	}
1000 
1001 	return frame;
1002 }
1003 
1004 // ResolveSpecifier
1005 BHandler*
1006 BListView::ResolveSpecifier(BMessage *msg, int32 index,
1007 									  BMessage *specifier, int32 form,
1008 									  const char *property)
1009 {
1010 	BPropertyInfo propInfo(prop_list);
1011 
1012 	if (propInfo.FindMatch(msg, 0, specifier, form, property) < 0)
1013 		return BView::ResolveSpecifier(msg, index, specifier, form, property);
1014 
1015 	// TODO: msg->AddInt32("_match_code_", );
1016 
1017 	return this;
1018 }
1019 
1020 // GetSupportedSuites
1021 status_t
1022 BListView::GetSupportedSuites(BMessage *data )
1023 {
1024 	BPropertyInfo propertyInfo(prop_list);
1025 
1026 	data->AddString("suites", "suite/vnd.Be-list-view");
1027 	data->AddFlat("messages", &propertyInfo);
1028 
1029 	return BView::GetSupportedSuites(data);
1030 }
1031 
1032 // Perform
1033 status_t
1034 BListView::Perform(perform_code d, void *arg)
1035 {
1036 	return BView::Perform(d, arg);
1037 }
1038 
1039 // WindowActivated
1040 void
1041 BListView::WindowActivated(bool state)
1042 {
1043 	BView::WindowActivated(state);
1044 
1045 	if (IsFocus()) {
1046 		Invalidate();
1047 	}
1048 }
1049 
1050 // MouseUp
1051 void
1052 BListView::MouseUp(BPoint pt)
1053 {
1054 	if (fWidth == 0)
1055 		return;
1056 
1057 	DoMouseMoved(pt);
1058 	DoMouseUp(pt);
1059 }
1060 
1061 // MouseMoved
1062 void
1063 BListView::MouseMoved(BPoint pt, uint32 code, const BMessage *msg)
1064 {
1065 	if (fTrack == NULL)
1066 		return;
1067 
1068 	if (_TryInitiateDrag(pt))
1069 		return;
1070 
1071 	DoMouseMoved(pt);
1072 }
1073 
1074 // DetachedFromWindow
1075 void
1076 BListView::DetachedFromWindow()
1077 {
1078 	BView::DetachedFromWindow();
1079 }
1080 
1081 // InitiateDrag
1082 bool
1083 BListView::InitiateDrag(BPoint point, int32 index, bool wasSelected)
1084 {
1085 	return false;
1086 }
1087 
1088 // ResizeToPreferred
1089 void
1090 BListView::ResizeToPreferred()
1091 {
1092 	BView::ResizeToPreferred();
1093 }
1094 
1095 // GetPreferredSize
1096 void
1097 BListView::GetPreferredSize(float *width, float *height)
1098 {
1099 	BView::GetPreferredSize(width, height);
1100 }
1101 
1102 // AllAttached
1103 void
1104 BListView::AllAttached()
1105 {
1106 	BView::AllAttached();
1107 }
1108 
1109 // AllDetached
1110 void
1111 BListView::AllDetached()
1112 {
1113 	BView::AllDetached();
1114 }
1115 
1116 // DoMiscellaneous
1117 bool
1118 BListView::DoMiscellaneous(MiscCode code, MiscData *data)
1119 {
1120 	if (code > B_SWAP_OP)
1121 		return false;
1122 
1123 	switch (code)
1124 	{
1125 		case B_NO_OP:
1126 		{
1127 			break;
1128 		}
1129 		case B_REPLACE_OP:
1130 		{
1131 			return ReplaceItem(data->replace.index, data->replace.item);
1132 		}
1133 		case B_MOVE_OP:
1134 		{
1135 			return MoveItem(data->move.from, data->move.to);
1136 		}
1137 		case B_SWAP_OP:
1138 		{
1139 			return SwapItems(data->swap.a, data->swap.b);
1140 		}
1141 	}
1142 
1143 	return false;
1144 }
1145 
1146 void BListView::_ReservedListView2() {}
1147 void BListView::_ReservedListView3() {}
1148 void BListView::_ReservedListView4() {}
1149 
1150 BListView &BListView::operator=(const BListView &)
1151 {
1152 	return *this;
1153 }
1154 
1155 // _InitObject
1156 void
1157 BListView::_InitObject(list_view_type type)
1158 {
1159 	fListType = type;
1160 	fFirstSelected = -1;
1161 	fLastSelected = -1;
1162 	fAnchorIndex = -1;
1163 	fWidth = Bounds().Width();
1164 	fSelectMessage = NULL;
1165 	fScrollView = NULL;
1166 	fTrack = NULL;
1167 }
1168 
1169 // _FixupScrollBar
1170 void
1171 BListView::_FixupScrollBar()
1172 {
1173 	BScrollBar* vertScroller = ScrollBar(B_VERTICAL);
1174 
1175 	if (!vertScroller)
1176 		return;
1177 
1178 	BRect bounds = Bounds();
1179 	int32 count = CountItems();
1180 
1181 	float itemHeight = 0;
1182 	for (int32 i = 0; BListItem* item = ItemAt(i); i++) {
1183 		itemHeight += item->Height();
1184 	}
1185 
1186 	if (bounds.Height() > itemHeight) {
1187 		vertScroller->SetRange(0.0, 0.0);
1188 		vertScroller->SetValue(0.0);
1189 	} else {
1190 		// TODO: what about removing items from a scrolled
1191 		// list view? Doesn't "value" have to be adjusted?
1192 		vertScroller->SetRange(0.0, itemHeight - bounds.Height() - 1.0);
1193 		vertScroller->SetProportion(bounds.Height () / itemHeight);
1194 	}
1195 
1196 	if (count != 0) {
1197 		vertScroller->SetSteps((float)ceil(FirstItem()->Height()),
1198 			bounds.Height());
1199 	}
1200 }
1201 
1202 // _InvalidateFrom
1203 void
1204 BListView::_InvalidateFrom(int32 index)
1205 {
1206 	// make sure index is behind last valid index
1207 	int32 count = CountItems();
1208 	if (index >= count) {
1209 		index = count;
1210 	}
1211 	// take the item before the wanted one,
1212 	// because that might already be removed
1213 	index--;
1214 	BRect dirty = Bounds();
1215 	if (index >= 0) {
1216 		dirty.top = ItemFrame(index).bottom + 1;
1217 	}
1218 	Invalidate(dirty);
1219 }
1220 
1221 // _FontChanged
1222 void
1223 BListView::_FontChanged()
1224 {
1225 	BFont font;
1226 	GetFont(&font);
1227 
1228 	for (int i = 0; i < CountItems (); i ++)
1229 		ItemAt(i)->Update(this, &font);
1230 }
1231 
1232 // _Select
1233 bool
1234 BListView::_Select(int32 index, bool extend)
1235 {
1236 	if (index < 0 || index >= CountItems())
1237 		return false;
1238 
1239     if ((fFirstSelected != -1) && (!extend))
1240     {
1241 		for (int32 item = fFirstSelected; item <= fLastSelected; ++item)
1242 		{
1243 			if ((ItemAt(item)->IsSelected()) && (item != index))
1244 			{
1245 				ItemAt(item)->Deselect();
1246 				InvalidateItem(item);
1247 			}
1248 		}
1249 
1250 		fFirstSelected = -1;
1251 	}
1252 
1253     if (fFirstSelected == -1)
1254     {
1255 		fFirstSelected = index;
1256 		fLastSelected = index;
1257 	}
1258     else if (index < fFirstSelected)
1259 		fFirstSelected = index;
1260     else if (index > fLastSelected)
1261 		fLastSelected = index;
1262 
1263     if (!ItemAt(index)->IsSelected())
1264     {
1265 		ItemAt(index)->Select();
1266 		InvalidateItem(index);
1267     }
1268 
1269 	return true;
1270 }
1271 
1272 // _Select
1273 bool
1274 BListView::_Select(int32 from, int32 to, bool extend)
1275 {
1276 	if (to < from)
1277 		return false;
1278 
1279 	if ((fFirstSelected != -1) && (!extend))
1280     {
1281 		for (int32 item = fFirstSelected; item <= fLastSelected; ++item)
1282 		{
1283 			if ((ItemAt(item)->IsSelected()) && (item < from || item > to))
1284 			{
1285 				ItemAt(item)->Deselect();
1286 				InvalidateItem(item);
1287 			}
1288 		}
1289 
1290 		fFirstSelected = -1;
1291 	}
1292 
1293 	if (fFirstSelected == -1)
1294     {
1295 		fFirstSelected = from;
1296 		fLastSelected = to;
1297 	}
1298     else if (from < fFirstSelected)
1299 		fFirstSelected = from;
1300     else if (to > fLastSelected)
1301 		fLastSelected = to;
1302 
1303 	for (int32 item = from; item <= to; ++item)
1304 	{
1305 		if (!ItemAt(item)->IsSelected())
1306 		{
1307 			ItemAt(item)->Select();
1308 			InvalidateItem(item);
1309 		}
1310 	}
1311 
1312 	return true;
1313 }
1314 
1315 // _Deselect
1316 bool
1317 BListView::_Deselect(int32 index)
1318 {
1319 	if (index < 0 || index >= CountItems())
1320 		return false;
1321 
1322 	if (!Window()->Lock())
1323 		return false;
1324 
1325 	BListItem *item = ItemAt(index);
1326 
1327 	if (item->IsSelected())
1328 	{
1329 		BRect frame(ItemFrame(index));
1330 		BRect bounds(Bounds());
1331 
1332 		item->Deselect();
1333 
1334 		if (fFirstSelected == index && fLastSelected == index)
1335 		{
1336 			fFirstSelected = -1;
1337 			fLastSelected = -1;
1338 		}
1339 		else
1340 		{
1341 			if (fFirstSelected == index)
1342 				fFirstSelected = _CalcFirstSelected(index);
1343 
1344 			if (fLastSelected == index)
1345 				fLastSelected = _CalcLastSelected(index);
1346 		}
1347 
1348 		if (bounds.Intersects(frame))
1349 			DrawItem(ItemAt(index), frame, true);
1350 	}
1351 
1352 	Window()->Unlock();
1353 
1354 	return true;
1355 }
1356 /*
1357 // _Deselect
1358 void
1359 BListView::_Deselect(int32 from, int32 to)
1360 {
1361 	if (from < 0 || from >= CountItems() || to < 0 || to >= CountItems())
1362 		return;
1363 
1364 	bool changed = false;
1365 
1366 	for (int32 i = from; i <= to; i++)
1367 	{
1368 		if (_Deselect(i))
1369 			changed = true;
1370 	}
1371 
1372 	if (changed)
1373 	{
1374 		SelectionChanged();
1375 		InvokeNotify(fSelectMessage, B_CONTROL_MODIFIED);
1376 	}
1377 }
1378 */
1379 // _DeselectAll
1380 bool
1381 BListView::_DeselectAll(int32 except_from, int32 except_to)
1382 {
1383 	if (fFirstSelected == -1)
1384 		return true;
1385 	// TODO...
1386 
1387 	return true;
1388 }
1389 
1390 // _TryInitiateDrag
1391 bool
1392 BListView::_TryInitiateDrag(BPoint where)
1393 {
1394 	return false;
1395 }
1396 
1397 // _CalcFirstSelected
1398 int32
1399 BListView::_CalcFirstSelected(int32 after)
1400 {
1401 	if (after >= CountItems())
1402 		return -1;
1403 
1404 	for (int32 i = after; i < CountItems(); i++) {
1405 		if (ItemAt(i)->IsSelected())
1406 			return i;
1407 	}
1408 
1409 	return -1;
1410 }
1411 //------------------------------------------------------------------------------
1412 int32 BListView::_CalcLastSelected(int32 before)
1413 {
1414 	if (before < 0)
1415 		return -1;
1416 
1417 	for (int32 i = before; i >= 0; i--)
1418 	{
1419 		if (ItemAt(i)->IsSelected())
1420 			return i;
1421 	}
1422 
1423 	return -1;
1424 }
1425 //------------------------------------------------------------------------------
1426 void BListView::DrawItem(BListItem *item, BRect itemRect, bool complete)
1427 {
1428 	item->DrawItem(this, itemRect, complete);
1429 }
1430 //------------------------------------------------------------------------------
1431 bool BListView::DoSwapItems(int32 a, int32 b)
1432 {
1433 	if (!fList.SwapItems(a, b))
1434 		return false;
1435 
1436 	Invalidate(ItemFrame(a));
1437 	Invalidate(ItemFrame(b));
1438 
1439 	if (fAnchorIndex == a)
1440 		fAnchorIndex = b;
1441 	else if (fAnchorIndex == b)
1442 		fAnchorIndex = a;
1443 
1444 	RescanSelection(a, b);
1445 
1446 	return true;
1447 }
1448 //------------------------------------------------------------------------------
1449 bool BListView::DoMoveItem(int32 from, int32 to)
1450 {
1451 	BRect frameFrom = ItemFrame(from);
1452 	BRect frameTo = ItemFrame(to);
1453 
1454 	if (!fList.MoveItem(from, to))
1455 		return false;
1456 
1457 	RescanSelection(from, to);
1458 
1459 	BRect frame = frameFrom | frameTo;
1460 
1461 	if (Bounds().Intersects(frame))
1462 		Invalidate(Bounds() & frame);
1463 
1464 	return true;
1465 }
1466 //------------------------------------------------------------------------------
1467 bool BListView::DoReplaceItem(int32 index, BListItem *item)
1468 {
1469 	BRect frame = ItemFrame(index);
1470 
1471 	if (!fList.ReplaceItem(index, item))
1472 		return false;
1473 
1474 	if (frame != ItemFrame(index))
1475 		_InvalidateFrom(index);
1476 	else
1477 		Invalidate(frame);
1478 
1479 	return true;
1480 }
1481 //------------------------------------------------------------------------------
1482 void BListView::RescanSelection(int32 from, int32 to)
1483 {
1484 	if (from > to)
1485 	{
1486 		int32 tmp = from;
1487 		from = to;
1488 		to = tmp;
1489 	}
1490 
1491 	if (fAnchorIndex != -1)
1492 	{
1493 		if (fAnchorIndex == from)
1494 			fAnchorIndex = to;
1495 		else if (fAnchorIndex == to)
1496 			fAnchorIndex = from;
1497 	}
1498 
1499 /*	if (from < fFirstSelected && from < fLastSelected)
1500 		return;
1501 
1502 	if (to > fFirstSelected && to > fLastSelected)
1503 		return;*/
1504 
1505 	int32 i;
1506 
1507 	for (i = from; i <= to; i++)
1508 	{
1509 		if (ItemAt(i)->IsSelected())
1510 		{
1511 			fFirstSelected = i;
1512 			break;
1513 		}
1514 	}
1515 
1516 	for (i = from; i <= to; i++)
1517 		if (ItemAt(i)->IsSelected())
1518 			fLastSelected = i;
1519 }
1520 //------------------------------------------------------------------------------
1521 void BListView::DoMouseUp(BPoint where)
1522 {
1523 }
1524 //------------------------------------------------------------------------------
1525 void BListView::DoMouseMoved(BPoint where)
1526 {
1527 }
1528 //------------------------------------------------------------------------------
1529 
1530 /*
1531  * $Log $
1532  *
1533  * $Id  $
1534  *
1535  */
1536