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
, wxControlWithItems
)
102 BEGIN_EVENT_TABLE(wxListBox
, wxListBoxBase
)
103 EVT_SIZE(wxListBox::OnSize
)
106 // ----------------------------------------------------------------------------
108 // ----------------------------------------------------------------------------
110 void wxListBox::Init()
112 // will be calculated later when needed
118 m_strings
.unsorted
= NULL
;
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
,
196 m_strings
.sorted
= new wxSortedArrayString
;
198 m_strings
.unsorted
= new wxArrayString
;
202 SetInitialSize(size
);
204 CreateInputHandler(wxINP_HANDLER_LISTBOX
);
209 wxListBox::~wxListBox()
211 // call this just to free the client data -- and avoid leaking memory
215 delete m_strings
.sorted
;
217 delete m_strings
.unsorted
;
219 m_strings
.sorted
= NULL
;
222 // ----------------------------------------------------------------------------
224 // ----------------------------------------------------------------------------
226 unsigned int wxListBox::GetCount() const
228 return IsSorted() ? m_strings
.sorted
->size()
229 : m_strings
.unsorted
->size();
232 wxString
wxListBox::GetString(unsigned int n
) const
234 return IsSorted() ? m_strings
.sorted
->Item(n
)
235 : m_strings
.unsorted
->Item(n
);
238 int wxListBox::FindString(const wxString
& s
, bool bCase
) const
240 return IsSorted() ? m_strings
.sorted
->Index(s
, bCase
)
241 : m_strings
.unsorted
->Index(s
, bCase
);
244 // ----------------------------------------------------------------------------
245 // adding/inserting strings
246 // ----------------------------------------------------------------------------
248 int wxListBox::DoInsertItems(const wxArrayStringsAdapter
& items
,
251 wxClientDataType type
)
253 int idx
= wxNOT_FOUND
;
255 const unsigned int numItems
= items
.GetCount();
256 for ( unsigned int i
= 0; i
< numItems
; ++i
)
258 const wxString
& item
= items
[i
];
259 idx
= IsSorted() ? m_strings
.sorted
->Add(item
)
260 : (m_strings
.unsorted
->Insert(item
, pos
), pos
++);
262 m_itemsClientData
.Insert(NULL
, idx
);
263 AssignNewItemClientData(idx
, clientData
, i
, type
);
265 // call the wxCheckListBox hook
269 // the number of items has changed so we might have to show the scrollbar
270 m_updateScrollbarY
= true;
272 // the max width also might have changed - just recalculate it instead of
273 // keeping track of it here, this is probably more efficient for a typical
275 RefreshHorzScrollbar();
277 // note that we have to refresh all the items after the ones we inserted,
278 // not just these items
279 RefreshFromItemToEnd(pos
);
284 void wxListBox::SetString(unsigned int n
, const wxString
& s
)
286 wxCHECK_RET( !IsSorted(), wxT("can't set string in sorted listbox") );
289 (*m_strings
.sorted
)[n
] = s
;
291 (*m_strings
.unsorted
)[n
] = s
;
293 if ( HasHorzScrollbar() )
295 // we need to update m_maxWidth as changing the string may cause the
296 // horz scrollbar [dis]appear
299 GetTextExtent(s
, &width
, NULL
);
301 // it might have increased if the new string is long
302 if ( width
> m_maxWidth
)
306 m_updateScrollbarX
= true;
308 // or also decreased if the old string was the longest one
309 else if ( n
== (unsigned int)m_maxWidthItem
)
311 RefreshHorzScrollbar();
318 // ----------------------------------------------------------------------------
320 // ----------------------------------------------------------------------------
322 void wxListBox::DoClear()
325 m_strings
.sorted
->Clear();
327 m_strings
.unsorted
->Clear();
329 m_itemsClientData
.Clear();
330 m_selections
.Clear();
334 m_updateScrollbarY
= true;
336 RefreshHorzScrollbar();
341 void wxListBox::DoDeleteOneItem(unsigned int n
)
343 wxCHECK_RET( IsValid(n
),
344 wxT("invalid index in wxListBox::Delete") );
346 // do it before removing the index as otherwise the last item will not be
347 // refreshed (as GetCount() will be decremented)
348 RefreshFromItemToEnd(n
);
351 m_strings
.sorted
->RemoveAt(n
);
353 m_strings
.unsorted
->RemoveAt(n
);
355 m_itemsClientData
.RemoveAt(n
);
357 // when the item disappears we must not keep using its index
358 if ( (int)n
== m_current
)
362 else if ( (int)n
< m_current
)
366 //else: current item may stay
368 // update the selections array: the indices of all seletected items after
369 // the one being deleted must change and the item itselfm ust be removed
370 int index
= wxNOT_FOUND
;
371 unsigned int count
= m_selections
.GetCount();
372 for ( unsigned int item
= 0; item
< count
; item
++ )
374 if ( m_selections
[item
] == (int)n
)
376 // remember to delete it later
379 else if ( m_selections
[item
] > (int)n
)
381 // to account for the index shift
382 m_selections
[item
]--;
384 //else: nothing changed for this one
387 if ( index
!= wxNOT_FOUND
)
389 m_selections
.RemoveAt(index
);
392 // the number of items has changed, hence the scrollbar may disappear
393 m_updateScrollbarY
= true;
395 // finally, if the longest item was deleted the scrollbar may disappear
396 if ( (int)n
== m_maxWidthItem
)
398 RefreshHorzScrollbar();
402 // ----------------------------------------------------------------------------
403 // client data handling
404 // ----------------------------------------------------------------------------
406 void wxListBox::DoSetItemClientData(unsigned int n
, void* clientData
)
408 m_itemsClientData
[n
] = clientData
;
411 void *wxListBox::DoGetItemClientData(unsigned int n
) const
413 return m_itemsClientData
[n
];
416 // ----------------------------------------------------------------------------
418 // ----------------------------------------------------------------------------
420 void wxListBox::DoSetSelection(int n
, bool select
)
424 if ( n
== wxNOT_FOUND
)
426 if ( !HasMultipleSelection() )
428 // selecting wxNOT_FOUND is documented to deselect all items
433 else if ( m_selections
.Index(n
) == wxNOT_FOUND
)
435 if ( !HasMultipleSelection() )
437 // selecting an item in a single selection listbox deselects
446 //else: already selected
450 int index
= m_selections
.Index(n
);
451 if ( index
!= wxNOT_FOUND
)
453 m_selections
.RemoveAt(index
);
460 // sanity check: a single selection listbox can't have more than one item
462 wxASSERT_MSG( HasMultipleSelection() || (m_selections
.GetCount() < 2),
463 wxT("multiple selected items in single selection lbox?") );
467 // the newly selected item becomes the current one
472 int wxListBox::GetSelection() const
474 wxCHECK_MSG( !HasMultipleSelection(), wxNOT_FOUND
,
475 wxT("use wxListBox::GetSelections for ths listbox") );
477 return m_selections
.IsEmpty() ? wxNOT_FOUND
: m_selections
[0];
480 static int wxCMPFUNC_CONV
wxCompareInts(int *n
, int *m
)
485 int wxListBox::GetSelections(wxArrayInt
& selections
) const
487 // always return sorted array to the user
488 selections
= m_selections
;
489 unsigned int count
= m_selections
.GetCount();
491 // don't call sort on an empty array
494 selections
.Sort(wxCompareInts
);
500 // ----------------------------------------------------------------------------
501 // refresh logic: we use delayed refreshing which allows to avoid multiple
502 // refreshes (and hence flicker) in case when several listbox items are
503 // added/deleted/changed subsequently
504 // ----------------------------------------------------------------------------
506 void wxListBox::RefreshFromItemToEnd(int from
)
508 RefreshItems(from
, GetCount() - from
);
511 void wxListBox::RefreshItems(int from
, int count
)
513 switch ( m_updateCount
)
517 m_updateCount
= count
;
521 // we refresh everything anyhow
525 // add these items to the others which we have to refresh
526 if ( m_updateFrom
< from
)
528 count
+= from
- m_updateFrom
;
529 if ( m_updateCount
< count
)
530 m_updateCount
= count
;
532 else // m_updateFrom >= from
534 int updateLast
= wxMax(m_updateFrom
+ m_updateCount
,
537 m_updateCount
= updateLast
- m_updateFrom
;
542 void wxListBox::RefreshItem(int n
)
544 switch ( m_updateCount
)
547 // refresh this item only
553 // we refresh everything anyhow
557 // add this item to the others which we have to refresh
558 if ( m_updateFrom
< n
)
560 if ( m_updateCount
< n
- m_updateFrom
+ 1 )
561 m_updateCount
= n
- m_updateFrom
+ 1;
563 else // n <= m_updateFrom
565 m_updateCount
+= m_updateFrom
- n
;
571 void wxListBox::RefreshAll()
576 void wxListBox::RefreshHorzScrollbar()
578 m_maxWidth
= 0; // recalculate it
579 m_updateScrollbarX
= true;
582 void wxListBox::UpdateScrollbars()
584 wxSize size
= GetClientSize();
586 // is our height enough to show all items?
587 unsigned int nLines
= GetCount();
588 wxCoord lineHeight
= GetLineHeight();
589 bool showScrollbarY
= (int)nLines
*lineHeight
> size
.y
;
591 // check the width too if required
592 wxCoord charWidth
, maxWidth
;
594 if ( HasHorzScrollbar() )
596 charWidth
= GetCharWidth();
597 maxWidth
= GetMaxWidth();
598 showScrollbarX
= maxWidth
> size
.x
;
600 else // never show it
602 charWidth
= maxWidth
= 0;
603 showScrollbarX
= false;
606 // what should be the scrollbar range now?
607 int scrollRangeX
= showScrollbarX
608 ? (maxWidth
+ charWidth
- 1) / charWidth
+ 2 // FIXME
610 int scrollRangeY
= showScrollbarY
612 (size
.y
% lineHeight
+ lineHeight
- 1) / lineHeight
615 // reset scrollbars if something changed: either the visibility status
616 // or the range of a scrollbar which is shown
617 if ( (showScrollbarY
!= m_showScrollbarY
) ||
618 (showScrollbarX
!= m_showScrollbarX
) ||
619 (showScrollbarY
&& (scrollRangeY
!= m_scrollRangeY
)) ||
620 (showScrollbarX
&& (scrollRangeX
!= m_scrollRangeX
)) )
623 GetViewStart(&x
, &y
);
624 SetScrollbars(charWidth
, lineHeight
,
625 scrollRangeX
, scrollRangeY
,
628 m_showScrollbarX
= showScrollbarX
;
629 m_showScrollbarY
= showScrollbarY
;
631 m_scrollRangeX
= scrollRangeX
;
632 m_scrollRangeY
= scrollRangeY
;
636 void wxListBox::UpdateItems()
638 // only refresh the items which must be refreshed
639 if ( m_updateCount
== -1 )
642 wxLogTrace(wxT("listbox"), wxT("Refreshing all"));
648 wxSize size
= GetClientSize();
651 rect
.height
= size
.y
;
652 rect
.y
+= m_updateFrom
*GetLineHeight();
653 rect
.height
= m_updateCount
*GetLineHeight();
655 // we don't need to calculate x position as we always refresh the
657 CalcScrolledPosition(0, rect
.y
, NULL
, &rect
.y
);
659 wxLogTrace(wxT("listbox"), wxT("Refreshing items %d..%d (%d-%d)"),
660 m_updateFrom
, m_updateFrom
+ m_updateCount
- 1,
661 rect
.GetTop(), rect
.GetBottom());
663 Refresh(true, &rect
);
667 void wxListBox::OnInternalIdle()
669 if ( m_updateScrollbarY
|| m_updateScrollbarX
)
674 m_updateScrollbarY
= false;
677 if ( m_currentChanged
)
679 DoEnsureVisible(m_current
);
681 m_currentChanged
= false;
690 wxListBoxBase::OnInternalIdle();
693 // ----------------------------------------------------------------------------
695 // ----------------------------------------------------------------------------
697 wxBorder
wxListBox::GetDefaultBorder() const
699 return wxBORDER_SUNKEN
;
702 void wxListBox::DoDraw(wxControlRenderer
*renderer
)
704 // adjust the DC to account for scrolling
705 wxDC
& dc
= renderer
->GetDC();
707 dc
.SetFont(GetFont());
709 // get the update rect
710 wxRect rectUpdate
= GetUpdateClientRect();
713 CalcUnscrolledPosition(0, rectUpdate
.GetTop(), NULL
, &yTop
);
714 CalcUnscrolledPosition(0, rectUpdate
.GetBottom(), NULL
, &yBottom
);
716 // get the items which must be redrawn
717 wxCoord lineHeight
= GetLineHeight();
718 unsigned int itemFirst
= yTop
/ lineHeight
,
719 itemLast
= (yBottom
+ lineHeight
- 1) / lineHeight
,
720 itemMax
= GetCount();
722 if ( itemFirst
>= itemMax
)
725 if ( itemLast
> itemMax
)
729 wxLogTrace(wxT("listbox"), wxT("Repainting items %d..%d"),
730 itemFirst
, itemLast
);
732 DoDrawRange(renderer
, itemFirst
, itemLast
);
735 void wxListBox::DoDrawRange(wxControlRenderer
*renderer
,
736 int itemFirst
, int itemLast
)
738 renderer
->DrawItems(this, itemFirst
, itemLast
);
741 // ----------------------------------------------------------------------------
743 // ----------------------------------------------------------------------------
745 bool wxListBox::SetFont(const wxFont
& font
)
747 if ( !wxControl::SetFont(font
) )
757 void wxListBox::CalcItemsPerPage()
759 m_lineHeight
= GetRenderer()->GetListboxItemHeight(GetCharHeight());
760 m_itemsPerPage
= GetClientSize().y
/ m_lineHeight
;
763 int wxListBox::GetItemsPerPage() const
765 if ( !m_itemsPerPage
)
767 wxConstCast(this, wxListBox
)->CalcItemsPerPage();
770 return m_itemsPerPage
;
773 wxCoord
wxListBox::GetLineHeight() const
777 wxConstCast(this, wxListBox
)->CalcItemsPerPage();
783 wxCoord
wxListBox::GetMaxWidth() const
785 if ( m_maxWidth
== 0 )
787 wxListBox
*self
= wxConstCast(this, wxListBox
);
789 unsigned int count
= GetCount();
790 for ( unsigned int n
= 0; n
< count
; n
++ )
792 GetTextExtent(this->GetString(n
), &width
, NULL
);
793 if ( width
> m_maxWidth
)
795 self
->m_maxWidth
= width
;
796 self
->m_maxWidthItem
= n
;
804 void wxListBox::OnSize(wxSizeEvent
& event
)
806 // recalculate the number of items per page
809 // the scrollbars might [dis]appear
811 m_updateScrollbarY
= true;
816 void wxListBox::DoSetFirstItem(int n
)
821 void wxListBox::DoSetSize(int x
, int y
,
822 int width
, int height
,
825 if ( GetWindowStyle() & wxLB_INT_HEIGHT
)
827 // we must round up the height to an entire number of rows
829 // the client area must contain an int number of rows, so take borders
831 wxRect rectBorders
= GetRenderer()->GetBorderDimensions(GetBorder());
832 wxCoord hBorders
= rectBorders
.y
+ rectBorders
.height
;
834 wxCoord hLine
= GetLineHeight();
835 height
= ((height
- hBorders
+ hLine
- 1) / hLine
)*hLine
+ hBorders
;
838 wxListBoxBase::DoSetSize(x
, y
, width
, height
, sizeFlags
);
841 wxSize
wxListBox::DoGetBestClientSize() const
846 unsigned int count
= GetCount();
847 for ( unsigned int n
= 0; n
< count
; n
++ )
850 GetTextExtent(this->GetString(n
), &w
, &h
);
858 // if the listbox is empty, still give it some non zero (even if
859 // arbitrary) size - otherwise, leave small margin around the strings
863 width
+= 3*GetCharWidth();
866 height
= GetCharHeight();
868 // we need the height of the entire listbox, not just of one line
869 height
*= wxMax(count
, 7);
871 return wxSize(width
, height
);
874 // ----------------------------------------------------------------------------
876 // ----------------------------------------------------------------------------
878 bool wxListBox::SendEvent(wxEventType type
, int item
)
880 wxCommandEvent
event(type
, m_windowId
);
881 event
.SetEventObject(this);
883 // use the current item by default
889 // client data and string parameters only make sense if we have an item
892 if ( HasClientObjectData() )
893 event
.SetClientObject(GetClientObject(item
));
894 else if ( HasClientUntypedData() )
895 event
.SetClientData(GetClientData(item
));
897 event
.SetString(GetString(item
));
902 return GetEventHandler()->ProcessEvent(event
);
905 void wxListBox::SetCurrentItem(int n
)
907 if ( n
!= m_current
)
909 if ( m_current
!= -1 )
910 RefreshItem(m_current
);
914 if ( m_current
!= -1 )
916 m_currentChanged
= true;
918 RefreshItem(m_current
);
921 //else: nothing to do
924 bool wxListBox::FindItem(const wxString
& prefix
, bool strictlyAfter
)
926 unsigned int count
= GetCount();
929 // empty listbox, we can't find anything in it
933 // start either from the current item or from the next one if strictlyAfter
938 // the following line will set first correctly to 0 if there is no
939 // selection (m_current == -1)
940 first
= m_current
== (int)(count
- 1) ? 0 : m_current
+ 1;
942 else // start with the current
944 first
= m_current
== -1 ? 0 : m_current
;
947 int last
= first
== 0 ? count
- 1 : first
- 1;
949 // if this is not true we'd never exit from the loop below!
950 wxASSERT_MSG( first
< (int)count
&& last
< (int)count
, wxT("logic error") );
952 // precompute it outside the loop
953 size_t len
= prefix
.length();
955 // loop over all items in the listbox
956 for ( int item
= first
; item
!= (int)last
; item
< (int)(count
- 1) ? item
++ : item
= 0 )
958 if ( wxStrnicmp(this->GetString(item
).c_str(), prefix
, len
) == 0 )
960 SetCurrentItem(item
);
962 if ( !(GetWindowStyle() & wxLB_MULTIPLE
) )
965 SelectAndNotify(item
);
967 if ( GetWindowStyle() & wxLB_EXTENDED
)
968 AnchorSelection(item
);
979 void wxListBox::EnsureVisible(int n
)
981 if ( m_updateScrollbarY
)
986 m_updateScrollbarY
= false;
992 void wxListBox::DoEnsureVisible(int n
)
994 if ( !m_showScrollbarY
)
996 // nothing to do - everything is shown anyhow
1001 GetViewStart(0, &first
);
1004 // we need to scroll upwards, so make the current item appear on top
1005 // of the shown range
1010 int last
= first
+ GetClientSize().y
/ GetLineHeight() - 1;
1013 // scroll down: the current item appears at the bottom of the
1015 Scroll(0, n
- (last
- first
));
1020 void wxListBox::ChangeCurrent(int diff
)
1022 int current
= m_current
== -1 ? 0 : m_current
;
1026 int last
= GetCount() - 1;
1029 else if ( current
> last
)
1032 SetCurrentItem(current
);
1035 void wxListBox::ExtendSelection(int itemTo
)
1037 // if we don't have the explicit values for selection start/end, make them
1039 if ( m_selAnchor
== -1 )
1040 m_selAnchor
= m_current
;
1045 // swap the start/end of selection range if necessary
1046 int itemFrom
= m_selAnchor
;
1047 if ( itemFrom
> itemTo
)
1049 int itemTmp
= itemFrom
;
1054 // the selection should now include all items in the range between the
1055 // anchor and the specified item and only them
1058 for ( n
= 0; n
< itemFrom
; n
++ )
1063 for ( ; n
<= itemTo
; n
++ )
1068 unsigned int count
= GetCount();
1069 for ( ; n
< (int)count
; n
++ )
1075 void wxListBox::DoSelect(int item
, bool sel
)
1079 // go to this item first
1080 SetCurrentItem(item
);
1083 // the current item is the one we want to change: either it was just
1084 // changed above to be the same as item or item == -1 in which we case we
1085 // are supposed to use the current one anyhow
1086 if ( m_current
!= -1 )
1089 SetSelection(m_current
, sel
);
1093 void wxListBox::SelectAndNotify(int item
)
1097 SendEvent(wxEVT_COMMAND_LISTBOX_SELECTED
);
1100 void wxListBox::Activate(int item
)
1103 SetCurrentItem(item
);
1107 if ( !(GetWindowStyle() & wxLB_MULTIPLE
) )
1116 SendEvent(wxEVT_COMMAND_LISTBOX_DOUBLECLICKED
);
1120 // ----------------------------------------------------------------------------
1122 // ----------------------------------------------------------------------------
1125 The numArg here is the listbox item index while the strArg is used
1126 differently for the different actions:
1128 a) for wxACTION_LISTBOX_FIND it has the natural meaning: this is the string
1131 b) for wxACTION_LISTBOX_SELECT and wxACTION_LISTBOX_EXTENDSEL it is used
1132 to decide if the listbox should send the notification event (it is empty)
1133 or not (it is not): this allows us to reuse the same action for when the
1134 user is dragging the mouse when it has been released although in the
1135 first case no notification is sent while in the second it is sent.
1137 bool wxListBox::PerformAction(const wxControlAction
& action
,
1139 const wxString
& strArg
)
1141 int item
= (int)numArg
;
1143 if ( action
== wxACTION_LISTBOX_SETFOCUS
)
1145 SetCurrentItem(item
);
1147 else if ( action
== wxACTION_LISTBOX_ACTIVATE
)
1151 else if ( action
== wxACTION_LISTBOX_TOGGLE
)
1156 if ( IsSelected(item
) )
1159 SelectAndNotify(item
);
1161 else if ( action
== wxACTION_LISTBOX_SELECT
)
1165 if ( strArg
.empty() )
1166 SelectAndNotify(item
);
1170 else if ( action
== wxACTION_LISTBOX_SELECTADD
)
1172 else if ( action
== wxACTION_LISTBOX_UNSELECT
)
1174 else if ( action
== wxACTION_LISTBOX_MOVEDOWN
)
1176 else if ( action
== wxACTION_LISTBOX_MOVEUP
)
1178 else if ( action
== wxACTION_LISTBOX_PAGEDOWN
)
1179 ChangeCurrent(GetItemsPerPage());
1180 else if ( action
== wxACTION_LISTBOX_PAGEUP
)
1181 ChangeCurrent(-GetItemsPerPage());
1182 else if ( action
== wxACTION_LISTBOX_START
)
1184 else if ( action
== wxACTION_LISTBOX_END
)
1185 SetCurrentItem(GetCount() - 1);
1186 else if ( action
== wxACTION_LISTBOX_UNSELECTALL
)
1188 else if ( action
== wxACTION_LISTBOX_EXTENDSEL
)
1189 ExtendSelection(item
);
1190 else if ( action
== wxACTION_LISTBOX_FIND
)
1191 FindNextItem(strArg
);
1192 else if ( action
== wxACTION_LISTBOX_ANCHOR
)
1193 AnchorSelection(item
== -1 ? m_current
: item
);
1194 else if ( action
== wxACTION_LISTBOX_SELECTALL
||
1195 action
== wxACTION_LISTBOX_SELTOGGLE
)
1196 wxFAIL_MSG(wxT("unimplemented yet"));
1198 return wxControl::PerformAction(action
, numArg
, strArg
);
1204 wxInputHandler
*wxListBox::GetStdInputHandler(wxInputHandler
*handlerDef
)
1206 static wxStdListboxInputHandler
s_handler(handlerDef
);
1211 // ============================================================================
1212 // implementation of wxStdListboxInputHandler
1213 // ============================================================================
1215 wxStdListboxInputHandler::wxStdListboxInputHandler(wxInputHandler
*handler
,
1216 bool toggleOnPressAlways
)
1217 : wxStdInputHandler(handler
)
1220 m_toggleOnPressAlways
= toggleOnPressAlways
;
1221 m_actionMouse
= wxACTION_NONE
;
1222 m_trackMouseOutside
= true;
1225 int wxStdListboxInputHandler::HitTest(const wxListBox
*lbox
,
1226 const wxMouseEvent
& event
)
1228 int item
= HitTestUnsafe(lbox
, event
);
1230 return FixItemIndex(lbox
, item
);
1233 int wxStdListboxInputHandler::HitTestUnsafe(const wxListBox
*lbox
,
1234 const wxMouseEvent
& event
)
1236 wxPoint pt
= event
.GetPosition();
1237 pt
-= lbox
->GetClientAreaOrigin();
1239 lbox
->CalcUnscrolledPosition(0, pt
.y
, NULL
, &y
);
1240 return y
/ lbox
->GetLineHeight();
1243 int wxStdListboxInputHandler::FixItemIndex(const wxListBox
*lbox
,
1248 // mouse is above the first item
1251 else if ( (unsigned int)item
>= lbox
->GetCount() )
1253 // mouse is below the last item
1254 item
= lbox
->GetCount() - 1;
1260 bool wxStdListboxInputHandler::IsValidIndex(const wxListBox
*lbox
, int item
)
1262 return item
>= 0 && (unsigned int)item
< lbox
->GetCount();
1266 wxStdListboxInputHandler::SetupCapture(wxListBox
*lbox
,
1267 const wxMouseEvent
& event
,
1270 // we currently only allow selecting with the left mouse button, if we
1271 // do need to allow using other buttons too we might use the code
1274 m_btnCapture
= event
.LeftDown()
1283 wxControlAction action
;
1284 if ( lbox
->HasMultipleSelection() )
1286 if ( lbox
->GetWindowStyle() & wxLB_MULTIPLE
)
1288 if ( m_toggleOnPressAlways
)
1290 // toggle the item right now
1291 action
= wxACTION_LISTBOX_TOGGLE
;
1295 m_actionMouse
= wxACTION_LISTBOX_SETFOCUS
;
1297 else // wxLB_EXTENDED listbox
1299 // simple click in an extended sel listbox clears the old
1300 // selection and adds the clicked item to it then, ctrl-click
1301 // toggles an item to it and shift-click adds a range between
1302 // the old selection anchor and the clicked item
1303 if ( event
.ControlDown() )
1305 lbox
->PerformAction(wxACTION_LISTBOX_ANCHOR
, item
);
1307 action
= wxACTION_LISTBOX_TOGGLE
;
1309 else if ( event
.ShiftDown() )
1311 action
= wxACTION_LISTBOX_EXTENDSEL
;
1313 else // simple click
1315 lbox
->PerformAction(wxACTION_LISTBOX_ANCHOR
, item
);
1317 action
= wxACTION_LISTBOX_SELECT
;
1320 m_actionMouse
= wxACTION_LISTBOX_EXTENDSEL
;
1323 else // single selection
1326 action
= wxACTION_LISTBOX_SELECT
;
1329 // by default we always do track it
1330 m_trackMouseOutside
= true;
1335 bool wxStdListboxInputHandler::HandleKey(wxInputConsumer
*consumer
,
1336 const wxKeyEvent
& event
,
1339 // we're only interested in the key press events
1340 if ( pressed
&& !event
.AltDown() )
1342 bool isMoveCmd
= true;
1343 int style
= consumer
->GetInputWindow()->GetWindowStyle();
1345 wxControlAction action
;
1348 int keycode
= event
.GetKeyCode();
1353 action
= wxACTION_LISTBOX_MOVEUP
;
1357 action
= wxACTION_LISTBOX_MOVEDOWN
;
1361 action
= wxACTION_LISTBOX_PAGEUP
;
1365 action
= wxACTION_LISTBOX_PAGEDOWN
;
1369 action
= wxACTION_LISTBOX_START
;
1373 action
= wxACTION_LISTBOX_END
;
1378 if ( style
& wxLB_MULTIPLE
)
1380 action
= wxACTION_LISTBOX_TOGGLE
;
1386 action
= wxACTION_LISTBOX_ACTIVATE
;
1391 if ( (keycode
< 255) && wxIsalnum((wxChar
)keycode
) )
1393 action
= wxACTION_LISTBOX_FIND
;
1394 strArg
= (wxChar
)keycode
;
1398 if ( !action
.IsEmpty() )
1400 consumer
->PerformAction(action
, -1, strArg
);
1404 if ( style
& wxLB_SINGLE
)
1406 // the current item is always the one selected
1407 consumer
->PerformAction(wxACTION_LISTBOX_SELECT
);
1409 else if ( style
& wxLB_EXTENDED
)
1411 if ( event
.ShiftDown() )
1412 consumer
->PerformAction(wxACTION_LISTBOX_EXTENDSEL
);
1415 // select the item and make it the new selection anchor
1416 consumer
->PerformAction(wxACTION_LISTBOX_SELECT
);
1417 consumer
->PerformAction(wxACTION_LISTBOX_ANCHOR
);
1420 //else: nothing to do for multiple selection listboxes
1427 return wxStdInputHandler::HandleKey(consumer
, event
, pressed
);
1430 bool wxStdListboxInputHandler::HandleMouse(wxInputConsumer
*consumer
,
1431 const wxMouseEvent
& event
)
1433 wxListBox
*lbox
= wxStaticCast(consumer
->GetInputWindow(), wxListBox
);
1434 int item
= HitTest(lbox
, event
);
1435 wxControlAction action
;
1437 // when the left mouse button is pressed, capture the mouse and track the
1438 // item under mouse (if the mouse leaves the window, we will still be
1439 // getting the mouse move messages generated by wxScrollWindow)
1440 if ( event
.LeftDown() )
1442 // capture the mouse to track the selected item
1443 lbox
->CaptureMouse();
1445 action
= SetupCapture(lbox
, event
, item
);
1447 else if ( m_btnCapture
&& event
.ButtonUp(m_btnCapture
) )
1449 // when the left mouse button is released, release the mouse too
1450 wxWindow
*winCapture
= wxWindow::GetCapture();
1453 winCapture
->ReleaseMouse();
1456 action
= m_actionMouse
;
1458 //else: the mouse wasn't presed over the listbox, only released here
1460 else if ( event
.LeftDClick() )
1462 action
= wxACTION_LISTBOX_ACTIVATE
;
1465 if ( !action
.IsEmpty() )
1467 lbox
->PerformAction(action
, item
);
1472 return wxStdInputHandler::HandleMouse(consumer
, event
);
1475 bool wxStdListboxInputHandler::HandleMouseMove(wxInputConsumer
*consumer
,
1476 const wxMouseEvent
& event
)
1478 wxWindow
*winCapture
= wxWindow::GetCapture();
1479 if ( winCapture
&& (event
.GetEventObject() == winCapture
) )
1481 wxListBox
*lbox
= wxStaticCast(consumer
->GetInputWindow(), wxListBox
);
1483 if ( !m_btnCapture
|| !m_trackMouseOutside
)
1485 // someone captured the mouse for us (we always set m_btnCapture
1486 // when we do it ourselves): in this case we only react to
1487 // the mouse messages when they happen inside the listbox
1488 if ( lbox
->HitTest(event
.GetPosition()) != wxHT_WINDOW_INSIDE
)
1492 int item
= HitTest(lbox
, event
);
1493 if ( !m_btnCapture
)
1495 // now that we have the mouse inside the listbox, do capture it
1496 // normally - but ensure that we will still ignore the outside
1498 SetupCapture(lbox
, event
, item
);
1500 m_trackMouseOutside
= false;
1503 if ( IsValidIndex(lbox
, item
) )
1505 // pass something into strArg to tell the listbox that it shouldn't
1506 // send the notification message: see PerformAction() above
1507 lbox
->PerformAction(m_actionMouse
, item
, wxT("no"));
1509 // else: don't pass invalid index to the listbox
1511 else // we don't have capture any more
1515 // if we lost capture unexpectedly (someone else took the capture
1516 // from us), return to a consistent state
1521 return wxStdInputHandler::HandleMouseMove(consumer
, event
);
1524 #endif // wxUSE_LISTBOX