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