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 BEGIN_EVENT_TABLE(wxListBox
, wxListBoxBase
)
101 EVT_SIZE(wxListBox::OnSize
)
104 // ----------------------------------------------------------------------------
106 // ----------------------------------------------------------------------------
108 void wxListBox::Init()
110 // will be calculated later when needed
116 m_strings
.unsorted
= NULL
;
118 // no items hence no current item
121 m_currentChanged
= false;
123 // no need to update anything initially
126 // no scrollbars to show nor update
130 m_showScrollbarY
= false;
133 wxListBox::wxListBox(wxWindow
*parent
,
137 const wxArrayString
& choices
,
139 const wxValidator
& validator
,
140 const wxString
&name
)
141 :wxScrollHelper(this)
145 Create(parent
, id
, pos
, size
, choices
, style
, validator
, name
);
148 bool wxListBox::Create(wxWindow
*parent
,
152 const wxArrayString
& choices
,
154 const wxValidator
& validator
,
155 const wxString
&name
)
157 wxCArrayString
chs(choices
);
159 return Create(parent
, id
, pos
, size
, chs
.GetCount(), chs
.GetStrings(),
160 style
, validator
, name
);
163 bool wxListBox::Create(wxWindow
*parent
,
168 const wxString choices
[],
170 const wxValidator
& validator
,
171 const wxString
&name
)
173 // for compatibility accept both the new and old styles - they mean the
175 if ( style
& wxLB_ALWAYS_SB
)
176 style
|= wxALWAYS_SHOW_SB
;
178 // if we don't have neither multiple nor extended flag, we must have the
179 // single selection listbox
180 if ( !(style
& (wxLB_MULTIPLE
| wxLB_EXTENDED
)) )
181 style
|= wxLB_SINGLE
;
183 #if wxUSE_TWO_WINDOWS
184 style
|= wxVSCROLL
|wxHSCROLL
;
185 if ((style
& wxBORDER_MASK
) == 0)
186 style
|= wxBORDER_SUNKEN
;
189 if ( !wxControl::Create(parent
, id
, pos
, size
, style
,
194 m_strings
.sorted
= new wxSortedArrayString
;
196 m_strings
.unsorted
= new wxArrayString
;
200 SetInitialSize(size
);
202 CreateInputHandler(wxINP_HANDLER_LISTBOX
);
207 wxListBox::~wxListBox()
209 // call this just to free the client data -- and avoid leaking memory
213 delete m_strings
.sorted
;
215 delete m_strings
.unsorted
;
217 m_strings
.sorted
= NULL
;
220 // ----------------------------------------------------------------------------
222 // ----------------------------------------------------------------------------
224 unsigned int wxListBox::GetCount() const
226 return IsSorted() ? m_strings
.sorted
->size()
227 : m_strings
.unsorted
->size();
230 wxString
wxListBox::GetString(unsigned int n
) const
232 return IsSorted() ? m_strings
.sorted
->Item(n
)
233 : m_strings
.unsorted
->Item(n
);
236 int wxListBox::FindString(const wxString
& s
, bool bCase
) const
238 return IsSorted() ? m_strings
.sorted
->Index(s
, bCase
)
239 : m_strings
.unsorted
->Index(s
, bCase
);
242 // ----------------------------------------------------------------------------
243 // adding/inserting strings
244 // ----------------------------------------------------------------------------
246 int wxListBox::DoInsertItems(const wxArrayStringsAdapter
& items
,
249 wxClientDataType type
)
251 int idx
= wxNOT_FOUND
;
253 const unsigned int numItems
= items
.GetCount();
254 for ( unsigned int i
= 0; i
< numItems
; ++i
)
256 const wxString
& item
= items
[i
];
257 idx
= IsSorted() ? m_strings
.sorted
->Add(item
)
258 : (m_strings
.unsorted
->Insert(item
, pos
), pos
++);
260 m_itemsClientData
.Insert(NULL
, idx
);
261 AssignNewItemClientData(idx
, clientData
, i
, type
);
263 // call the wxCheckListBox hook
267 // the number of items has changed so we might have to show the scrollbar
268 m_updateScrollbarY
= true;
270 // the max width also might have changed - just recalculate it instead of
271 // keeping track of it here, this is probably more efficient for a typical
273 RefreshHorzScrollbar();
275 // note that we have to refresh all the items after the ones we inserted,
276 // not just these items
277 RefreshFromItemToEnd(pos
);
282 void wxListBox::SetString(unsigned int n
, const wxString
& s
)
284 wxCHECK_RET( !IsSorted(), wxT("can't set string in sorted listbox") );
287 (*m_strings
.sorted
)[n
] = s
;
289 (*m_strings
.unsorted
)[n
] = s
;
291 if ( HasHorzScrollbar() )
293 // we need to update m_maxWidth as changing the string may cause the
294 // horz scrollbar [dis]appear
297 GetTextExtent(s
, &width
, NULL
);
299 // it might have increased if the new string is long
300 if ( width
> m_maxWidth
)
304 m_updateScrollbarX
= true;
306 // or also decreased if the old string was the longest one
307 else if ( n
== (unsigned int)m_maxWidthItem
)
309 RefreshHorzScrollbar();
316 // ----------------------------------------------------------------------------
318 // ----------------------------------------------------------------------------
320 void wxListBox::DoClear()
323 m_strings
.sorted
->Clear();
325 m_strings
.unsorted
->Clear();
327 m_itemsClientData
.Clear();
328 m_selections
.Clear();
332 m_updateScrollbarY
= true;
334 RefreshHorzScrollbar();
339 void wxListBox::DoDeleteOneItem(unsigned int n
)
341 wxCHECK_RET( IsValid(n
),
342 wxT("invalid index in wxListBox::Delete") );
344 // do it before removing the index as otherwise the last item will not be
345 // refreshed (as GetCount() will be decremented)
346 RefreshFromItemToEnd(n
);
349 m_strings
.sorted
->RemoveAt(n
);
351 m_strings
.unsorted
->RemoveAt(n
);
353 m_itemsClientData
.RemoveAt(n
);
355 // when the item disappears we must not keep using its index
356 if ( (int)n
== m_current
)
360 else if ( (int)n
< m_current
)
364 //else: current item may stay
366 // update the selections array: the indices of all seletected items after
367 // the one being deleted must change and the item itselfm ust be removed
368 int index
= wxNOT_FOUND
;
369 unsigned int count
= m_selections
.GetCount();
370 for ( unsigned int item
= 0; item
< count
; item
++ )
372 if ( m_selections
[item
] == (int)n
)
374 // remember to delete it later
377 else if ( m_selections
[item
] > (int)n
)
379 // to account for the index shift
380 m_selections
[item
]--;
382 //else: nothing changed for this one
385 if ( index
!= wxNOT_FOUND
)
387 m_selections
.RemoveAt(index
);
390 // the number of items has changed, hence the scrollbar may disappear
391 m_updateScrollbarY
= true;
393 // finally, if the longest item was deleted the scrollbar may disappear
394 if ( (int)n
== m_maxWidthItem
)
396 RefreshHorzScrollbar();
400 // ----------------------------------------------------------------------------
401 // client data handling
402 // ----------------------------------------------------------------------------
404 void wxListBox::DoSetItemClientData(unsigned int n
, void* clientData
)
406 m_itemsClientData
[n
] = clientData
;
409 void *wxListBox::DoGetItemClientData(unsigned int n
) const
411 return m_itemsClientData
[n
];
414 // ----------------------------------------------------------------------------
416 // ----------------------------------------------------------------------------
418 void wxListBox::DoSetSelection(int n
, bool select
)
422 if ( n
== wxNOT_FOUND
)
424 if ( !HasMultipleSelection() )
426 // selecting wxNOT_FOUND is documented to deselect all items
431 else if ( m_selections
.Index(n
) == wxNOT_FOUND
)
433 if ( !HasMultipleSelection() )
435 // selecting an item in a single selection listbox deselects
444 //else: already selected
448 int index
= m_selections
.Index(n
);
449 if ( index
!= wxNOT_FOUND
)
451 m_selections
.RemoveAt(index
);
458 // sanity check: a single selection listbox can't have more than one item
460 wxASSERT_MSG( HasMultipleSelection() || (m_selections
.GetCount() < 2),
461 wxT("multiple selected items in single selection lbox?") );
465 // the newly selected item becomes the current one
470 int wxListBox::GetSelection() const
472 wxCHECK_MSG( !HasMultipleSelection(), wxNOT_FOUND
,
473 wxT("use wxListBox::GetSelections for ths listbox") );
475 return m_selections
.IsEmpty() ? wxNOT_FOUND
: m_selections
[0];
478 static int wxCMPFUNC_CONV
wxCompareInts(int *n
, int *m
)
483 int wxListBox::GetSelections(wxArrayInt
& selections
) const
485 // always return sorted array to the user
486 selections
= m_selections
;
487 unsigned int count
= m_selections
.GetCount();
489 // don't call sort on an empty array
492 selections
.Sort(wxCompareInts
);
498 // ----------------------------------------------------------------------------
499 // refresh logic: we use delayed refreshing which allows to avoid multiple
500 // refreshes (and hence flicker) in case when several listbox items are
501 // added/deleted/changed subsequently
502 // ----------------------------------------------------------------------------
504 void wxListBox::RefreshFromItemToEnd(int from
)
506 RefreshItems(from
, GetCount() - from
);
509 void wxListBox::RefreshItems(int from
, int count
)
511 switch ( m_updateCount
)
515 m_updateCount
= count
;
519 // we refresh everything anyhow
523 // add these items to the others which we have to refresh
524 if ( m_updateFrom
< from
)
526 count
+= from
- m_updateFrom
;
527 if ( m_updateCount
< count
)
528 m_updateCount
= count
;
530 else // m_updateFrom >= from
532 int updateLast
= wxMax(m_updateFrom
+ m_updateCount
,
535 m_updateCount
= updateLast
- m_updateFrom
;
540 void wxListBox::RefreshItem(int n
)
542 switch ( m_updateCount
)
545 // refresh this item only
551 // we refresh everything anyhow
555 // add this item to the others which we have to refresh
556 if ( m_updateFrom
< n
)
558 if ( m_updateCount
< n
- m_updateFrom
+ 1 )
559 m_updateCount
= n
- m_updateFrom
+ 1;
561 else // n <= m_updateFrom
563 m_updateCount
+= m_updateFrom
- n
;
569 void wxListBox::RefreshAll()
574 void wxListBox::RefreshHorzScrollbar()
576 m_maxWidth
= 0; // recalculate it
577 m_updateScrollbarX
= true;
580 void wxListBox::UpdateScrollbars()
582 wxSize size
= GetClientSize();
584 // is our height enough to show all items?
585 unsigned int nLines
= GetCount();
586 wxCoord lineHeight
= GetLineHeight();
587 bool showScrollbarY
= (int)nLines
*lineHeight
> size
.y
;
589 // check the width too if required
590 wxCoord charWidth
, maxWidth
;
592 if ( HasHorzScrollbar() )
594 charWidth
= GetCharWidth();
595 maxWidth
= GetMaxWidth();
596 showScrollbarX
= maxWidth
> size
.x
;
598 else // never show it
600 charWidth
= maxWidth
= 0;
601 showScrollbarX
= false;
604 // what should be the scrollbar range now?
605 int scrollRangeX
= showScrollbarX
606 ? (maxWidth
+ charWidth
- 1) / charWidth
+ 2 // FIXME
608 int scrollRangeY
= showScrollbarY
610 (size
.y
% lineHeight
+ lineHeight
- 1) / lineHeight
613 // reset scrollbars if something changed: either the visibility status
614 // or the range of a scrollbar which is shown
615 if ( (showScrollbarY
!= m_showScrollbarY
) ||
616 (showScrollbarX
!= m_showScrollbarX
) ||
617 (showScrollbarY
&& (scrollRangeY
!= m_scrollRangeY
)) ||
618 (showScrollbarX
&& (scrollRangeX
!= m_scrollRangeX
)) )
621 GetViewStart(&x
, &y
);
622 SetScrollbars(charWidth
, lineHeight
,
623 scrollRangeX
, scrollRangeY
,
626 m_showScrollbarX
= showScrollbarX
;
627 m_showScrollbarY
= showScrollbarY
;
629 m_scrollRangeX
= scrollRangeX
;
630 m_scrollRangeY
= scrollRangeY
;
634 void wxListBox::UpdateItems()
636 // only refresh the items which must be refreshed
637 if ( m_updateCount
== -1 )
640 wxLogTrace(wxT("listbox"), wxT("Refreshing all"));
646 wxSize size
= GetClientSize();
649 rect
.height
= size
.y
;
650 rect
.y
+= m_updateFrom
*GetLineHeight();
651 rect
.height
= m_updateCount
*GetLineHeight();
653 // we don't need to calculate x position as we always refresh the
655 CalcScrolledPosition(0, rect
.y
, NULL
, &rect
.y
);
657 wxLogTrace(wxT("listbox"), wxT("Refreshing items %d..%d (%d-%d)"),
658 m_updateFrom
, m_updateFrom
+ m_updateCount
- 1,
659 rect
.GetTop(), rect
.GetBottom());
661 Refresh(true, &rect
);
665 void wxListBox::OnInternalIdle()
667 if ( m_updateScrollbarY
|| m_updateScrollbarX
)
672 m_updateScrollbarY
= false;
675 if ( m_currentChanged
)
677 DoEnsureVisible(m_current
);
679 m_currentChanged
= false;
688 wxListBoxBase::OnInternalIdle();
691 // ----------------------------------------------------------------------------
693 // ----------------------------------------------------------------------------
695 wxBorder
wxListBox::GetDefaultBorder() const
697 return wxBORDER_SUNKEN
;
700 void wxListBox::DoDraw(wxControlRenderer
*renderer
)
702 // adjust the DC to account for scrolling
703 wxDC
& dc
= renderer
->GetDC();
705 dc
.SetFont(GetFont());
707 // get the update rect
708 wxRect rectUpdate
= GetUpdateClientRect();
711 CalcUnscrolledPosition(0, rectUpdate
.GetTop(), NULL
, &yTop
);
712 CalcUnscrolledPosition(0, rectUpdate
.GetBottom(), NULL
, &yBottom
);
714 // get the items which must be redrawn
715 wxCoord lineHeight
= GetLineHeight();
716 unsigned int itemFirst
= yTop
/ lineHeight
,
717 itemLast
= (yBottom
+ lineHeight
- 1) / lineHeight
,
718 itemMax
= GetCount();
720 if ( itemFirst
>= itemMax
)
723 if ( itemLast
> itemMax
)
727 wxLogTrace(wxT("listbox"), wxT("Repainting items %d..%d"),
728 itemFirst
, itemLast
);
730 DoDrawRange(renderer
, itemFirst
, itemLast
);
733 void wxListBox::DoDrawRange(wxControlRenderer
*renderer
,
734 int itemFirst
, int itemLast
)
736 renderer
->DrawItems(this, itemFirst
, itemLast
);
739 // ----------------------------------------------------------------------------
741 // ----------------------------------------------------------------------------
743 bool wxListBox::SetFont(const wxFont
& font
)
745 if ( !wxControl::SetFont(font
) )
755 void wxListBox::CalcItemsPerPage()
757 m_lineHeight
= GetRenderer()->GetListboxItemHeight(GetCharHeight());
758 m_itemsPerPage
= GetClientSize().y
/ m_lineHeight
;
761 int wxListBox::GetItemsPerPage() const
763 if ( !m_itemsPerPage
)
765 wxConstCast(this, wxListBox
)->CalcItemsPerPage();
768 return m_itemsPerPage
;
771 wxCoord
wxListBox::GetLineHeight() const
775 wxConstCast(this, wxListBox
)->CalcItemsPerPage();
781 wxCoord
wxListBox::GetMaxWidth() const
783 if ( m_maxWidth
== 0 )
785 wxListBox
*self
= wxConstCast(this, wxListBox
);
787 unsigned int count
= GetCount();
788 for ( unsigned int n
= 0; n
< count
; n
++ )
790 GetTextExtent(this->GetString(n
), &width
, NULL
);
791 if ( width
> m_maxWidth
)
793 self
->m_maxWidth
= width
;
794 self
->m_maxWidthItem
= n
;
802 void wxListBox::OnSize(wxSizeEvent
& event
)
804 // recalculate the number of items per page
807 // the scrollbars might [dis]appear
809 m_updateScrollbarY
= true;
814 void wxListBox::DoSetFirstItem(int n
)
819 void wxListBox::DoSetSize(int x
, int y
,
820 int width
, int height
,
823 if ( GetWindowStyle() & wxLB_INT_HEIGHT
)
825 // we must round up the height to an entire number of rows
827 // the client area must contain an int number of rows, so take borders
829 wxRect rectBorders
= GetRenderer()->GetBorderDimensions(GetBorder());
830 wxCoord hBorders
= rectBorders
.y
+ rectBorders
.height
;
832 wxCoord hLine
= GetLineHeight();
833 height
= ((height
- hBorders
+ hLine
- 1) / hLine
)*hLine
+ hBorders
;
836 wxListBoxBase::DoSetSize(x
, y
, width
, height
, sizeFlags
);
839 wxSize
wxListBox::DoGetBestClientSize() const
844 unsigned int count
= GetCount();
845 for ( unsigned int n
= 0; n
< count
; n
++ )
848 GetTextExtent(this->GetString(n
), &w
, &h
);
856 // if the listbox is empty, still give it some non zero (even if
857 // arbitrary) size - otherwise, leave small margin around the strings
861 width
+= 3*GetCharWidth();
864 height
= GetCharHeight();
866 // we need the height of the entire listbox, not just of one line
867 height
*= wxMax(count
, 7);
869 return wxSize(width
, height
);
872 // ----------------------------------------------------------------------------
874 // ----------------------------------------------------------------------------
876 bool wxListBox::SendEvent(wxEventType type
, int item
)
878 wxCommandEvent
event(type
, m_windowId
);
879 event
.SetEventObject(this);
881 // use the current item by default
887 // client data and string parameters only make sense if we have an item
890 if ( HasClientObjectData() )
891 event
.SetClientObject(GetClientObject(item
));
892 else if ( HasClientUntypedData() )
893 event
.SetClientData(GetClientData(item
));
895 event
.SetString(GetString(item
));
900 return GetEventHandler()->ProcessEvent(event
);
903 void wxListBox::SetCurrentItem(int n
)
905 if ( n
!= m_current
)
907 if ( m_current
!= -1 )
908 RefreshItem(m_current
);
912 if ( m_current
!= -1 )
914 m_currentChanged
= true;
916 RefreshItem(m_current
);
919 //else: nothing to do
922 bool wxListBox::FindItem(const wxString
& prefix
, bool strictlyAfter
)
924 unsigned int count
= GetCount();
927 // empty listbox, we can't find anything in it
931 // start either from the current item or from the next one if strictlyAfter
936 // the following line will set first correctly to 0 if there is no
937 // selection (m_current == -1)
938 first
= m_current
== (int)(count
- 1) ? 0 : m_current
+ 1;
940 else // start with the current
942 first
= m_current
== -1 ? 0 : m_current
;
945 int last
= first
== 0 ? count
- 1 : first
- 1;
947 // if this is not true we'd never exit from the loop below!
948 wxASSERT_MSG( first
< (int)count
&& last
< (int)count
, wxT("logic error") );
950 // precompute it outside the loop
951 size_t len
= prefix
.length();
953 // loop over all items in the listbox
954 for ( int item
= first
; item
!= (int)last
; item
< (int)(count
- 1) ? item
++ : item
= 0 )
956 if ( wxStrnicmp(this->GetString(item
).c_str(), prefix
, len
) == 0 )
958 SetCurrentItem(item
);
960 if ( !(GetWindowStyle() & wxLB_MULTIPLE
) )
963 SelectAndNotify(item
);
965 if ( GetWindowStyle() & wxLB_EXTENDED
)
966 AnchorSelection(item
);
977 void wxListBox::EnsureVisible(int n
)
979 if ( m_updateScrollbarY
)
984 m_updateScrollbarY
= false;
990 void wxListBox::DoEnsureVisible(int n
)
992 if ( !m_showScrollbarY
)
994 // nothing to do - everything is shown anyhow
999 GetViewStart(0, &first
);
1002 // we need to scroll upwards, so make the current item appear on top
1003 // of the shown range
1008 int last
= first
+ GetClientSize().y
/ GetLineHeight() - 1;
1011 // scroll down: the current item appears at the bottom of the
1013 Scroll(0, n
- (last
- first
));
1018 void wxListBox::ChangeCurrent(int diff
)
1020 int current
= m_current
== -1 ? 0 : m_current
;
1024 int last
= GetCount() - 1;
1027 else if ( current
> last
)
1030 SetCurrentItem(current
);
1033 void wxListBox::ExtendSelection(int itemTo
)
1035 // if we don't have the explicit values for selection start/end, make them
1037 if ( m_selAnchor
== -1 )
1038 m_selAnchor
= m_current
;
1043 // swap the start/end of selection range if necessary
1044 int itemFrom
= m_selAnchor
;
1045 if ( itemFrom
> itemTo
)
1047 int itemTmp
= itemFrom
;
1052 // the selection should now include all items in the range between the
1053 // anchor and the specified item and only them
1056 for ( n
= 0; n
< itemFrom
; n
++ )
1061 for ( ; n
<= itemTo
; n
++ )
1066 unsigned int count
= GetCount();
1067 for ( ; n
< (int)count
; n
++ )
1073 void wxListBox::DoSelect(int item
, bool sel
)
1077 // go to this item first
1078 SetCurrentItem(item
);
1081 // the current item is the one we want to change: either it was just
1082 // changed above to be the same as item or item == -1 in which we case we
1083 // are supposed to use the current one anyhow
1084 if ( m_current
!= -1 )
1087 SetSelection(m_current
, sel
);
1091 void wxListBox::SelectAndNotify(int item
)
1095 SendEvent(wxEVT_COMMAND_LISTBOX_SELECTED
);
1098 void wxListBox::Activate(int item
)
1101 SetCurrentItem(item
);
1105 if ( !(GetWindowStyle() & wxLB_MULTIPLE
) )
1114 SendEvent(wxEVT_COMMAND_LISTBOX_DOUBLECLICKED
);
1118 // ----------------------------------------------------------------------------
1120 // ----------------------------------------------------------------------------
1123 The numArg here is the listbox item index while the strArg is used
1124 differently for the different actions:
1126 a) for wxACTION_LISTBOX_FIND it has the natural meaning: this is the string
1129 b) for wxACTION_LISTBOX_SELECT and wxACTION_LISTBOX_EXTENDSEL it is used
1130 to decide if the listbox should send the notification event (it is empty)
1131 or not (it is not): this allows us to reuse the same action for when the
1132 user is dragging the mouse when it has been released although in the
1133 first case no notification is sent while in the second it is sent.
1135 bool wxListBox::PerformAction(const wxControlAction
& action
,
1137 const wxString
& strArg
)
1139 int item
= (int)numArg
;
1141 if ( action
== wxACTION_LISTBOX_SETFOCUS
)
1143 SetCurrentItem(item
);
1145 else if ( action
== wxACTION_LISTBOX_ACTIVATE
)
1149 else if ( action
== wxACTION_LISTBOX_TOGGLE
)
1154 if ( IsSelected(item
) )
1157 SelectAndNotify(item
);
1159 else if ( action
== wxACTION_LISTBOX_SELECT
)
1163 if ( strArg
.empty() )
1164 SelectAndNotify(item
);
1168 else if ( action
== wxACTION_LISTBOX_SELECTADD
)
1170 else if ( action
== wxACTION_LISTBOX_UNSELECT
)
1172 else if ( action
== wxACTION_LISTBOX_MOVEDOWN
)
1174 else if ( action
== wxACTION_LISTBOX_MOVEUP
)
1176 else if ( action
== wxACTION_LISTBOX_PAGEDOWN
)
1177 ChangeCurrent(GetItemsPerPage());
1178 else if ( action
== wxACTION_LISTBOX_PAGEUP
)
1179 ChangeCurrent(-GetItemsPerPage());
1180 else if ( action
== wxACTION_LISTBOX_START
)
1182 else if ( action
== wxACTION_LISTBOX_END
)
1183 SetCurrentItem(GetCount() - 1);
1184 else if ( action
== wxACTION_LISTBOX_UNSELECTALL
)
1186 else if ( action
== wxACTION_LISTBOX_EXTENDSEL
)
1187 ExtendSelection(item
);
1188 else if ( action
== wxACTION_LISTBOX_FIND
)
1189 FindNextItem(strArg
);
1190 else if ( action
== wxACTION_LISTBOX_ANCHOR
)
1191 AnchorSelection(item
== -1 ? m_current
: item
);
1192 else if ( action
== wxACTION_LISTBOX_SELECTALL
||
1193 action
== wxACTION_LISTBOX_SELTOGGLE
)
1194 wxFAIL_MSG(wxT("unimplemented yet"));
1196 return wxControl::PerformAction(action
, numArg
, strArg
);
1202 wxInputHandler
*wxListBox::GetStdInputHandler(wxInputHandler
*handlerDef
)
1204 static wxStdListboxInputHandler
s_handler(handlerDef
);
1209 // ============================================================================
1210 // implementation of wxStdListboxInputHandler
1211 // ============================================================================
1213 wxStdListboxInputHandler::wxStdListboxInputHandler(wxInputHandler
*handler
,
1214 bool toggleOnPressAlways
)
1215 : wxStdInputHandler(handler
)
1218 m_toggleOnPressAlways
= toggleOnPressAlways
;
1219 m_actionMouse
= wxACTION_NONE
;
1220 m_trackMouseOutside
= true;
1223 int wxStdListboxInputHandler::HitTest(const wxListBox
*lbox
,
1224 const wxMouseEvent
& event
)
1226 int item
= HitTestUnsafe(lbox
, event
);
1228 return FixItemIndex(lbox
, item
);
1231 int wxStdListboxInputHandler::HitTestUnsafe(const wxListBox
*lbox
,
1232 const wxMouseEvent
& event
)
1234 wxPoint pt
= event
.GetPosition();
1235 pt
-= lbox
->GetClientAreaOrigin();
1237 lbox
->CalcUnscrolledPosition(0, pt
.y
, NULL
, &y
);
1238 return y
/ lbox
->GetLineHeight();
1241 int wxStdListboxInputHandler::FixItemIndex(const wxListBox
*lbox
,
1246 // mouse is above the first item
1249 else if ( (unsigned int)item
>= lbox
->GetCount() )
1251 // mouse is below the last item
1252 item
= lbox
->GetCount() - 1;
1258 bool wxStdListboxInputHandler::IsValidIndex(const wxListBox
*lbox
, int item
)
1260 return item
>= 0 && (unsigned int)item
< lbox
->GetCount();
1264 wxStdListboxInputHandler::SetupCapture(wxListBox
*lbox
,
1265 const wxMouseEvent
& event
,
1268 // we currently only allow selecting with the left mouse button, if we
1269 // do need to allow using other buttons too we might use the code
1272 m_btnCapture
= event
.LeftDown()
1281 wxControlAction action
;
1282 if ( lbox
->HasMultipleSelection() )
1284 if ( lbox
->GetWindowStyle() & wxLB_MULTIPLE
)
1286 if ( m_toggleOnPressAlways
)
1288 // toggle the item right now
1289 action
= wxACTION_LISTBOX_TOGGLE
;
1293 m_actionMouse
= wxACTION_LISTBOX_SETFOCUS
;
1295 else // wxLB_EXTENDED listbox
1297 // simple click in an extended sel listbox clears the old
1298 // selection and adds the clicked item to it then, ctrl-click
1299 // toggles an item to it and shift-click adds a range between
1300 // the old selection anchor and the clicked item
1301 if ( event
.ControlDown() )
1303 lbox
->PerformAction(wxACTION_LISTBOX_ANCHOR
, item
);
1305 action
= wxACTION_LISTBOX_TOGGLE
;
1307 else if ( event
.ShiftDown() )
1309 action
= wxACTION_LISTBOX_EXTENDSEL
;
1311 else // simple click
1313 lbox
->PerformAction(wxACTION_LISTBOX_ANCHOR
, item
);
1315 action
= wxACTION_LISTBOX_SELECT
;
1318 m_actionMouse
= wxACTION_LISTBOX_EXTENDSEL
;
1321 else // single selection
1324 action
= wxACTION_LISTBOX_SELECT
;
1327 // by default we always do track it
1328 m_trackMouseOutside
= true;
1333 bool wxStdListboxInputHandler::HandleKey(wxInputConsumer
*consumer
,
1334 const wxKeyEvent
& event
,
1337 // we're only interested in the key press events
1338 if ( pressed
&& !event
.AltDown() )
1340 bool isMoveCmd
= true;
1341 int style
= consumer
->GetInputWindow()->GetWindowStyle();
1343 wxControlAction action
;
1346 int keycode
= event
.GetKeyCode();
1351 action
= wxACTION_LISTBOX_MOVEUP
;
1355 action
= wxACTION_LISTBOX_MOVEDOWN
;
1359 action
= wxACTION_LISTBOX_PAGEUP
;
1363 action
= wxACTION_LISTBOX_PAGEDOWN
;
1367 action
= wxACTION_LISTBOX_START
;
1371 action
= wxACTION_LISTBOX_END
;
1376 if ( style
& wxLB_MULTIPLE
)
1378 action
= wxACTION_LISTBOX_TOGGLE
;
1384 action
= wxACTION_LISTBOX_ACTIVATE
;
1389 if ( (keycode
< 255) && wxIsalnum((wxChar
)keycode
) )
1391 action
= wxACTION_LISTBOX_FIND
;
1392 strArg
= (wxChar
)keycode
;
1396 if ( !action
.IsEmpty() )
1398 consumer
->PerformAction(action
, -1, strArg
);
1402 if ( style
& wxLB_SINGLE
)
1404 // the current item is always the one selected
1405 consumer
->PerformAction(wxACTION_LISTBOX_SELECT
);
1407 else if ( style
& wxLB_EXTENDED
)
1409 if ( event
.ShiftDown() )
1410 consumer
->PerformAction(wxACTION_LISTBOX_EXTENDSEL
);
1413 // select the item and make it the new selection anchor
1414 consumer
->PerformAction(wxACTION_LISTBOX_SELECT
);
1415 consumer
->PerformAction(wxACTION_LISTBOX_ANCHOR
);
1418 //else: nothing to do for multiple selection listboxes
1425 return wxStdInputHandler::HandleKey(consumer
, event
, pressed
);
1428 bool wxStdListboxInputHandler::HandleMouse(wxInputConsumer
*consumer
,
1429 const wxMouseEvent
& event
)
1431 wxListBox
*lbox
= wxStaticCast(consumer
->GetInputWindow(), wxListBox
);
1432 int item
= HitTest(lbox
, event
);
1433 wxControlAction action
;
1435 // when the left mouse button is pressed, capture the mouse and track the
1436 // item under mouse (if the mouse leaves the window, we will still be
1437 // getting the mouse move messages generated by wxScrollWindow)
1438 if ( event
.LeftDown() )
1440 // capture the mouse to track the selected item
1441 lbox
->CaptureMouse();
1443 action
= SetupCapture(lbox
, event
, item
);
1445 else if ( m_btnCapture
&& event
.ButtonUp(m_btnCapture
) )
1447 // when the left mouse button is released, release the mouse too
1448 wxWindow
*winCapture
= wxWindow::GetCapture();
1451 winCapture
->ReleaseMouse();
1454 action
= m_actionMouse
;
1456 //else: the mouse wasn't presed over the listbox, only released here
1458 else if ( event
.LeftDClick() )
1460 action
= wxACTION_LISTBOX_ACTIVATE
;
1463 if ( !action
.IsEmpty() )
1465 lbox
->PerformAction(action
, item
);
1470 return wxStdInputHandler::HandleMouse(consumer
, event
);
1473 bool wxStdListboxInputHandler::HandleMouseMove(wxInputConsumer
*consumer
,
1474 const wxMouseEvent
& event
)
1476 wxWindow
*winCapture
= wxWindow::GetCapture();
1477 if ( winCapture
&& (event
.GetEventObject() == winCapture
) )
1479 wxListBox
*lbox
= wxStaticCast(consumer
->GetInputWindow(), wxListBox
);
1481 if ( !m_btnCapture
|| !m_trackMouseOutside
)
1483 // someone captured the mouse for us (we always set m_btnCapture
1484 // when we do it ourselves): in this case we only react to
1485 // the mouse messages when they happen inside the listbox
1486 if ( lbox
->HitTest(event
.GetPosition()) != wxHT_WINDOW_INSIDE
)
1490 int item
= HitTest(lbox
, event
);
1491 if ( !m_btnCapture
)
1493 // now that we have the mouse inside the listbox, do capture it
1494 // normally - but ensure that we will still ignore the outside
1496 SetupCapture(lbox
, event
, item
);
1498 m_trackMouseOutside
= false;
1501 if ( IsValidIndex(lbox
, item
) )
1503 // pass something into strArg to tell the listbox that it shouldn't
1504 // send the notification message: see PerformAction() above
1505 lbox
->PerformAction(m_actionMouse
, item
, wxT("no"));
1507 // else: don't pass invalid index to the listbox
1509 else // we don't have capture any more
1513 // if we lost capture unexpectedly (someone else took the capture
1514 // from us), return to a consistent state
1519 return wxStdInputHandler::HandleMouseMove(consumer
, event
);
1522 #endif // wxUSE_LISTBOX