xref: /haiku/src/kits/interface/ListView.cpp (revision 7120e97489acbf17d86d3f33e3b2e68974fd4b23)
1 ////////////////////////////////////////////////////////////////////////////////
2 //
3 //  File:           ListView.cpp
4 //
5 //  Description:    BListView represents a one-dimensional list view.
6 //
7 //  Copyright 2001, Ulrich Wimboeck
8 //
9 ////////////////////////////////////////////////////////////////////////////////
10 
11 #include "ListView.h"
12 #include <Region.h>
13 #include <Window.h>
14 #include <ClassInfo.h>
15 #include <iostream>
16 #include <ScrollView.h>
17 
18 
19 
20 struct track_data
21 {
22 } ;
23 
24 /**
25  * Initializes the new BListView. The frame, name, resizingMode, and flags
26  * arguments are identical to those declared for the BView class and are
27  * passed unchanged to the BView constructor.
28  *
29  * The list type can be either:
30  * B_SINGLE_SELECTION_LIST
31  * The user can select only one item in the list at a time. This is the
32  * default setting.
33  * B_MULTIPLE_SELECTION_LIST
34  * The user can select any number of items by holding down an Option
35  * key (for discontinuous selections) or a Shift key (for contiguous
36  * selections).
37  */
38 BListView::BListView(BRect frame, const char *name,
39                      list_view_type type /* = B_SINGLE_SELECTION_LIST */,
40                      uint32 resizeMask   /* = B_FOLLOW_LEFT | B_FOLLOW_TOP */,
41                      uint32 flags /* = B_WILL_DRAW | B_FRAME_EVENTS | B_NAVIGABLE */)
42  : BView(frame, name, resizeMask, flags)
43 {
44   InitObject(type) ;
45 }
46 
47 
48 BListView::BListView(BMessage* data)
49  : BView(data)
50 {
51   // if the pointer is null the object should be initialized anyway
52   if (data == NULL) {
53     InitObject(B_SINGLE_SELECTION_LIST) ;
54     return ;
55   }
56 
57   {
58     if (data->FindInt32("_lv_type", fListType) != B_OK)
59       fList = B_SINGLE_SELECTION_LIST ;
60 
61     // first check if a selection message is included in the messgage
62     // if there is one allocate memory for it and get it.
63     type_code code ;
64 
65     if (data->GetInfo("_2nd_msg", &code) == B_OK )
66     {
67       if (code == B_MESSAGE_TYPE)
68       {
69         fSelectMessage = new BMessage() ;
70         data->FindMessage("_msg", fSelectMessage) ;
71         BInvoker::SetMessage(fSelectMessage) ;
72 
73         // set the fSelectMessage back to NULL
74         fSelectMessage = NULL ;
75       }
76     }
77 
78     // check if there is an invokation message stored
79     // in the archiv and set it
80     if (data->GetInfo("_msg", &code) == B_OK )
81     {
82       if (code == B_MESSAGE_TYPE)
83       {
84         fSelectMessage = new BMessage() ;
85         data->FindMessage("_msg", fSelectMessage) ;
86       }
87       else {
88         fSelectMessage = NULL ;
89       }
90     }
91 
92     int32 count ;
93 
94     // add the item archivied in the message if it was a deep copy
95     if (data->GetInfo("_l_items", &code, &count) == B_OK)
96     {
97       if (code == B_MESSAGE_TYPE)
98       {
99         BMessage msg ;
100         BArchivable* unarchived ;
101         BListItem* item ;
102 
103         for (int32 i = 0 ; i < count ; ++i)
104         {
105           data->FindMessage("_l_items", i, &msg) ;
106           unarchived = instantiate_object(data) ;
107           item = dynamic_cast<BListItem*>(unarchived) ;
108 
109           if (item != NULL) {
110             AddItem(item) ;
111           }
112         }
113       }
114     }
115   }
116 }
117 
118 /**
119  * Frees the selection and invocation messages, if any, and any memory
120  * allocated to hold the list of items, but not the items themselves.
121  */
122 BListView::~BListView()
123 {
124   // Remove all pointers from the list
125   fList.MakeEmpty() ;
126 
127   // delete the select message if there is one
128   // the invocation message is deleted by the destructor of BInvoker
129   if (fSelectMessage != NULL)
130   {
131     delete fSelectMessage ;
132     fSelectMessage = NULL ;
133   }
134 
135   if (fTrack != NULL)
136   {
137     delete fTrack ;
138     fTrack = NULL ;
139   }
140 }
141 
142 /**
143  * Returns a new BListView object, allocated by new and created
144  * with the version of the constructor that takes a BMessage
145  * archive. However, if the archive message doesn't contain
146  * data for a BListView object, this function returns NULL.
147  *
148  * @param data Pointer to a BMessge object containing the data
149  *             of an archieved BListView object.
150  */
151 BArchivable*
152 BListView::Instantiate(BMessage* data)
153 {
154   if (validate_instantiation(data, "BListView"))
155     return new BListView(data) ;
156 
157   return NULL ;
158 }
159 
160 status_t
161 BListView::Archive(BMessage* data, bool deep /* = true */) const
162 {
163   if (!data)
164     return B_ERROR ;
165 
166   BView::Archive(data, deep) ;
167 
168   data->AddString("class", "BListView") ;
169   data->AddInt32("_lv_type", fListType) ;
170 
171   if (fSelectMessage) {
172     data->AddMessage("_msg", fSelectMessage) ;
173   }
174 
175   if (BInvoker::Message()) {
176     data->AddMessage("_2nd_msg", BInvoker::Message()) ;
177   }
178 
179   if (deep)
180   {
181     BMessage* msg ;
182 
183     for (int i = 0 ; i < fList.CountItems() ; ++i)
184     {
185       msg = new BMessage() ;
186       ItemAt(i)->Archive(msg, deep) ;
187       data->AddMessage("_l_items", msg) ;
188     }
189   }
190 
191   return B_OK ;
192 }
193 
194 /**
195  * Calls upon every item in the updateRect area of the view to draw itself.
196  * Draw() is called for you whenever the list view is to be updated or
197  * redisplayed; you don't need to call it yourself. You also don't need to
198  * reimplement it; to change the way items are drawn, define a new version
199  * of DrawItem() in a class derived from BListItem.
200  */
201 void
202 BListView::Draw(BRect updateRect)
203 {
204   SetHighColor(0, 0 ,0) ;
205 
206   if (!fList.IsEmpty())
207   {
208     // Find the first and the last item which has to be updated
209     float tmp = 0 ;
210     int32 firstIndex = 0 ;
211 
212     for ( ; firstIndex < fList.CountItems() ; ++firstIndex)
213     {
214       float help = (ItemAt(firstIndex))->Height() ;
215       tmp += help ;
216       if (tmp >= updateRect.top)
217         break ;
218     }
219 
220     int32 lastIndex = firstIndex ;
221 
222     for (; lastIndex < fList.CountItems() ; ++lastIndex)
223     {
224       //Draw the item
225       BRect rect(Bounds());
226       rect.top = tmp - ItemAt(lastIndex)->Height() ;
227       rect.bottom = tmp ;
228       rect.right = 2000 ;
229 
230 //      BRegion region ;
231   //    region.Include(rect) ;
232 
233 //      ConstrainClippingRegion(&region) ;
234       ItemAt(lastIndex)->DrawItem(this, rect);//, true) ;
235 
236       if (tmp >= updateRect.bottom)
237         break ;
238 
239       tmp += ItemAt(lastIndex)->Height() ;
240     }
241   }
242 }
243 
244 void
245 BListView::MessageReceived(BMessage *msg)
246 {
247   switch (msg->what)
248   {
249   // scripting stuff
250   case B_GET_SUPPORTED_SUITES:
251   {
252     BMessage reply(B_REPLY) ;
253     reply.AddString("suites", "suite/vnd.Be-list-view") ;
254     msg->SendReply(&reply) ;
255     break ;
256   }
257   case B_COUNT_PROPERTIES:
258   {
259     BMessage reply(B_REPLY) ;
260     reply.AddInt32("result", fList.CountItems()) ;
261     msg->SendReply(&reply) ;
262     break ;
263   }
264   case B_EXECUTE_PROPERTY:
265   break ;
266   case B_GET_PROPERTY:
267   break ;
268   case B_SET_PROPERTY:
269   break ;
270   case B_MOUSE_WHEEL_CHANGED:
271     break ;
272   }
273 
274   BView::MessageReceived(msg) ;
275 }
276 
277 /**
278  * Responds to B_MOUSE_DOWN messages by selecting items, invoking them
279  * (if the mouse-down event is the second of a double-click), and
280  * autoscrolling the list (when the user drags with a mouse button down).
281  * This function also calls InitiateDrag() to give derived classes the
282  * opportunity to drag items. You can implement that function; you shouldn't
283  * override (or call) this one.
284  */
285  // The docu says shift key and option key like Windows but it does not.
286 void
287 BListView::MouseDown(BPoint where)
288 {
289   int32 index = IndexOf(where) ;
290 
291   if (index >= 0)
292   {
293     bool extend = false ;
294 
295     if ((fListType == B_MULTIPLE_SELECTION_LIST) && (modifiers() & B_SHIFT_KEY))
296       extend = true ;
297 
298     if (!ItemAt(index)->IsSelected())
299       Select(index, extend) ;
300     else {
301       ItemAt(index)->Deselect() ;
302       InvalidateItem(index) ;
303     }
304   }
305 
306   MakeFocus() ;
307 }
308 
309 /**
310  * Permits the user to operate the list using the following keys:
311  *
312  * Up Arrow and Down Arrow
313  * Select the items that are immediately before and immediately after
314  * the currently selected item.
315  *
316  * Page Up and Page Down
317  * Select the items that are one viewful above and below the currently
318  * selected item—or the first and last items if there's no item a viewful away.
319  *
320  * Home and End
321  * Select the first and last items in the list.
322  *
323  * Enter and the space bar
324  * Invoke the current selection.
325  *
326  * This function also incorporates the inherited BView version so that the
327  * Tab key can navigate to another view. KeyDown() is called to report
328  * B_KEY_DOWN messages when the BListView is the focus view of the active
329  * window; you shouldn't call it yourself.
330  */
331 void
332 BListView::KeyDown(const char *bytes, int32 numBytes)
333 {
334   switch (*bytes)
335   {
336   // pressing the home key the first item in the list is selected
337   // and made visible
338   case B_HOME:
339   {
340     if (CountItems() > 0)
341     {
342       Select(0) ;
343       ScrollToSelection() ;
344     }
345     break;
346   }
347   // if the end key is pressed the last entry is selected and
348   // made visible
349   case B_END:
350   {
351     if (CountItems() > 0)
352     {
353       Select(CountItems() - 1) ;
354       ScrollToSelection() ;
355     }
356     break ;
357   }
358   // select the item above the first selected one
359   case B_UP_ARROW:
360     if (fFirstSelected > 0)
361     {
362       Select(fFirstSelected - 1) ;
363       ScrollToSelection() ;
364     }
365     break ;
366   // select the item below the last one
367   case B_DOWN_ARROW:
368     if ((fFirstSelected >= 0) && (fLastSelected < (fList.CountItems() - 1)))
369     {
370       Select(fLastSelected + 1) ;
371       ScrollToSelection() ;
372     }
373     break ;
374   case B_PAGE_UP:
375   {
376     BRect bounds(Bounds()) ;
377     BPoint top(bounds.left, bounds.top) ;
378     int32 index = IndexOf(top) ;
379 
380     if (index >= 0)
381     {
382       BRect frame(ItemFrame(index)) ;
383 
384       if ((frame.bottom - (bounds.bottom - bounds.top)) >= 0)
385         ScrollTo(bounds.left, frame.bottom - (bounds.bottom - bounds.top)) ;
386       else
387         ScrollTo(bounds.left, 0) ;
388     }
389 
390     break ;
391   }
392   case B_PAGE_DOWN:
393   {
394     BRect bounds(Bounds()) ;
395     BPoint bottom(bounds.left, bounds.bottom) ;
396     int32 index = IndexOf(bottom) ;
397 
398     if (index >= 0)
399     {
400       BRect frame(ItemFrame(index)) ;
401       float height ;
402       GetPreferredSize(NULL, &height) ;
403 
404       if ((bounds.bottom - bounds.top + frame.top) <= height)
405       {
406         ScrollTo(bounds.left, frame.top) ;
407       }
408       else {
409         ScrollTo(bounds.left, height - (bounds.bottom - bounds.top)) ;
410       }
411     }
412 
413     break;
414   }
415   // both, the space bar and the return key invoke the current selection
416   case B_SPACE:
417   case B_RETURN:
418   {
419     if (fFirstSelected >= 0)
420     {
421       Invoke() ;
422     }
423     break ;
424   }
425   }
426   BView::KeyDown(bytes, numBytes) ;
427 }
428 
429 /**
430  * Overrides the BView version of MakeFocus() to draw an indication that
431  * the BListView has become the focus for keyboard events when the
432  * focused flag is true, and to remove that indication when the flag is false.
433  */
434 void
435 BListView::MakeFocus(bool state /* = true */)
436 {
437   if (state != IsFocus())
438   {
439     BView::MakeFocus(state) ;
440   }
441 
442   // if the list view is target of a scroll view
443   // the border of the scroll view needs to be set to the
444   // correct state
445   if (fScrollView != NULL)
446   {
447     fScrollView->SetBorderHighlighted(state) ;
448   }
449 }
450 
451 /**
452  * Updates the on-screen display in response to a notification that
453  * the BListView's frame rectangle has been resized. In particular,
454  * this function looks for a vertical scroll bar that's a sibling of
455  * the BListView. It adjusts this scroll bar to reflect the way the
456  * list view was resized, under the assumption that it must have the
457  * BListView as its target.
458  */
459 void
460 BListView::FrameResized(float newWidth, float newHeight)
461 {
462   // set the range of the scroll bars of the scroll view
463   FixupScrollBar() ;
464 }
465 
466 void
467 BListView::TargetedByScrollView(BScrollView *scroller)
468 {
469 //  if (fScrollView != NULL)
470   {
471   //  delete fScrollView ;
472   }
473 
474   fScrollView = scroller ;
475 }
476 
477 void
478 BListView::ScrollTo(BPoint where)
479 {
480   BView::ScrollTo(where) ;
481 }
482 
483 /**
484  * Adds an item to the BListView at the end of the list. If necessary,
485  * additional memory is allocated to accommodate the new item.
486  * Adding an item never removes an item already in the list.
487  * If the supplied pointer is NULL the function returns false. Otherwise,
488  * it returns true.
489  */
490 bool
491 BListView::AddItem(BListItem *item)
492 {
493   // check if the supplied pointer is NULL
494   bool bReturn = (item != NULL) ;
495 
496   if (bReturn)
497   {
498     if (bReturn = fList.AddItem(static_cast<void*>(item)))
499     {
500       // If the view is already attached to a view the item needs to be updated
501       if (Parent() != NULL)
502       {
503         BFont font ;
504         GetFont(&font) ;
505         item->Update(this, &font) ;
506       }
507     }
508 
509     // The new item needs to be drawn if visible
510     InvalidateItem(fList.CountItems() - 1) ;
511   }
512 
513   return bReturn ;
514 }
515 
516 /**
517  * Adds an item to the BListView at index. If necessary, additional memory is
518  * allocated to accommodate the new item.
519  * Adding an item never removes an item already in the list. If the item is
520  * added at an index that's already occupied, items currently in the list are
521  * bumped down one slot to make room.
522  * If index is out of range (greater than the current item count, or less than zero), this function fails and returns false. Otherwise, it returns
523  * true
524 */
525 bool
526 BListView::AddItem(BListItem *item, int32 atIndex)
527 {
528   bool bReturn(item != NULL) ;
529 
530   // If successful invalidate all items starting at atIndex
531   if (bReturn)
532   {
533     if (bReturn = fList.AddItem(static_cast<void*>(item), atIndex))
534     {
535       if (Parent() != NULL)
536       {
537         BFont font ;
538         GetFont(&font) ;
539         item->Update(this, &font) ;
540       }
541     }
542 
543     // The new item needs to drawn if visible
544     InvalidateFrom(atIndex) ;
545   }
546 
547   return bReturn ;
548 }
549 
550 /**
551  * Adds the contents of another list to this BListView. The items from
552  * the BList are appended to the end of the list.
553  * If the supplied pointer or one of the pointers within the list is NULL,
554  * the function fails and returns false. If successful, it returns true.
555  * The BListView doesn't check to be sure that all the items it adds from the
556  * list are pointers to BListItem objects. It assumes that they are; if the
557  * assumption is false, the program will crash.
558  */
559 bool
560 BListView::AddList(BList* newItems)
561 {
562   bool bReturn(newItems != NULL) ;
563 
564   if (bReturn)
565   {
566     for (int32 index = 0 ; index < newItems->CountItems() ; ++index)
567     {
568       if (newItems->ItemAt(index) == NULL)
569       {
570         bReturn = false ;
571         break ;
572       }
573     }
574 
575     if (bReturn)
576     {
577       int32 index = CountItems() ;
578       fList.AddList(newItems) ;
579       InvalidateFrom(index) ;
580 
581       if (Parent() != NULL)
582       {
583         BFont font ;
584         GetFont(&font) ;
585 
586         for (int32 index = CountItems() - fList.CountItems() ;
587              index < CountItems() ; ++index)
588         {
589           ItemAt(index)->Update(this, &font) ;
590         }
591       }
592     }
593   }
594 
595   return bReturn ;
596 }
597 
598 /**
599  * Adds the contents of another list to this BListView. The items from the
600  * BList are inserted at index.
601  * If the supplied pointer or one of the pointers within the list is NULL or
602  * the index is out of range, the function, fails and returns false.
603  * If successful, it returns true.
604  * The BListView doesn't check to be sure that all the items it adds from
605  * the list are pointers to BListItem objects. It assumes that they are; if the
606  * assumption is false, the program will crash.
607  */
608 bool
609 BListView::AddList(BList *newItems, int32 atIndex)
610 {
611   bool bReturn(newItems != NULL) ;
612 
613   if (bReturn)
614   {
615     for (int32 index = 0 ; index < newItems->CountItems() ; ++index)
616     {
617       if (newItems->ItemAt(index) == NULL)
618       {
619         bReturn = false ;
620         break ;
621       }
622     }
623 
624     if (bReturn)
625     {
626       fList.AddList(newItems, atIndex) ;
627       InvalidateFrom(atIndex) ;
628 
629       if (Parent() != NULL)
630       {
631         BFont font ;
632         GetFont(&font) ;
633 
634         for (int32 index = CountItems() - fList.CountItems() ;
635              index < CountItems() ; ++index)
636         {
637           ItemAt(index)->Update(this, &font) ;
638         }
639       }
640     }
641   }
642 
643   return bReturn ;
644 }
645 
646 /**
647  * Removes a single item from the BListView. If passed an index,
648  * it removes the item at that index and returns it. If there's no
649  * item at the index, it returns NULL. If passed an item, this
650  * function looks for that particular item in the list, removes it,
651  * and returns true. If it can't find the item, it returns false.
652  * If the item is in the list more than once, this function removes
653  * only its first occurrence.
654  */
655 bool
656 BListView::RemoveItem(BListItem* item)
657 {
658   bool bReturn = false ;
659   // Remember the position of the item
660   int32 index = IndexOf(item) ;
661   bReturn = fList.RemoveItem(static_cast<void*>(item)) ;
662 
663   // update all items with a higher index than the removed item
664   if (bReturn)
665   {
666     for ( ; index < fList.CountItems() ; ++index)
667     {
668       InvalidateItem(index) ;
669     }
670   }
671 
672   return bReturn ;
673 }
674 
675 BListItem*
676 BListView::RemoveItem(int32 index)
677 {
678   BListItem* item = static_cast<BListItem*>(fList.RemoveItem(index)) ;
679 
680   // update all items with a higher index than the removed item
681   if (item != NULL)
682   {
683     for ( ; index < fList.CountItems() ; ++index)
684     {
685       InvalidateItem(index) ;
686     }
687   }
688 
689   return item ;
690 }
691 
692 bool
693 BListView::RemoveItems(int32 index, int32 count)
694 {
695   bool bReturn = fList.RemoveItems(index, count) ;
696 
697   // update all items with a higher index than the last removed item
698   if (bReturn)
699   {
700     for ( ; index < fList.CountItems() ; ++index)
701     {
702       InvalidateItem(index) ;
703     }
704   }
705 
706   return bReturn ;
707 }
708 
709 
710 /**
711  * These functions set, and return information about, the message that
712  * a BListView sends whenever a new item is selected. They're exact
713  * counterparts to the functions described above under SetInvocationMessage(),
714  * except that the selection message is sent whenever an item in the list
715  * is selected, rather than when invoked. It's more common to take action
716  * (to initiate a message) when invoking an item than when selecting one.
717  */
718 void
719 BListView::SetSelectionMessage(BMessage *message)
720 {
721   if (fSelectMessage != NULL)
722   {
723     delete fSelectMessage ;
724   }
725 
726   fSelectMessage = message ;
727 }
728 
729 BMessage*
730 BListView::SelectionMessage() const
731 {
732   return fSelectMessage ;
733 }
734 
735 uint32
736 BListView::SelectionCommand() const
737 {
738   if (fSelectMessage)
739     return fSelectMessage->what ;
740   else
741     return 0 ;
742 }
743 
744 void
745 BListView::SetInvocationMessage(BMessage *message)
746 {
747   BInvoker::SetMessage(message) ;
748 }
749 
750 /**
751  * These functions set and return information about the BMessage
752  * that the BListView sends when currently selected items are invoked.
753  * SetInvocationMessage() assigns message to the BListView, freeing
754  * any message previously assigned. The message becomes the responsibility
755  * of the BListView object and will be freed only when it's replaced by
756  * another message or the BListView is freed; you shouldn't free it
757  * yourself.
758  * Passing a NULL pointer to this function deletes the current message
759  * without replacing it.
760  * When sending the message, the Invoke() function makes a copy of it
761  * and adds two pieces of relevant information—"when" the message is
762  * sent and the "source" BListView. These names should not be used for
763  * any data that you add to the invocation message.
764  * InvocationMessage() returns a pointer to the BMessage and
765  * InvocationCommand() returns its what data member. The message
766  * belongs to the BListView; it can be altered by adding or removing
767  * data, but it shouldn't be deleted. To get rid of the current message,
768  * pass a NULL pointer to SetInvocationMessage().
769  */
770 BMessage*
771 BListView::InvocationMessage() const
772 {
773   return BInvoker::Message() ;
774 }
775 
776 
777 uint32
778 BListView::InvocationCommand() const
779 {
780   return BInvoker::Command() ;
781 }
782 
783 /**
784  * These functions set and return the list type—whether or not
785  * it permits multiple selections. The list_view_type must be either
786  * B_SINGLE_SELECTION_LIST or B_MULTIPLE_SELECTION_LIST. The type
787  * is first set when the BListView is constructed.
788  */
789 void
790 BListView::SetListType(list_view_type type)
791 {
792   if (type != fListType)
793   {
794     // When the type is changed from B_MULTIPLE_SELECTION_LIST to
795     // B_SINGLE_SELECTION_LIST all selected items will be deselcted.
796     if (fListType == B_MULTIPLE_SELECTION_LIST)
797     {
798       for (int32 item = fFirstSelected ; item <= fLastSelected ; ++item)
799       {
800         if (ItemAt(item)->IsSelected())
801         {
802           ItemAt(item)->Deselect() ;
803           InvalidateItem(item) ;
804         }
805       }
806     }
807 
808     fListType = type ;
809   }
810 }
811 
812 list_view_type
813 BListView::ListType() const
814 {
815   return fListType ;
816 }
817 
818 /**
819  * This function  returns the BListItem at index in the list, or NULL if the
820  * list is empty.
821  * The function does not alter the contents of the list—they don't remove the returned
822  * item.
823  */
824 BListItem*
825 BListView::ItemAt(int32 index) const
826 {
827   return static_cast<BListItem*>(fList.ItemAt(index)) ;
828 }
829 
830 /**
831  * Returns the index where a particular item whose display rectangle
832  * includes a particular point—is located in the list.
833  * To determine whether an item lies at the specified point, only
834  * the y-coordinate value of the point is considered.
835  * If the item isn't in the list or the y-coordinate of the point
836  * doesn't intersect with the data rectangle of the BListView, the return
837  * value will be a negative number.
838  */
839 int32
840 BListView::IndexOf(BPoint point) const
841 {
842   float tmp = 0 ;
843 
844   for (int32 index = 0 ; index < fList.CountItems() ; ++index)
845   {
846     tmp += ItemAt(index)->Height() ;
847 
848     if (point.y < tmp)
849     {
850       return index ;
851     }
852   }
853 
854   return -1 ;
855 }
856 
857 /**
858  * Returns the index where a particular item is located in the list.
859  * If the item is in the list more than once, the index returned will
860  * be the position of its first occurrence.
861  * If the item isn't in the list of the BListView, the return value
862  * will be a negative number.
863  */
864 int32
865 BListView::IndexOf(BListItem *item) const
866 {
867   return fList.IndexOf(static_cast<void*>(item)) ;
868 }
869 
870 
871 /**
872  * This function returns the very first in the list, or NULL if the list is empty.
873  * The function does not alter the contents of the list—they don't remove the returned
874  * item.
875  */
876 BListItem*
877 BListView::FirstItem() const
878 {
879   return static_cast<BListItem*>(fList.FirstItem()) ;
880 }
881 
882 /**
883  * This function returns the very last in the list, or NULL if the list is empty.
884  * The function does not alter the contents of the list—they don't remove the returned
885  * item.
886  */
887 
888 BListItem*
889 BListView::LastItem() const
890 {
891   return static_cast<BListItem*>(fList.FirstItem()) ;
892 }
893 
894 /**
895  * Returns true if item is in the list, and false if not.
896  */
897 bool
898 BListView::HasItem(BListItem *item) const
899 {
900   return fList.HasItem(static_cast<void*>(item)) ;
901 }
902 
903 /**
904  * Returns the number of BListItems currently in the list.
905  */
906 int32
907 BListView::CountItems() const
908 {
909   return fList.CountItems() ;
910 }
911 
912 /**
913  * MakeEmpty() empties the BListView of all its items, without freeing
914  * the BListItem objects.
915  */
916 void
917 BListView::MakeEmpty()
918 {
919   // Remove all items from the list
920   fList.MakeEmpty() ;
921   fFirstSelected = -1 ;
922 
923   // update the list view
924   Invalidate() ;
925 }
926 
927 /**
928  * IsEmpty() returns true if the list is empty (if it contains no items),
929  * and false otherwise.
930  */
931 bool
932 BListView::IsEmpty() const
933 {
934   return fList.IsEmpty() ;
935 }
936 
937 /**
938  * Calls the func function once for each item in the BListView.
939  * BListItems are visited in order, beginning with the first one in
940  * the list (index 0) and ending with the last.
941  * If a call to func returns true, the iteration is stopped, even
942  * if some items have not yet been visited.
943  */
944 void
945 BListView::DoForEach(bool (*func)(BListItem *))
946 {
947   fList.DoForEach(reinterpret_cast<bool (*)(void*)>(func)) ;
948 }
949 
950 /**
951  * Calls the func function once for each item in the BListView.
952  * BListItems are visited in order, beginning with the first one in
953  * the list (index 0) and ending with the last.
954  * If a call to func returns true, the iteration is stopped, even
955  * if some items have not yet been visited.
956  */
957 void
958 BListView::DoForEach(bool (*func)(BListItem *, void *), void* parameter)
959 {
960   fList.DoForEach(reinterpret_cast<bool (*)(void*, void*)>(func), parameter) ;
961 }
962 
963 /**
964  * Returns a pointer to the BListView's list of BListItems. You can index
965  * directly into the list of items if you're certain that the index is in range:
966  * BListItem *item = Items()[index] ;
967  *
968  * Although the practice is discouraged, you can also step through the list
969  * of items by incrementing the list pointer that Items() returns. Be aware
970  * that the list isn't null-terminated—you have to detect the end of the
971  * list by some other means. The simplest method is to count items:
972  *
973  * BListItem **ptr = myListView->Items() ;
974  *
975  * for ( long i = myListView->CountItems(); i > 0; i-- )
976  * {
977  *   . . .
978  *   *ptr++ ;
979  * }
980  *
981  * You should never use the items pointer to alter the contents of the list.
982  */
983 const BListItem**
984 BListView::Items() const
985 {
986   return reinterpret_cast<const BListItem**>(fList.Items()) ;
987 }
988 
989 /**
990  * Invalidates the item at index so that an update message will
991  * be sent forcing the BListView to redraw it.
992  */
993 void
994 BListView::InvalidateItem(int32 index)
995 {
996   if (index <= fList.CountItems())
997   {
998     // Invalidate the rectangle of the list item
999     Invalidate(Bounds() | ItemFrame(index)) ;
1000   }
1001 }
1002 
1003 void
1004 BListView::ScrollToSelection()
1005 {
1006   if (fFirstSelected != -1)
1007   {
1008     float tmp = 0 ;
1009     int32 index = 0 ;
1010 
1011     for ( ; index < fFirstSelected ; ++index)
1012     {
1013       tmp += ItemAt(index)->Height() ;
1014     }
1015 
1016     BRect rect (Bounds()) ;
1017 
1018     if (tmp < rect.top)
1019     {
1020       ScrollTo(0, tmp) ;
1021     }
1022     else if ((tmp + ItemAt(index)->Height()) > rect.bottom)
1023     {
1024       ScrollTo(0, tmp + ItemAt(index)->Height() - (rect.bottom - rect.top)) ;
1025     }
1026   }
1027 }
1028 
1029 /**
1030  * Selects the item at the index
1031  * if extend is false all former selections will be removed
1032  * if extend is true the
1033  */
1034 void
1035 BListView::Select(int32 index, bool extend /* = false */)
1036 {
1037   // The index must be in range
1038   if (index < fList.CountItems() && (index >= 0))
1039   {
1040     // Deselect all selected items
1041     if ((fFirstSelected != -1) && (!extend))
1042     {
1043       for (int32 item = fFirstSelected ; item <= fLastSelected ; ++item)
1044       {
1045         if ((ItemAt(item)->IsSelected()) && (item != index))
1046         {
1047           ItemAt(item)->Deselect() ;
1048           InvalidateItem(item) ;
1049         }
1050       }
1051 
1052       // No item is selected any more
1053       fFirstSelected = -1 ;
1054     }
1055 
1056     if (fFirstSelected == -1)
1057     {
1058       fFirstSelected = index ;
1059       fLastSelected = index ;
1060     }
1061     else if (index < fFirstSelected)
1062       fFirstSelected = index ;
1063     else if (index > fLastSelected)
1064       fLastSelected = index ;
1065 
1066     if (!ItemAt(index)->IsSelected())
1067     {
1068       ItemAt(index)->Select() ;
1069       InvalidateItem(index) ;
1070     }
1071 
1072     SelectionChanged() ;
1073   }
1074 }
1075 
1076 void
1077 BListView::Select(int32 from, int32 to, bool extend /* = false */)
1078 {
1079   if ((from <= to) && (to < fList.CountItems()))
1080   {
1081     // Deselect all current selected items
1082     if (!extend)
1083     {
1084     }
1085 
1086     for (int32 index = from ; index <= to ; ++index)
1087     {
1088       ItemAt(index)->Select() ;
1089     }
1090   }
1091 }
1092 
1093 /**
1094  * Returns true if the item at index is currently selected, and false if it's not.
1095  * It also returns false if the index is not in the range an therefoe does not
1096  * exist.
1097  */
1098 bool
1099 BListView::IsItemSelected(int32 index) const
1100 {
1101   if ((fFirstSelected >= 0) && (index >= fFirstSelected) && (index <= fLastSelected))
1102     return ItemAt(index)->IsSelected() ;
1103 
1104   return false ;
1105 }
1106 
1107 /**
1108  * Returns the index of a currently selected item in the list, or
1109  * a negative number if no item is selected.
1110  * The domain of the index passed as an argument is the current set
1111  * of selected items; the first selected item is at index 0, the second
1112  * at index 1, and so on, even if the selection is not  contiguous. The
1113  * domain of the returned index is the set of all items in the list.
1114  *
1115  * To get all currently selected items, increment the passed index until
1116  * the function returns a negative number.
1117  */
1118 int32
1119 BListView::CurrentSelection(int32 index /* = 0 */) const
1120 {
1121   if ((index >= 0) && (fFirstSelected >= 0))
1122   {
1123     for (int32 count = (fFirstSelected > index ? fFirstSelected : index) ;
1124          count <= fLastSelected ; ++count)
1125     {
1126       if (ItemAt(count)->IsSelected())
1127       {
1128         --index ;
1129 
1130         if (index == 0)
1131         {
1132           return count ;
1133         }
1134       }
1135     }
1136   }
1137 
1138   return -1 ;
1139 }
1140 
1141 /**
1142  * Augments the BInvoker version of Invoke() to add three pieces of information
1143  * to each message the BListView sends:
1144  *
1145  * "when" - B_INT64_TYPE
1146  * When the message is sent, as measured by the number of microseconds
1147  * since 12:00:00 AM 1970.
1148  *
1149  * "source" - B_POINTER_TYPE
1150  * A pointer to the BListView object.
1151  *
1152  * "index" - B_INT32_TYPE
1153  * An array containing the index of every selected item.
1154  *
1155  * This function is called to send both the selection message and the
1156  * invocation message. It can also be called from application code. The default
1157  * target of the message (established by AttachedToWindow()) is the BWindow
1158  * where the BListView is located.
1159  * What it means to "invoke" selected items depends entirely on the
1160  * invocation BMessage and the receiver's response to it. This function does
1161  * nothing but send the message.
1162  */
1163 
1164  // check if it is done in the right way
1165 status_t
1166 BListView::Invoke(BMessage* msg /* = NULL */)
1167 {
1168   return BInvoker::Invoke(msg) ;
1169 }
1170 
1171 /**
1172  * This function deselects all the items.
1173  */
1174 void
1175 BListView::DeselectAll()
1176 {
1177   if (fFirstSelected != -1)
1178   {
1179     for (int32 index = fFirstSelected ; index <= fLastSelected ; ++index)
1180     {
1181       if (!ItemAt(index)->IsSelected())
1182       {
1183         ItemAt(index)->Deselect() ;
1184         InvalidateItem(index) ;
1185       }
1186     }
1187   }
1188 }
1189 
1190 /**
1191  * This function deselects all the items except those from index start through
1192  * index finish.
1193  */
1194 void
1195 BListView::DeselectExcept(int32 except_from, int32 except_to)
1196 {
1197   // if no item is selected -> nothing to do
1198   if ((fFirstSelected != -1) && (except_from <= except_to))
1199   {
1200     for (int32 index = fFirstSelected ; index < except_from ; ++index)
1201     {
1202       if (!ItemAt(index)->IsSelected())
1203       {
1204         ItemAt(index)->Deselect() ;
1205         InvalidateItem(index) ;
1206       }
1207     }
1208     for (int32 index = except_to + 1 ; index <= fLastSelected ; ++index)
1209     {
1210       if (!ItemAt(index)->IsSelected())
1211       {
1212         ItemAt(index)->Deselect() ;
1213         InvalidateItem(index) ;
1214       }
1215     }
1216 
1217     SelectionChanged() ;
1218   }
1219 }
1220 
1221 /**
1222  * These functions deselect the item at index.
1223  */
1224 void
1225 BListView::Deselect(int32 index)
1226 {
1227   if (fFirstSelected != -1)
1228   {
1229     if ((index < fLastSelected) && (index >= fFirstSelected))
1230     {
1231       if (!ItemAt(index)->IsSelected())
1232       {
1233         ItemAt(index)->Deselect() ;
1234         InvalidateItem(index) ;
1235         SelectionChanged() ;
1236       }
1237     }
1238   }
1239 }
1240 
1241 // Implemented by derived class
1242 void
1243 BListView::SelectionChanged()
1244 {
1245 }
1246 
1247 void
1248 BListView::SortItems(int (*cmp)(const void *, const void *))
1249 {
1250   fList.SortItems(cmp) ;
1251   Invalidate() ;
1252 }
1253 
1254 
1255 /* These functions bottleneck through DoMiscellaneous() */
1256 bool
1257 BListView::SwapItems(int32 a, int32 b)
1258 {
1259   bool success = true ;
1260 
1261   if ((a >= 0) && (a < fList.CountItems()) && (b >= 0) && (b < fList.CountItems()))
1262   {
1263     // just swap if a is not b
1264     if (a != b)
1265     {
1266       // ToDo chack if it is done correct
1267       BListItem* item = RemoveItem(a) ;
1268       AddItem(item, b - 1) ;
1269       item = RemoveItem(b) ;
1270       AddItem(item, a) ;
1271     }
1272   }
1273   else {
1274     success = false ;
1275   }
1276 
1277   return success ;
1278 }
1279 
1280 /**
1281  * MoveItem() moves the item located at the index from to the index
1282  * specified by the to argument.
1283  * This function returns true if the requested operation is
1284  * completed successfully, or false if the operation failed (for example,
1285  * if a specified  index is out of range).
1286  */
1287 
1288  // Is redraw neccessary????
1289 bool
1290 BListView::MoveItem(int32 from, int32 to)
1291 {
1292   bool success = true ;
1293 
1294   if ((to >= 0) && (to < fList.CountItems()))
1295   {
1296     // no move required if from would be to
1297     if (from != to)
1298     {
1299       BListItem* item = RemoveItem(from) ;
1300 
1301       if (item != NULL)
1302         AddItem(item, to - 1) ;
1303       else
1304         success = false ;
1305     }
1306   }
1307   else {
1308     success = false ;
1309   }
1310 
1311   return success ;
1312 }
1313 
1314 bool
1315 BListView::ReplaceItem(int32 index, BListItem* item)
1316 {
1317   bool success = true ;
1318 
1319   // otherwise the "new" item would be deleted
1320   if (ItemAt(index) != item)
1321   {
1322     BListItem* old = RemoveItem(index) ;
1323 
1324     if (old != NULL)
1325     {
1326       AddItem(item, index) ;
1327       delete old ;
1328     }
1329     // In this case the index is not in the range
1330     else {
1331       success = false ;
1332     }
1333   }
1334 
1335   return success ;
1336 }
1337 
1338 /**
1339  * Sets up the BListView and makes the BWindow to which it has become
1340  * attached the target for the messages it sends when items are selected
1341  * or invoked—provided another target hasn't already been set. In addition,
1342  * this function calls Update() for each item in the list to give it a
1343  * chance to adjust its layout. The BListView's vertical scroll bar is
1344  * also adjusted.
1345  * This function is called for you when the BListView becomes part of a
1346  * window's view hierarchy.
1347  */
1348 void
1349 BListView::AttachedToWindow()
1350 {
1351   // If no target is set the window to which the ListView is attached will
1352   // become the target
1353   if (BInvoker::Target() == NULL)
1354   {
1355     BInvoker::SetTarget(BView::Window()) ;
1356   }
1357 
1358   // call the update function of all items in the list
1359   for (int32 index = 0 ; index < fList.CountItems() ; ++index)
1360   {
1361     BFont font ;
1362     BView::GetFont(&font) ;
1363     ItemAt(index)->Update(this, &font) ;
1364 
1365     if (fWidth < ItemAt(index)->Width())
1366       fWidth = ItemAt(index)->Width() ;
1367   }
1368 
1369   // Adjust the ScrollBars.
1370   FixupScrollBar() ;
1371 }
1372 
1373 void
1374 BListView::FrameMoved(BPoint new_position)
1375 {
1376 }
1377 
1378 /**
1379  * Returns the frame rectangle of the BListItem at index. The rectangle is stated
1380  * in the coordinate system of the BListView and defines the area where the item
1381  * is drawn. Items can differ in height, (but all have the same width ??).
1382  */
1383 BRect
1384 BListView::ItemFrame(int32 index)
1385 {
1386   BRect rect ;
1387 
1388   if ((index < fList.CountItems()) && (index >= 0))
1389   {
1390     rect.left = 0 ;
1391 
1392     for (int32 item = 0 ; item < index ; ++item)
1393     {
1394       rect.top += ItemAt(item)->Height() ;
1395     }
1396 
1397     rect.bottom = rect.top + ItemAt(index)->Height() ;
1398     rect.right = Bounds().right ;
1399   }
1400 
1401   return rect ;
1402 }
1403 
1404 BHandler*
1405 BListView::ResolveSpecifier(BMessage *msg, int32 index,
1406                             BMessage *specifier, int32 form,
1407                             const char *property)
1408 {
1409   return NULL ;
1410 }
1411 
1412 status_t
1413 BListView::GetSupportedSuites(BMessage *data)
1414 {
1415   return B_OK ;
1416 }
1417 
1418 status_t
1419 BListView::Perform(perform_code d, void *arg)
1420 {
1421   return B_OK ;
1422 }
1423 
1424 void
1425 BListView::WindowActivated(bool state)
1426 {
1427 }
1428 
1429 void
1430 BListView::MouseUp(BPoint pt)
1431 {
1432   int32 index = IndexOf(pt) ;
1433 
1434   if ((index > 0) && (fListType == B_MULTIPLE_SELECTION_LIST) && ((modifiers() & B_SHIFT_KEY)))
1435   {
1436     if (!ItemAt(index)->IsSelected())
1437       Deselect(index) ;
1438   }
1439 }
1440 
1441 void
1442 BListView::MouseMoved(BPoint pt, uint32 code, const BMessage *msg)
1443 {
1444 }
1445 
1446 void
1447 BListView::DetachedFromWindow()
1448 {
1449 }
1450 
1451 /**
1452  * Implemented by derived classes to permit users to drag items.
1453  * This function is called from the BListView's MouseDown() function;
1454  * it should initiate the drag-and-drop operation and return true,
1455  * or refuse to do so and return false. By default, it always
1456  * returns false.
1457  * The point that's passed to InitiateDrag() is the same as the
1458  * point passed to MouseDown(); it's where the cursor was located
1459  * when the user pressed the mouse button. The index of the item
1460  * under the cursor (the item that would be dragged) is passed as the
1461  * second argument, and the wasSelected flag indicates whether or
1462  * not the item was selected before the mouse button went down.
1463  *
1464  * A BListView allows users to autoscroll the list by holding the
1465  * mouse button down and dragging outside its frame rectangle. If a
1466  * derived class implements InitiateDrag() to drag an item each time
1467  * the user moves the mouse with a button down, it will hide this
1468  * autoscrolling behavior. Therefore, derived classes typically
1469  * permit users to drag items only if they're already selected (if
1470  * wasSelected is true). In other words, it takes two mouse-down events
1471  * to drag an item—one to select it and one to begin dragging it.
1472  */
1473 bool
1474 BListView::InitiateDrag(BPoint pt, int32 itemIndex, bool initialySelected)
1475 {
1476   return false ;
1477 }
1478 
1479 void
1480 BListView::ResizeToPreferred()
1481 {
1482 }
1483 
1484 void
1485 BListView::GetPreferredSize(float* width, float* height)
1486 {
1487   if (height != NULL)
1488   {
1489     *height = 0 ;
1490 
1491     for (int32 index = 0 ; index < CountItems() ; ++index)
1492     {
1493       *height += ItemAt(index)->Height() ;
1494     }
1495   }
1496 
1497   if (width != NULL)
1498     *width = 0 ;
1499 }
1500 
1501 void
1502 BListView::AllAttached()
1503 {
1504 }
1505 
1506 void
1507 BListView::AllDetached()
1508 {
1509 }
1510 
1511 //---------------------------------------------------------------------------
1512 //  protected!
1513 
1514 bool
1515 BListView::DoMiscellaneous(MiscCode code, MiscData* data)
1516 {
1517   switch(code)
1518   {
1519   case B_NO_OP:
1520     break;
1521 
1522   case B_REPLACE_OP:
1523     data->replace.index ;
1524     data->replace.item ;
1525     break ;
1526 
1527   case B_MOVE_OP:
1528     data->move.from ;
1529     data->move.to ;
1530     break ;
1531 
1532   case B_SWAP_OP:
1533     data->swap.a ;
1534     data->swap.b ;
1535     break ;
1536   }
1537 
1538   return true ;
1539 }
1540 
1541 /*----- Private or reserved -----------------------------------------*/
1542 
1543 void
1544 BListView::InitObject(list_view_type type)
1545 {
1546   fList.MakeEmpty() ;
1547   fListType = type ;
1548   fFirstSelected = -1 ;
1549 /*
1550 int32			fLastSelected;
1551 int32			fAnchorIndex;
1552 */
1553   fWidth = 0 ;
1554   fSelectMessage = NULL ;
1555   fScrollView = NULL ;
1556   fTrack = NULL ;
1557 }
1558 
1559 /**
1560  * Updates the scroll bars if the list view is targetted by a scroll view
1561  */
1562 void
1563 BListView::FixupScrollBar()
1564 {
1565   // set the range of the scroll bars of the scroll view
1566   if (fScrollView != NULL)
1567   {
1568     BScrollBar* bar ;
1569     float height ;
1570     float width ;
1571     BRect bounds(Bounds()) ;
1572 
1573     GetPreferredSize(&width, &height) ;
1574     height -= (bounds.bottom - bounds.top) ;
1575     width  -= (bounds.right - bounds.left) ;
1576 
1577     if (height < 0)
1578       height = 0 ;
1579 
1580     if (width < 0)
1581       width = 0 ;
1582 
1583     if ((bar = fScrollView->ScrollBar(B_HORIZONTAL)) != NULL)
1584     {
1585       bar->SetRange(0, width) ;
1586       bar->SetSteps(ItemAt(0)->Height(), bounds.bottom - bounds.top) ;
1587     }
1588 
1589     if ((bar = fScrollView->ScrollBar(B_VERTICAL)) != NULL)
1590     {
1591       bar->SetRange(0, height) ;
1592       bar->SetSteps(ItemAt(0)->Height(), bounds.bottom - bounds.top) ;
1593     }
1594   }
1595 }
1596 
1597 void
1598 BListView::InvalidateFrom(int32 index)
1599 {
1600   if ((index >= 0) && (index < CountItems()))
1601   {
1602     BRect frame(ItemFrame(index)) ;
1603 
1604     for (++index ; index < fList.CountItems() ; ++index)
1605       frame.bottom += ItemAt(index)->Height() ;
1606 
1607     // build the rect which needs an update
1608     BRect bounds(Bounds()) ;
1609     frame = frame & bounds ;
1610     frame = frame | bounds ;
1611     Invalidate(frame) ;
1612   }
1613 }
1614 
1615 inline status_t
1616 BListView::PostMsg(BMessage* msg)
1617 {
1618   return BInvoker::Invoke(msg) ;
1619 }
1620 
1621 void
1622 BListView::FontChanged()
1623 {
1624   // all items need to be updated
1625   for (int32 i = 0 ; i < fList.CountItems() ; ++i)
1626   {
1627     ItemAt(i)->Update(this, NULL) ;
1628   }
1629 }
1630 
1631 int32
1632 BListView::RangeCheck(int32 index)
1633 {
1634   return 0 ;
1635 }
1636 
1637 bool
1638 BListView::_Select(int32 index, bool extend)
1639 {
1640   return true ;
1641 }
1642 
1643 bool
1644 BListView::_Select(int32 from, int32 to, bool extend)
1645 {
1646   return true ;
1647 }
1648 
1649 bool
1650 BListView::_Deselect(int32 index)
1651 {
1652   return true ;
1653 }
1654 
1655 void
1656 BListView::Deselect(int32 from, int32 to)
1657 {
1658   for ( ; (from <= to) && (from < fList.CountItems()) ; ++from)
1659   {
1660     ItemAt(from)->Deselect() ;
1661   }
1662 }
1663 
1664 bool
1665 BListView::_DeselectAll(int32 except_from, int32 except_to)
1666 {
1667   for (int32 i = 0 ; i < CountItems() ; ++i)
1668   {
1669     if (i == except_from)
1670     {
1671       i = except_to ;
1672       if (i >= CountItems())
1673         break ;
1674     }
1675 
1676     ItemAt(i)->Deselect() ;
1677   }
1678 
1679   return true ;
1680 }
1681 
1682 void
1683 BListView::PerformDelayedSelect()
1684 {
1685 }
1686 
1687 bool
1688 BListView::TryInitiateDrag(BPoint where)
1689 {
1690   return true ;
1691 }
1692 
1693 int32
1694 BListView::CalcFirstSelected(int32 after)
1695 {
1696   return 0 ;
1697 }
1698 
1699 int32
1700 BListView::CalcLastSelected(int32 before)
1701 {
1702   return 0 ;
1703 }
1704 
1705 void
1706 BListView::DrawItem(BListItem* item, BRect itemRect,
1707                     bool complete /* = false */)
1708 {
1709 }
1710 
1711 bool
1712 BListView::DoSwapItems(int32 a, int32 b)
1713 {
1714   return true ;
1715 }
1716 
1717 bool
1718 BListView::DoMoveItem(int32 from, int32 to)
1719 {
1720   return true ;
1721 }
1722 
1723 bool
1724 BListView::DoReplaceItem(int32 index, BListItem* item)
1725 {
1726   return true ;
1727 }
1728 
1729 void
1730 BListView::RescanSelection(int32 from, int32 to)
1731 {
1732 }
1733 
1734 void
1735 BListView::DoMouseUp(BPoint where)
1736 {
1737 }
1738 
1739 void
1740 BListView::DoMouseMoved(BPoint where)
1741 {
1742 }
1743