1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/univ/listbox.cpp
3 // Purpose: wxListBox implementation
4 // Author: Vadim Zeitlin
8 // Copyright: (c) 2000 SciTech Software, Inc. (www.scitechsoft.com)
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
12 // ============================================================================
14 // ============================================================================
16 // ----------------------------------------------------------------------------
18 // ----------------------------------------------------------------------------
20 #include "wx/wxprec.h"
31 #include "wx/dcclient.h"
32 #include "wx/listbox.h"
33 #include "wx/validate.h"
36 #include "wx/univ/renderer.h"
37 #include "wx/univ/inphand.h"
38 #include "wx/univ/theme.h"
40 // ----------------------------------------------------------------------------
41 // wxStdListboxInputHandler: handles mouse and kbd in a single or multi
43 // ----------------------------------------------------------------------------
45 class WXDLLEXPORT wxStdListboxInputHandler
: public wxStdInputHandler
48 // if pressing the mouse button in a multiselection listbox should toggle
49 // the item under mouse immediately, then specify true as the second
50 // parameter (this is the standard behaviour, under GTK the item is toggled
51 // only when the mouse is released in the multi selection listbox)
52 wxStdListboxInputHandler(wxInputHandler
*inphand
,
53 bool toggleOnPressAlways
= true);
56 virtual bool HandleKey(wxInputConsumer
*consumer
,
57 const wxKeyEvent
& event
,
59 virtual bool HandleMouse(wxInputConsumer
*consumer
,
60 const wxMouseEvent
& event
);
61 virtual bool HandleMouseMove(wxInputConsumer
*consumer
,
62 const wxMouseEvent
& event
);
65 // return the item under mouse, 0 if the mouse is above the listbox or
66 // GetCount() if it is below it
67 int HitTest(const wxListBox
*listbox
, const wxMouseEvent
& event
);
69 // parts of HitTest(): first finds the pseudo (because not in range) index
70 // of the item and the second one adjusts it if necessary - that is if the
71 // third one returns false
72 int HitTestUnsafe(const wxListBox
*listbox
, const wxMouseEvent
& event
);
73 int FixItemIndex(const wxListBox
*listbox
, int item
);
74 bool IsValidIndex(const wxListBox
*listbox
, int item
);
76 // init m_btnCapture and m_actionMouse
77 wxControlAction
SetupCapture(wxListBox
*lbox
,
78 const wxMouseEvent
& event
,
81 wxRenderer
*m_renderer
;
83 // the button which initiated the mouse capture (currently 0 or 1)
86 // the action to perform when the mouse moves while we capture it
87 wxControlAction m_actionMouse
;
89 // the ctor parameter toggleOnPressAlways (see comments near it)
90 bool m_toggleOnPressAlways
;
92 // do we track the mouse outside the window when it is captured?
93 bool m_trackMouseOutside
;
96 // ============================================================================
97 // implementation of wxListBox
98 // ============================================================================
100 IMPLEMENT_DYNAMIC_CLASS(wxListBox
, wxControl
)
102 BEGIN_EVENT_TABLE(wxListBox
, wxListBoxBase
)
103 EVT_SIZE(wxListBox::OnSize
)
106 // ----------------------------------------------------------------------------
108 // ----------------------------------------------------------------------------
110 void wxListBox::Init()
112 // will be calculated later when needed
120 // no items hence no current item
123 m_currentChanged
= false;
125 // no need to update anything initially
128 // no scrollbars to show nor update
132 m_showScrollbarY
= false;
135 wxListBox::wxListBox(wxWindow
*parent
,
139 const wxArrayString
& choices
,
141 const wxValidator
& validator
,
142 const wxString
&name
)
143 :wxScrollHelper(this)
147 Create(parent
, id
, pos
, size
, choices
, style
, validator
, name
);
150 bool wxListBox::Create(wxWindow
*parent
,
154 const wxArrayString
& choices
,
156 const wxValidator
& validator
,
157 const wxString
&name
)
159 wxCArrayString
chs(choices
);
161 return Create(parent
, id
, pos
, size
, chs
.GetCount(), chs
.GetStrings(),
162 style
, validator
, name
);
165 bool wxListBox::Create(wxWindow
*parent
,
170 const wxString choices
[],
172 const wxValidator
& validator
,
173 const wxString
&name
)
175 // for compatibility accept both the new and old styles - they mean the
177 if ( style
& wxLB_ALWAYS_SB
)
178 style
|= wxALWAYS_SHOW_SB
;
180 // if we don't have neither multiple nor extended flag, we must have the
181 // single selection listbox
182 if ( !(style
& (wxLB_MULTIPLE
| wxLB_EXTENDED
)) )
183 style
|= wxLB_SINGLE
;
185 #if wxUSE_TWO_WINDOWS
186 style
|= wxVSCROLL
|wxHSCROLL
;
187 if ((style
& wxBORDER_MASK
) == 0)
188 style
|= wxBORDER_SUNKEN
;
191 if ( !wxControl::Create(parent
, id
, pos
, size
, style
,
195 m_strings
= new wxArrayString
;
201 CreateInputHandler(wxINP_HANDLER_LISTBOX
);
206 wxListBox::~wxListBox()
208 // call this just to free the client data -- and avoid leaking memory
216 // ----------------------------------------------------------------------------
217 // adding/inserting strings
218 // ----------------------------------------------------------------------------
220 int wxCMPFUNC_CONV
wxListBoxSortNoCase(wxString
* s1
, wxString
* s2
)
222 return s1
->CmpNoCase(*s2
);
225 int wxListBox::DoAppendOnly(const wxString
& item
)
231 m_strings
->Add(item
);
232 m_strings
->Sort(wxListBoxSortNoCase
);
233 index
= m_strings
->Index(item
);
237 index
= m_strings
->GetCount();
238 m_strings
->Add(item
);
244 int wxListBox::DoAppend(const wxString
& item
)
246 size_t index
= DoAppendOnly( item
);
248 m_itemsClientData
.Insert(NULL
, index
);
250 m_updateScrollbarY
= true;
252 if ( HasHorzScrollbar() )
254 // has the max width increased?
256 GetTextExtent(item
, &width
, NULL
);
257 if ( width
> m_maxWidth
)
260 m_maxWidthItem
= index
;
261 m_updateScrollbarX
= true;
265 RefreshFromItemToEnd(index
);
270 void wxListBox::DoInsertItems(const wxArrayString
& items
, unsigned int pos
)
272 // the position of the item being added to a sorted listbox can't be
274 wxCHECK_RET( !IsSorted(), _T("can't insert items into sorted listbox") );
276 unsigned int count
= items
.GetCount();
277 for ( unsigned int n
= 0; n
< count
; n
++ )
279 m_strings
->Insert(items
[n
], pos
+ n
);
280 m_itemsClientData
.Insert(NULL
, pos
+ n
);
283 // the number of items has changed so we might have to show the scrollbar
284 m_updateScrollbarY
= true;
286 // the max width also might have changed - just recalculate it instead of
287 // keeping track of it here, this is probably more efficient for a typical
289 RefreshHorzScrollbar();
291 // note that we have to refresh all the items after the ones we inserted,
292 // not just these items
293 RefreshFromItemToEnd(pos
);
296 void wxListBox::DoSetItems(const wxArrayString
& items
, void **clientData
)
300 unsigned int count
= items
.GetCount();
304 m_strings
->Alloc(count
);
306 m_itemsClientData
.Alloc(count
);
307 for ( unsigned int n
= 0; n
< count
; n
++ )
309 unsigned int index
= DoAppendOnly(items
[n
]);
311 m_itemsClientData
.Insert(clientData
? clientData
[n
] : NULL
, index
);
314 m_updateScrollbarY
= true;
319 void wxListBox::SetString(unsigned int n
, const wxString
& s
)
321 wxCHECK_RET( !IsSorted(), _T("can't set string in sorted listbox") );
325 if ( HasHorzScrollbar() )
327 // we need to update m_maxWidth as changing the string may cause the
328 // horz scrollbar [dis]appear
331 GetTextExtent(s
, &width
, NULL
);
333 // it might have increased if the new string is long
334 if ( width
> m_maxWidth
)
338 m_updateScrollbarX
= true;
340 // or also decreased if the old string was the longest one
341 else if ( n
== (unsigned int)m_maxWidthItem
)
343 RefreshHorzScrollbar();
350 // ----------------------------------------------------------------------------
352 // ----------------------------------------------------------------------------
354 void wxListBox::DoClear()
358 if ( HasClientObjectData() )
360 unsigned int count
= m_itemsClientData
.GetCount();
361 for ( unsigned int n
= 0; n
< count
; n
++ )
363 delete (wxClientData
*) m_itemsClientData
[n
];
367 m_itemsClientData
.Clear();
368 m_selections
.Clear();
373 void wxListBox::Clear()
377 m_updateScrollbarY
= true;
379 RefreshHorzScrollbar();
384 void wxListBox::Delete(unsigned int n
)
386 wxCHECK_RET( IsValid(n
),
387 _T("invalid index in wxListBox::Delete") );
389 // do it before removing the index as otherwise the last item will not be
390 // refreshed (as GetCount() will be decremented)
391 RefreshFromItemToEnd(n
);
393 m_strings
->RemoveAt(n
);
395 if ( HasClientObjectData() )
397 delete (wxClientData
*)m_itemsClientData
[n
];
400 m_itemsClientData
.RemoveAt(n
);
402 // when the item disappears we must not keep using its index
403 if ( (int)n
== m_current
)
407 else if ( (int)n
< m_current
)
411 //else: current item may stay
413 // update the selections array: the indices of all seletected items after
414 // the one being deleted must change and the item itselfm ust be removed
415 int index
= wxNOT_FOUND
;
416 unsigned int count
= m_selections
.GetCount();
417 for ( unsigned int item
= 0; item
< count
; item
++ )
419 if ( m_selections
[item
] == (int)n
)
421 // remember to delete it later
424 else if ( m_selections
[item
] > (int)n
)
426 // to account for the index shift
427 m_selections
[item
]--;
429 //else: nothing changed for this one
432 if ( index
!= wxNOT_FOUND
)
434 m_selections
.RemoveAt(index
);
437 // the number of items has changed, hence the scrollbar may disappear
438 m_updateScrollbarY
= true;
440 // finally, if the longest item was deleted the scrollbar may disappear
441 if ( (int)n
== m_maxWidthItem
)
443 RefreshHorzScrollbar();
447 // ----------------------------------------------------------------------------
448 // client data handling
449 // ----------------------------------------------------------------------------
451 void wxListBox::DoSetItemClientData(unsigned int n
, void* clientData
)
453 m_itemsClientData
[n
] = clientData
;
456 void *wxListBox::DoGetItemClientData(unsigned int n
) const
458 return m_itemsClientData
[n
];
461 void wxListBox::DoSetItemClientObject(unsigned int n
, wxClientData
* clientData
)
463 m_itemsClientData
[n
] = clientData
;
466 wxClientData
* wxListBox::DoGetItemClientObject(unsigned int n
) const
468 return (wxClientData
*)m_itemsClientData
[n
];
471 // ----------------------------------------------------------------------------
473 // ----------------------------------------------------------------------------
475 void wxListBox::DoSetSelection(int n
, bool select
)
479 if ( m_selections
.Index(n
) == wxNOT_FOUND
)
481 if ( !HasMultipleSelection() )
483 // selecting an item in a single selection listbox deselects
493 //else: already selected
497 int index
= m_selections
.Index(n
);
498 if ( index
!= wxNOT_FOUND
)
500 m_selections
.RemoveAt(index
);
507 // sanity check: a single selection listbox can't have more than one item
509 wxASSERT_MSG( HasMultipleSelection() || (m_selections
.GetCount() < 2),
510 _T("multiple selected items in single selection lbox?") );
514 // the newly selected item becomes the current one
519 int wxListBox::GetSelection() const
521 wxCHECK_MSG( !HasMultipleSelection(), wxNOT_FOUND
,
522 _T("use wxListBox::GetSelections for ths listbox") );
524 return m_selections
.IsEmpty() ? wxNOT_FOUND
: m_selections
[0];
527 int wxCMPFUNC_CONV
wxCompareInts(int *n
, int *m
)
532 int wxListBox::GetSelections(wxArrayInt
& selections
) const
534 // always return sorted array to the user
535 selections
= m_selections
;
536 unsigned int count
= m_selections
.GetCount();
538 // don't call sort on an empty array
541 selections
.Sort(wxCompareInts
);
547 // ----------------------------------------------------------------------------
548 // refresh logic: we use delayed refreshing which allows to avoid multiple
549 // refreshes (and hence flicker) in case when several listbox items are
550 // added/deleted/changed subsequently
551 // ----------------------------------------------------------------------------
553 void wxListBox::RefreshFromItemToEnd(int from
)
555 RefreshItems(from
, GetCount() - from
);
558 void wxListBox::RefreshItems(int from
, int count
)
560 switch ( m_updateCount
)
564 m_updateCount
= count
;
568 // we refresh everything anyhow
572 // add these items to the others which we have to refresh
573 if ( m_updateFrom
< from
)
575 count
+= from
- m_updateFrom
;
576 if ( m_updateCount
< count
)
577 m_updateCount
= count
;
579 else // m_updateFrom >= from
581 int updateLast
= wxMax(m_updateFrom
+ m_updateCount
,
584 m_updateCount
= updateLast
- m_updateFrom
;
589 void wxListBox::RefreshItem(int n
)
591 switch ( m_updateCount
)
594 // refresh this item only
600 // we refresh everything anyhow
604 // add this item to the others which we have to refresh
605 if ( m_updateFrom
< n
)
607 if ( m_updateCount
< n
- m_updateFrom
+ 1 )
608 m_updateCount
= n
- m_updateFrom
+ 1;
610 else // n <= m_updateFrom
612 m_updateCount
+= m_updateFrom
- n
;
618 void wxListBox::RefreshAll()
623 void wxListBox::RefreshHorzScrollbar()
625 m_maxWidth
= 0; // recalculate it
626 m_updateScrollbarX
= true;
629 void wxListBox::UpdateScrollbars()
631 wxSize size
= GetClientSize();
633 // is our height enough to show all items?
634 unsigned int nLines
= GetCount();
635 wxCoord lineHeight
= GetLineHeight();
636 bool showScrollbarY
= (int)nLines
*lineHeight
> size
.y
;
638 // check the width too if required
639 wxCoord charWidth
, maxWidth
;
641 if ( HasHorzScrollbar() )
643 charWidth
= GetCharWidth();
644 maxWidth
= GetMaxWidth();
645 showScrollbarX
= maxWidth
> size
.x
;
647 else // never show it
649 charWidth
= maxWidth
= 0;
650 showScrollbarX
= false;
653 // what should be the scrollbar range now?
654 int scrollRangeX
= showScrollbarX
655 ? (maxWidth
+ charWidth
- 1) / charWidth
+ 2 // FIXME
657 int scrollRangeY
= showScrollbarY
659 (size
.y
% lineHeight
+ lineHeight
- 1) / lineHeight
662 // reset scrollbars if something changed: either the visibility status
663 // or the range of a scrollbar which is shown
664 if ( (showScrollbarY
!= m_showScrollbarY
) ||
665 (showScrollbarX
!= m_showScrollbarX
) ||
666 (showScrollbarY
&& (scrollRangeY
!= m_scrollRangeY
)) ||
667 (showScrollbarX
&& (scrollRangeX
!= m_scrollRangeX
)) )
670 GetViewStart(&x
, &y
);
671 SetScrollbars(charWidth
, lineHeight
,
672 scrollRangeX
, scrollRangeY
,
675 m_showScrollbarX
= showScrollbarX
;
676 m_showScrollbarY
= showScrollbarY
;
678 m_scrollRangeX
= scrollRangeX
;
679 m_scrollRangeY
= scrollRangeY
;
683 void wxListBox::UpdateItems()
685 // only refresh the items which must be refreshed
686 if ( m_updateCount
== -1 )
689 wxLogTrace(_T("listbox"), _T("Refreshing all"));
695 wxSize size
= GetClientSize();
698 rect
.height
= size
.y
;
699 rect
.y
+= m_updateFrom
*GetLineHeight();
700 rect
.height
= m_updateCount
*GetLineHeight();
702 // we don't need to calculate x position as we always refresh the
704 CalcScrolledPosition(0, rect
.y
, NULL
, &rect
.y
);
706 wxLogTrace(_T("listbox"), _T("Refreshing items %d..%d (%d-%d)"),
707 m_updateFrom
, m_updateFrom
+ m_updateCount
- 1,
708 rect
.GetTop(), rect
.GetBottom());
710 Refresh(true, &rect
);
714 void wxListBox::OnInternalIdle()
716 if ( m_updateScrollbarY
|| m_updateScrollbarX
)
721 m_updateScrollbarY
= false;
724 if ( m_currentChanged
)
726 DoEnsureVisible(m_current
);
728 m_currentChanged
= false;
737 wxListBoxBase::OnInternalIdle();
740 // ----------------------------------------------------------------------------
742 // ----------------------------------------------------------------------------
744 wxBorder
wxListBox::GetDefaultBorder() const
746 return wxBORDER_SUNKEN
;
749 void wxListBox::DoDraw(wxControlRenderer
*renderer
)
751 // adjust the DC to account for scrolling
752 wxDC
& dc
= renderer
->GetDC();
754 dc
.SetFont(GetFont());
756 // get the update rect
757 wxRect rectUpdate
= GetUpdateClientRect();
760 CalcUnscrolledPosition(0, rectUpdate
.GetTop(), NULL
, &yTop
);
761 CalcUnscrolledPosition(0, rectUpdate
.GetBottom(), NULL
, &yBottom
);
763 // get the items which must be redrawn
764 wxCoord lineHeight
= GetLineHeight();
765 unsigned int itemFirst
= yTop
/ lineHeight
,
766 itemLast
= (yBottom
+ lineHeight
- 1) / lineHeight
,
767 itemMax
= m_strings
->GetCount();
769 if ( itemFirst
>= itemMax
)
772 if ( itemLast
> itemMax
)
776 wxLogTrace(_T("listbox"), _T("Repainting items %d..%d"),
777 itemFirst
, itemLast
);
779 DoDrawRange(renderer
, itemFirst
, itemLast
);
782 void wxListBox::DoDrawRange(wxControlRenderer
*renderer
,
783 int itemFirst
, int itemLast
)
785 renderer
->DrawItems(this, itemFirst
, itemLast
);
788 // ----------------------------------------------------------------------------
790 // ----------------------------------------------------------------------------
792 bool wxListBox::SetFont(const wxFont
& font
)
794 if ( !wxControl::SetFont(font
) )
804 void wxListBox::CalcItemsPerPage()
806 m_lineHeight
= GetRenderer()->GetListboxItemHeight(GetCharHeight());
807 m_itemsPerPage
= GetClientSize().y
/ m_lineHeight
;
810 int wxListBox::GetItemsPerPage() const
812 if ( !m_itemsPerPage
)
814 wxConstCast(this, wxListBox
)->CalcItemsPerPage();
817 return m_itemsPerPage
;
820 wxCoord
wxListBox::GetLineHeight() const
824 wxConstCast(this, wxListBox
)->CalcItemsPerPage();
830 wxCoord
wxListBox::GetMaxWidth() const
832 if ( m_maxWidth
== 0 )
834 wxListBox
*self
= wxConstCast(this, wxListBox
);
836 unsigned int count
= m_strings
->GetCount();
837 for ( unsigned int n
= 0; n
< count
; n
++ )
839 GetTextExtent(this->GetString(n
), &width
, NULL
);
840 if ( width
> m_maxWidth
)
842 self
->m_maxWidth
= width
;
843 self
->m_maxWidthItem
= n
;
851 void wxListBox::OnSize(wxSizeEvent
& event
)
853 // recalculate the number of items per page
856 // the scrollbars might [dis]appear
858 m_updateScrollbarY
= true;
863 void wxListBox::DoSetFirstItem(int n
)
868 void wxListBox::DoSetSize(int x
, int y
,
869 int width
, int height
,
872 if ( GetWindowStyle() & wxLB_INT_HEIGHT
)
874 // we must round up the height to an entire number of rows
876 // the client area must contain an int number of rows, so take borders
878 wxRect rectBorders
= GetRenderer()->GetBorderDimensions(GetBorder());
879 wxCoord hBorders
= rectBorders
.y
+ rectBorders
.height
;
881 wxCoord hLine
= GetLineHeight();
882 height
= ((height
- hBorders
+ hLine
- 1) / hLine
)*hLine
+ hBorders
;
885 wxListBoxBase::DoSetSize(x
, y
, width
, height
, sizeFlags
);
888 wxSize
wxListBox::DoGetBestClientSize() const
893 unsigned int count
= m_strings
->GetCount();
894 for ( unsigned int n
= 0; n
< count
; n
++ )
897 GetTextExtent(this->GetString(n
), &w
, &h
);
905 // if the listbox is empty, still give it some non zero (even if
906 // arbitrary) size - otherwise, leave small margin around the strings
910 width
+= 3*GetCharWidth();
913 height
= GetCharHeight();
915 // we need the height of the entire listbox, not just of one line
916 height
*= wxMax(count
, 7);
918 return wxSize(width
, height
);
921 // ----------------------------------------------------------------------------
923 // ----------------------------------------------------------------------------
925 bool wxListBox::SendEvent(wxEventType type
, int item
)
927 wxCommandEvent
event(type
, m_windowId
);
928 event
.SetEventObject(this);
930 // use the current item by default
936 // client data and string parameters only make sense if we have an item
939 if ( HasClientObjectData() )
940 event
.SetClientObject(GetClientObject(item
));
941 else if ( HasClientUntypedData() )
942 event
.SetClientData(GetClientData(item
));
944 event
.SetString(GetString(item
));
949 return GetEventHandler()->ProcessEvent(event
);
952 void wxListBox::SetCurrentItem(int n
)
954 if ( n
!= m_current
)
956 if ( m_current
!= -1 )
957 RefreshItem(m_current
);
961 if ( m_current
!= -1 )
963 m_currentChanged
= true;
965 RefreshItem(m_current
);
968 //else: nothing to do
971 bool wxListBox::FindItem(const wxString
& prefix
, bool strictlyAfter
)
973 unsigned int count
= GetCount();
976 // empty listbox, we can't find anything in it
980 // start either from the current item or from the next one if strictlyAfter
985 // the following line will set first correctly to 0 if there is no
986 // selection (m_current == -1)
987 first
= m_current
== (int)(count
- 1) ? 0 : m_current
+ 1;
989 else // start with the current
991 first
= m_current
== -1 ? 0 : m_current
;
994 int last
= first
== 0 ? count
- 1 : first
- 1;
996 // if this is not true we'd never exit from the loop below!
997 wxASSERT_MSG( first
< (int)count
&& last
< (int)count
, _T("logic error") );
999 // precompute it outside the loop
1000 size_t len
= prefix
.length();
1002 // loop over all items in the listbox
1003 for ( int item
= first
; item
!= (int)last
; item
< (int)(count
- 1) ? item
++ : item
= 0 )
1005 if ( wxStrnicmp(this->GetString(item
).c_str(), prefix
, len
) == 0 )
1007 SetCurrentItem(item
);
1009 if ( !(GetWindowStyle() & wxLB_MULTIPLE
) )
1012 SelectAndNotify(item
);
1014 if ( GetWindowStyle() & wxLB_EXTENDED
)
1015 AnchorSelection(item
);
1026 void wxListBox::EnsureVisible(int n
)
1028 if ( m_updateScrollbarY
)
1032 m_updateScrollbarX
=
1033 m_updateScrollbarY
= false;
1039 void wxListBox::DoEnsureVisible(int n
)
1041 if ( !m_showScrollbarY
)
1043 // nothing to do - everything is shown anyhow
1048 GetViewStart(0, &first
);
1051 // we need to scroll upwards, so make the current item appear on top
1052 // of the shown range
1057 int last
= first
+ GetClientSize().y
/ GetLineHeight() - 1;
1060 // scroll down: the current item appears at the bottom of the
1062 Scroll(0, n
- (last
- first
));
1067 void wxListBox::ChangeCurrent(int diff
)
1069 int current
= m_current
== -1 ? 0 : m_current
;
1073 int last
= GetCount() - 1;
1076 else if ( current
> last
)
1079 SetCurrentItem(current
);
1082 void wxListBox::ExtendSelection(int itemTo
)
1084 // if we don't have the explicit values for selection start/end, make them
1086 if ( m_selAnchor
== -1 )
1087 m_selAnchor
= m_current
;
1092 // swap the start/end of selection range if necessary
1093 int itemFrom
= m_selAnchor
;
1094 if ( itemFrom
> itemTo
)
1096 int itemTmp
= itemFrom
;
1101 // the selection should now include all items in the range between the
1102 // anchor and the specified item and only them
1105 for ( n
= 0; n
< itemFrom
; n
++ )
1110 for ( ; n
<= itemTo
; n
++ )
1115 unsigned int count
= GetCount();
1116 for ( ; n
< (int)count
; n
++ )
1122 void wxListBox::DoSelect(int item
, bool sel
)
1126 // go to this item first
1127 SetCurrentItem(item
);
1130 // the current item is the one we want to change: either it was just
1131 // changed above to be the same as item or item == -1 in which we case we
1132 // are supposed to use the current one anyhow
1133 if ( m_current
!= -1 )
1136 SetSelection(m_current
, sel
);
1140 void wxListBox::SelectAndNotify(int item
)
1144 SendEvent(wxEVT_COMMAND_LISTBOX_SELECTED
);
1147 void wxListBox::Activate(int item
)
1150 SetCurrentItem(item
);
1154 if ( !(GetWindowStyle() & wxLB_MULTIPLE
) )
1163 SendEvent(wxEVT_COMMAND_LISTBOX_DOUBLECLICKED
);
1167 // ----------------------------------------------------------------------------
1169 // ----------------------------------------------------------------------------
1172 The numArg here is the listbox item index while the strArg is used
1173 differently for the different actions:
1175 a) for wxACTION_LISTBOX_FIND it has the natural meaning: this is the string
1178 b) for wxACTION_LISTBOX_SELECT and wxACTION_LISTBOX_EXTENDSEL it is used
1179 to decide if the listbox should send the notification event (it is empty)
1180 or not (it is not): this allows us to reuse the same action for when the
1181 user is dragging the mouse when it has been released although in the
1182 first case no notification is sent while in the second it is sent.
1184 bool wxListBox::PerformAction(const wxControlAction
& action
,
1186 const wxString
& strArg
)
1188 int item
= (int)numArg
;
1190 if ( action
== wxACTION_LISTBOX_SETFOCUS
)
1192 SetCurrentItem(item
);
1194 else if ( action
== wxACTION_LISTBOX_ACTIVATE
)
1198 else if ( action
== wxACTION_LISTBOX_TOGGLE
)
1203 if ( IsSelected(item
) )
1206 SelectAndNotify(item
);
1208 else if ( action
== wxACTION_LISTBOX_SELECT
)
1212 if ( strArg
.empty() )
1213 SelectAndNotify(item
);
1217 else if ( action
== wxACTION_LISTBOX_SELECTADD
)
1219 else if ( action
== wxACTION_LISTBOX_UNSELECT
)
1221 else if ( action
== wxACTION_LISTBOX_MOVEDOWN
)
1223 else if ( action
== wxACTION_LISTBOX_MOVEUP
)
1225 else if ( action
== wxACTION_LISTBOX_PAGEDOWN
)
1226 ChangeCurrent(GetItemsPerPage());
1227 else if ( action
== wxACTION_LISTBOX_PAGEUP
)
1228 ChangeCurrent(-GetItemsPerPage());
1229 else if ( action
== wxACTION_LISTBOX_START
)
1231 else if ( action
== wxACTION_LISTBOX_END
)
1232 SetCurrentItem(GetCount() - 1);
1233 else if ( action
== wxACTION_LISTBOX_UNSELECTALL
)
1235 else if ( action
== wxACTION_LISTBOX_EXTENDSEL
)
1236 ExtendSelection(item
);
1237 else if ( action
== wxACTION_LISTBOX_FIND
)
1238 FindNextItem(strArg
);
1239 else if ( action
== wxACTION_LISTBOX_ANCHOR
)
1240 AnchorSelection(item
== -1 ? m_current
: item
);
1241 else if ( action
== wxACTION_LISTBOX_SELECTALL
||
1242 action
== wxACTION_LISTBOX_SELTOGGLE
)
1243 wxFAIL_MSG(_T("unimplemented yet"));
1245 return wxControl::PerformAction(action
, numArg
, strArg
);
1251 wxInputHandler
*wxListBox::GetStdInputHandler(wxInputHandler
*handlerDef
)
1253 static wxStdListboxInputHandler
s_handler(handlerDef
);
1258 // ============================================================================
1259 // implementation of wxStdListboxInputHandler
1260 // ============================================================================
1262 wxStdListboxInputHandler::wxStdListboxInputHandler(wxInputHandler
*handler
,
1263 bool toggleOnPressAlways
)
1264 : wxStdInputHandler(handler
)
1267 m_toggleOnPressAlways
= toggleOnPressAlways
;
1268 m_actionMouse
= wxACTION_NONE
;
1269 m_trackMouseOutside
= true;
1272 int wxStdListboxInputHandler::HitTest(const wxListBox
*lbox
,
1273 const wxMouseEvent
& event
)
1275 int item
= HitTestUnsafe(lbox
, event
);
1277 return FixItemIndex(lbox
, item
);
1280 int wxStdListboxInputHandler::HitTestUnsafe(const wxListBox
*lbox
,
1281 const wxMouseEvent
& event
)
1283 wxPoint pt
= event
.GetPosition();
1284 pt
-= lbox
->GetClientAreaOrigin();
1286 lbox
->CalcUnscrolledPosition(0, pt
.y
, NULL
, &y
);
1287 return y
/ lbox
->GetLineHeight();
1290 int wxStdListboxInputHandler::FixItemIndex(const wxListBox
*lbox
,
1295 // mouse is above the first item
1298 else if ( (unsigned int)item
>= lbox
->GetCount() )
1300 // mouse is below the last item
1301 item
= lbox
->GetCount() - 1;
1307 bool wxStdListboxInputHandler::IsValidIndex(const wxListBox
*lbox
, int item
)
1309 return item
>= 0 && (unsigned int)item
< lbox
->GetCount();
1313 wxStdListboxInputHandler::SetupCapture(wxListBox
*lbox
,
1314 const wxMouseEvent
& event
,
1317 // we currently only allow selecting with the left mouse button, if we
1318 // do need to allow using other buttons too we might use the code
1321 m_btnCapture
= event
.LeftDown()
1330 wxControlAction action
;
1331 if ( lbox
->HasMultipleSelection() )
1333 if ( lbox
->GetWindowStyle() & wxLB_MULTIPLE
)
1335 if ( m_toggleOnPressAlways
)
1337 // toggle the item right now
1338 action
= wxACTION_LISTBOX_TOGGLE
;
1342 m_actionMouse
= wxACTION_LISTBOX_SETFOCUS
;
1344 else // wxLB_EXTENDED listbox
1346 // simple click in an extended sel listbox clears the old
1347 // selection and adds the clicked item to it then, ctrl-click
1348 // toggles an item to it and shift-click adds a range between
1349 // the old selection anchor and the clicked item
1350 if ( event
.ControlDown() )
1352 lbox
->PerformAction(wxACTION_LISTBOX_ANCHOR
, item
);
1354 action
= wxACTION_LISTBOX_TOGGLE
;
1356 else if ( event
.ShiftDown() )
1358 action
= wxACTION_LISTBOX_EXTENDSEL
;
1360 else // simple click
1362 lbox
->PerformAction(wxACTION_LISTBOX_ANCHOR
, item
);
1364 action
= wxACTION_LISTBOX_SELECT
;
1367 m_actionMouse
= wxACTION_LISTBOX_EXTENDSEL
;
1370 else // single selection
1373 action
= wxACTION_LISTBOX_SELECT
;
1376 // by default we always do track it
1377 m_trackMouseOutside
= true;
1382 bool wxStdListboxInputHandler::HandleKey(wxInputConsumer
*consumer
,
1383 const wxKeyEvent
& event
,
1386 // we're only interested in the key press events
1387 if ( pressed
&& !event
.AltDown() )
1389 bool isMoveCmd
= true;
1390 int style
= consumer
->GetInputWindow()->GetWindowStyle();
1392 wxControlAction action
;
1395 int keycode
= event
.GetKeyCode();
1400 action
= wxACTION_LISTBOX_MOVEUP
;
1404 action
= wxACTION_LISTBOX_MOVEDOWN
;
1408 action
= wxACTION_LISTBOX_PAGEUP
;
1412 action
= wxACTION_LISTBOX_PAGEDOWN
;
1416 action
= wxACTION_LISTBOX_START
;
1420 action
= wxACTION_LISTBOX_END
;
1425 if ( style
& wxLB_MULTIPLE
)
1427 action
= wxACTION_LISTBOX_TOGGLE
;
1433 action
= wxACTION_LISTBOX_ACTIVATE
;
1438 if ( (keycode
< 255) && wxIsalnum((wxChar
)keycode
) )
1440 action
= wxACTION_LISTBOX_FIND
;
1441 strArg
= (wxChar
)keycode
;
1445 if ( !action
.IsEmpty() )
1447 consumer
->PerformAction(action
, -1, strArg
);
1451 if ( style
& wxLB_SINGLE
)
1453 // the current item is always the one selected
1454 consumer
->PerformAction(wxACTION_LISTBOX_SELECT
);
1456 else if ( style
& wxLB_EXTENDED
)
1458 if ( event
.ShiftDown() )
1459 consumer
->PerformAction(wxACTION_LISTBOX_EXTENDSEL
);
1462 // select the item and make it the new selection anchor
1463 consumer
->PerformAction(wxACTION_LISTBOX_SELECT
);
1464 consumer
->PerformAction(wxACTION_LISTBOX_ANCHOR
);
1467 //else: nothing to do for multiple selection listboxes
1474 return wxStdInputHandler::HandleKey(consumer
, event
, pressed
);
1477 bool wxStdListboxInputHandler::HandleMouse(wxInputConsumer
*consumer
,
1478 const wxMouseEvent
& event
)
1480 wxListBox
*lbox
= wxStaticCast(consumer
->GetInputWindow(), wxListBox
);
1481 int item
= HitTest(lbox
, event
);
1482 wxControlAction action
;
1484 // when the left mouse button is pressed, capture the mouse and track the
1485 // item under mouse (if the mouse leaves the window, we will still be
1486 // getting the mouse move messages generated by wxScrollWindow)
1487 if ( event
.LeftDown() )
1489 // capture the mouse to track the selected item
1490 lbox
->CaptureMouse();
1492 action
= SetupCapture(lbox
, event
, item
);
1494 else if ( m_btnCapture
&& event
.ButtonUp(m_btnCapture
) )
1496 // when the left mouse button is released, release the mouse too
1497 wxWindow
*winCapture
= wxWindow::GetCapture();
1500 winCapture
->ReleaseMouse();
1503 action
= m_actionMouse
;
1505 //else: the mouse wasn't presed over the listbox, only released here
1507 else if ( event
.LeftDClick() )
1509 action
= wxACTION_LISTBOX_ACTIVATE
;
1512 if ( !action
.IsEmpty() )
1514 lbox
->PerformAction(action
, item
);
1519 return wxStdInputHandler::HandleMouse(consumer
, event
);
1522 bool wxStdListboxInputHandler::HandleMouseMove(wxInputConsumer
*consumer
,
1523 const wxMouseEvent
& event
)
1525 wxWindow
*winCapture
= wxWindow::GetCapture();
1526 if ( winCapture
&& (event
.GetEventObject() == winCapture
) )
1528 wxListBox
*lbox
= wxStaticCast(consumer
->GetInputWindow(), wxListBox
);
1530 if ( !m_btnCapture
|| !m_trackMouseOutside
)
1532 // someone captured the mouse for us (we always set m_btnCapture
1533 // when we do it ourselves): in this case we only react to
1534 // the mouse messages when they happen inside the listbox
1535 if ( lbox
->HitTest(event
.GetPosition()) != wxHT_WINDOW_INSIDE
)
1539 int item
= HitTest(lbox
, event
);
1540 if ( !m_btnCapture
)
1542 // now that we have the mouse inside the listbox, do capture it
1543 // normally - but ensure that we will still ignore the outside
1545 SetupCapture(lbox
, event
, item
);
1547 m_trackMouseOutside
= false;
1550 if ( IsValidIndex(lbox
, item
) )
1552 // pass something into strArg to tell the listbox that it shouldn't
1553 // send the notification message: see PerformAction() above
1554 lbox
->PerformAction(m_actionMouse
, item
, _T("no"));
1556 // else: don't pass invalid index to the listbox
1558 else // we don't have capture any more
1562 // if we lost capture unexpectedly (someone else took the capture
1563 // from us), return to a consistent state
1568 return wxStdInputHandler::HandleMouseMove(consumer
, event
);
1571 #endif // wxUSE_LISTBOX