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
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
= IsSorted() ? new wxSortedArrayString
: new wxArrayString
;
199 SetInitialSize(size
);
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 wxListBox::DoInsertItems(const wxArrayStringsAdapter
& items
,
223 wxClientDataType type
)
225 int idx
= wxNOT_FOUND
;
227 const unsigned int numItems
= items
.GetCount();
228 for ( unsigned int i
= 0; i
< numItems
; ++i
)
230 const wxString
& item
= items
[i
];
231 idx
= IsSorted() ? m_strings
->Add(item
)
232 : (m_strings
->Insert(item
, pos
), pos
++);
234 m_itemsClientData
.Insert(NULL
, idx
);
235 AssignNewItemClientData(idx
, clientData
, i
, type
);
237 // call the wxCheckListBox hook
241 // the number of items has changed so we might have to show the scrollbar
242 m_updateScrollbarY
= true;
244 // the max width also might have changed - just recalculate it instead of
245 // keeping track of it here, this is probably more efficient for a typical
247 RefreshHorzScrollbar();
249 // note that we have to refresh all the items after the ones we inserted,
250 // not just these items
251 RefreshFromItemToEnd(pos
);
256 void wxListBox::SetString(unsigned int n
, const wxString
& s
)
258 wxCHECK_RET( !IsSorted(), _T("can't set string in sorted listbox") );
262 if ( HasHorzScrollbar() )
264 // we need to update m_maxWidth as changing the string may cause the
265 // horz scrollbar [dis]appear
268 GetTextExtent(s
, &width
, NULL
);
270 // it might have increased if the new string is long
271 if ( width
> m_maxWidth
)
275 m_updateScrollbarX
= true;
277 // or also decreased if the old string was the longest one
278 else if ( n
== (unsigned int)m_maxWidthItem
)
280 RefreshHorzScrollbar();
287 // ----------------------------------------------------------------------------
289 // ----------------------------------------------------------------------------
291 void wxListBox::DoClear()
295 m_itemsClientData
.Clear();
296 m_selections
.Clear();
300 m_updateScrollbarY
= true;
302 RefreshHorzScrollbar();
307 void wxListBox::DoDeleteOneItem(unsigned int n
)
309 wxCHECK_RET( IsValid(n
),
310 _T("invalid index in wxListBox::Delete") );
312 // do it before removing the index as otherwise the last item will not be
313 // refreshed (as GetCount() will be decremented)
314 RefreshFromItemToEnd(n
);
316 m_strings
->RemoveAt(n
);
318 m_itemsClientData
.RemoveAt(n
);
320 // when the item disappears we must not keep using its index
321 if ( (int)n
== m_current
)
325 else if ( (int)n
< m_current
)
329 //else: current item may stay
331 // update the selections array: the indices of all seletected items after
332 // the one being deleted must change and the item itselfm ust be removed
333 int index
= wxNOT_FOUND
;
334 unsigned int count
= m_selections
.GetCount();
335 for ( unsigned int item
= 0; item
< count
; item
++ )
337 if ( m_selections
[item
] == (int)n
)
339 // remember to delete it later
342 else if ( m_selections
[item
] > (int)n
)
344 // to account for the index shift
345 m_selections
[item
]--;
347 //else: nothing changed for this one
350 if ( index
!= wxNOT_FOUND
)
352 m_selections
.RemoveAt(index
);
355 // the number of items has changed, hence the scrollbar may disappear
356 m_updateScrollbarY
= true;
358 // finally, if the longest item was deleted the scrollbar may disappear
359 if ( (int)n
== m_maxWidthItem
)
361 RefreshHorzScrollbar();
365 // ----------------------------------------------------------------------------
366 // client data handling
367 // ----------------------------------------------------------------------------
369 void wxListBox::DoSetItemClientData(unsigned int n
, void* clientData
)
371 m_itemsClientData
[n
] = clientData
;
374 void *wxListBox::DoGetItemClientData(unsigned int n
) const
376 return m_itemsClientData
[n
];
379 // ----------------------------------------------------------------------------
381 // ----------------------------------------------------------------------------
383 void wxListBox::DoSetSelection(int n
, bool select
)
387 if ( n
== wxNOT_FOUND
)
389 if ( !HasMultipleSelection() )
391 // selecting wxNOT_FOUND is documented to deselect all items
396 else if ( m_selections
.Index(n
) == wxNOT_FOUND
)
398 if ( !HasMultipleSelection() )
400 // selecting an item in a single selection listbox deselects
409 //else: already selected
413 int index
= m_selections
.Index(n
);
414 if ( index
!= wxNOT_FOUND
)
416 m_selections
.RemoveAt(index
);
423 // sanity check: a single selection listbox can't have more than one item
425 wxASSERT_MSG( HasMultipleSelection() || (m_selections
.GetCount() < 2),
426 _T("multiple selected items in single selection lbox?") );
430 // the newly selected item becomes the current one
435 int wxListBox::GetSelection() const
437 wxCHECK_MSG( !HasMultipleSelection(), wxNOT_FOUND
,
438 _T("use wxListBox::GetSelections for ths listbox") );
440 return m_selections
.IsEmpty() ? wxNOT_FOUND
: m_selections
[0];
443 int wxCMPFUNC_CONV
wxCompareInts(int *n
, int *m
)
448 int wxListBox::GetSelections(wxArrayInt
& selections
) const
450 // always return sorted array to the user
451 selections
= m_selections
;
452 unsigned int count
= m_selections
.GetCount();
454 // don't call sort on an empty array
457 selections
.Sort(wxCompareInts
);
463 // ----------------------------------------------------------------------------
464 // refresh logic: we use delayed refreshing which allows to avoid multiple
465 // refreshes (and hence flicker) in case when several listbox items are
466 // added/deleted/changed subsequently
467 // ----------------------------------------------------------------------------
469 void wxListBox::RefreshFromItemToEnd(int from
)
471 RefreshItems(from
, GetCount() - from
);
474 void wxListBox::RefreshItems(int from
, int count
)
476 switch ( m_updateCount
)
480 m_updateCount
= count
;
484 // we refresh everything anyhow
488 // add these items to the others which we have to refresh
489 if ( m_updateFrom
< from
)
491 count
+= from
- m_updateFrom
;
492 if ( m_updateCount
< count
)
493 m_updateCount
= count
;
495 else // m_updateFrom >= from
497 int updateLast
= wxMax(m_updateFrom
+ m_updateCount
,
500 m_updateCount
= updateLast
- m_updateFrom
;
505 void wxListBox::RefreshItem(int n
)
507 switch ( m_updateCount
)
510 // refresh this item only
516 // we refresh everything anyhow
520 // add this item to the others which we have to refresh
521 if ( m_updateFrom
< n
)
523 if ( m_updateCount
< n
- m_updateFrom
+ 1 )
524 m_updateCount
= n
- m_updateFrom
+ 1;
526 else // n <= m_updateFrom
528 m_updateCount
+= m_updateFrom
- n
;
534 void wxListBox::RefreshAll()
539 void wxListBox::RefreshHorzScrollbar()
541 m_maxWidth
= 0; // recalculate it
542 m_updateScrollbarX
= true;
545 void wxListBox::UpdateScrollbars()
547 wxSize size
= GetClientSize();
549 // is our height enough to show all items?
550 unsigned int nLines
= GetCount();
551 wxCoord lineHeight
= GetLineHeight();
552 bool showScrollbarY
= (int)nLines
*lineHeight
> size
.y
;
554 // check the width too if required
555 wxCoord charWidth
, maxWidth
;
557 if ( HasHorzScrollbar() )
559 charWidth
= GetCharWidth();
560 maxWidth
= GetMaxWidth();
561 showScrollbarX
= maxWidth
> size
.x
;
563 else // never show it
565 charWidth
= maxWidth
= 0;
566 showScrollbarX
= false;
569 // what should be the scrollbar range now?
570 int scrollRangeX
= showScrollbarX
571 ? (maxWidth
+ charWidth
- 1) / charWidth
+ 2 // FIXME
573 int scrollRangeY
= showScrollbarY
575 (size
.y
% lineHeight
+ lineHeight
- 1) / lineHeight
578 // reset scrollbars if something changed: either the visibility status
579 // or the range of a scrollbar which is shown
580 if ( (showScrollbarY
!= m_showScrollbarY
) ||
581 (showScrollbarX
!= m_showScrollbarX
) ||
582 (showScrollbarY
&& (scrollRangeY
!= m_scrollRangeY
)) ||
583 (showScrollbarX
&& (scrollRangeX
!= m_scrollRangeX
)) )
586 GetViewStart(&x
, &y
);
587 SetScrollbars(charWidth
, lineHeight
,
588 scrollRangeX
, scrollRangeY
,
591 m_showScrollbarX
= showScrollbarX
;
592 m_showScrollbarY
= showScrollbarY
;
594 m_scrollRangeX
= scrollRangeX
;
595 m_scrollRangeY
= scrollRangeY
;
599 void wxListBox::UpdateItems()
601 // only refresh the items which must be refreshed
602 if ( m_updateCount
== -1 )
605 wxLogTrace(_T("listbox"), _T("Refreshing all"));
611 wxSize size
= GetClientSize();
614 rect
.height
= size
.y
;
615 rect
.y
+= m_updateFrom
*GetLineHeight();
616 rect
.height
= m_updateCount
*GetLineHeight();
618 // we don't need to calculate x position as we always refresh the
620 CalcScrolledPosition(0, rect
.y
, NULL
, &rect
.y
);
622 wxLogTrace(_T("listbox"), _T("Refreshing items %d..%d (%d-%d)"),
623 m_updateFrom
, m_updateFrom
+ m_updateCount
- 1,
624 rect
.GetTop(), rect
.GetBottom());
626 Refresh(true, &rect
);
630 void wxListBox::OnInternalIdle()
632 if ( m_updateScrollbarY
|| m_updateScrollbarX
)
637 m_updateScrollbarY
= false;
640 if ( m_currentChanged
)
642 DoEnsureVisible(m_current
);
644 m_currentChanged
= false;
653 wxListBoxBase::OnInternalIdle();
656 // ----------------------------------------------------------------------------
658 // ----------------------------------------------------------------------------
660 wxBorder
wxListBox::GetDefaultBorder() const
662 return wxBORDER_SUNKEN
;
665 void wxListBox::DoDraw(wxControlRenderer
*renderer
)
667 // adjust the DC to account for scrolling
668 wxDC
& dc
= renderer
->GetDC();
670 dc
.SetFont(GetFont());
672 // get the update rect
673 wxRect rectUpdate
= GetUpdateClientRect();
676 CalcUnscrolledPosition(0, rectUpdate
.GetTop(), NULL
, &yTop
);
677 CalcUnscrolledPosition(0, rectUpdate
.GetBottom(), NULL
, &yBottom
);
679 // get the items which must be redrawn
680 wxCoord lineHeight
= GetLineHeight();
681 unsigned int itemFirst
= yTop
/ lineHeight
,
682 itemLast
= (yBottom
+ lineHeight
- 1) / lineHeight
,
683 itemMax
= m_strings
->GetCount();
685 if ( itemFirst
>= itemMax
)
688 if ( itemLast
> itemMax
)
692 wxLogTrace(_T("listbox"), _T("Repainting items %d..%d"),
693 itemFirst
, itemLast
);
695 DoDrawRange(renderer
, itemFirst
, itemLast
);
698 void wxListBox::DoDrawRange(wxControlRenderer
*renderer
,
699 int itemFirst
, int itemLast
)
701 renderer
->DrawItems(this, itemFirst
, itemLast
);
704 // ----------------------------------------------------------------------------
706 // ----------------------------------------------------------------------------
708 bool wxListBox::SetFont(const wxFont
& font
)
710 if ( !wxControl::SetFont(font
) )
720 void wxListBox::CalcItemsPerPage()
722 m_lineHeight
= GetRenderer()->GetListboxItemHeight(GetCharHeight());
723 m_itemsPerPage
= GetClientSize().y
/ m_lineHeight
;
726 int wxListBox::GetItemsPerPage() const
728 if ( !m_itemsPerPage
)
730 wxConstCast(this, wxListBox
)->CalcItemsPerPage();
733 return m_itemsPerPage
;
736 wxCoord
wxListBox::GetLineHeight() const
740 wxConstCast(this, wxListBox
)->CalcItemsPerPage();
746 wxCoord
wxListBox::GetMaxWidth() const
748 if ( m_maxWidth
== 0 )
750 wxListBox
*self
= wxConstCast(this, wxListBox
);
752 unsigned int count
= m_strings
->GetCount();
753 for ( unsigned int n
= 0; n
< count
; n
++ )
755 GetTextExtent(this->GetString(n
), &width
, NULL
);
756 if ( width
> m_maxWidth
)
758 self
->m_maxWidth
= width
;
759 self
->m_maxWidthItem
= n
;
767 void wxListBox::OnSize(wxSizeEvent
& event
)
769 // recalculate the number of items per page
772 // the scrollbars might [dis]appear
774 m_updateScrollbarY
= true;
779 void wxListBox::DoSetFirstItem(int n
)
784 void wxListBox::DoSetSize(int x
, int y
,
785 int width
, int height
,
788 if ( GetWindowStyle() & wxLB_INT_HEIGHT
)
790 // we must round up the height to an entire number of rows
792 // the client area must contain an int number of rows, so take borders
794 wxRect rectBorders
= GetRenderer()->GetBorderDimensions(GetBorder());
795 wxCoord hBorders
= rectBorders
.y
+ rectBorders
.height
;
797 wxCoord hLine
= GetLineHeight();
798 height
= ((height
- hBorders
+ hLine
- 1) / hLine
)*hLine
+ hBorders
;
801 wxListBoxBase::DoSetSize(x
, y
, width
, height
, sizeFlags
);
804 wxSize
wxListBox::DoGetBestClientSize() const
809 unsigned int count
= m_strings
->GetCount();
810 for ( unsigned int n
= 0; n
< count
; n
++ )
813 GetTextExtent(this->GetString(n
), &w
, &h
);
821 // if the listbox is empty, still give it some non zero (even if
822 // arbitrary) size - otherwise, leave small margin around the strings
826 width
+= 3*GetCharWidth();
829 height
= GetCharHeight();
831 // we need the height of the entire listbox, not just of one line
832 height
*= wxMax(count
, 7);
834 return wxSize(width
, height
);
837 // ----------------------------------------------------------------------------
839 // ----------------------------------------------------------------------------
841 bool wxListBox::SendEvent(wxEventType type
, int item
)
843 wxCommandEvent
event(type
, m_windowId
);
844 event
.SetEventObject(this);
846 // use the current item by default
852 // client data and string parameters only make sense if we have an item
855 if ( HasClientObjectData() )
856 event
.SetClientObject(GetClientObject(item
));
857 else if ( HasClientUntypedData() )
858 event
.SetClientData(GetClientData(item
));
860 event
.SetString(GetString(item
));
865 return GetEventHandler()->ProcessEvent(event
);
868 void wxListBox::SetCurrentItem(int n
)
870 if ( n
!= m_current
)
872 if ( m_current
!= -1 )
873 RefreshItem(m_current
);
877 if ( m_current
!= -1 )
879 m_currentChanged
= true;
881 RefreshItem(m_current
);
884 //else: nothing to do
887 bool wxListBox::FindItem(const wxString
& prefix
, bool strictlyAfter
)
889 unsigned int count
= GetCount();
892 // empty listbox, we can't find anything in it
896 // start either from the current item or from the next one if strictlyAfter
901 // the following line will set first correctly to 0 if there is no
902 // selection (m_current == -1)
903 first
= m_current
== (int)(count
- 1) ? 0 : m_current
+ 1;
905 else // start with the current
907 first
= m_current
== -1 ? 0 : m_current
;
910 int last
= first
== 0 ? count
- 1 : first
- 1;
912 // if this is not true we'd never exit from the loop below!
913 wxASSERT_MSG( first
< (int)count
&& last
< (int)count
, _T("logic error") );
915 // precompute it outside the loop
916 size_t len
= prefix
.length();
918 // loop over all items in the listbox
919 for ( int item
= first
; item
!= (int)last
; item
< (int)(count
- 1) ? item
++ : item
= 0 )
921 if ( wxStrnicmp(this->GetString(item
).c_str(), prefix
, len
) == 0 )
923 SetCurrentItem(item
);
925 if ( !(GetWindowStyle() & wxLB_MULTIPLE
) )
928 SelectAndNotify(item
);
930 if ( GetWindowStyle() & wxLB_EXTENDED
)
931 AnchorSelection(item
);
942 void wxListBox::EnsureVisible(int n
)
944 if ( m_updateScrollbarY
)
949 m_updateScrollbarY
= false;
955 void wxListBox::DoEnsureVisible(int n
)
957 if ( !m_showScrollbarY
)
959 // nothing to do - everything is shown anyhow
964 GetViewStart(0, &first
);
967 // we need to scroll upwards, so make the current item appear on top
968 // of the shown range
973 int last
= first
+ GetClientSize().y
/ GetLineHeight() - 1;
976 // scroll down: the current item appears at the bottom of the
978 Scroll(0, n
- (last
- first
));
983 void wxListBox::ChangeCurrent(int diff
)
985 int current
= m_current
== -1 ? 0 : m_current
;
989 int last
= GetCount() - 1;
992 else if ( current
> last
)
995 SetCurrentItem(current
);
998 void wxListBox::ExtendSelection(int itemTo
)
1000 // if we don't have the explicit values for selection start/end, make them
1002 if ( m_selAnchor
== -1 )
1003 m_selAnchor
= m_current
;
1008 // swap the start/end of selection range if necessary
1009 int itemFrom
= m_selAnchor
;
1010 if ( itemFrom
> itemTo
)
1012 int itemTmp
= itemFrom
;
1017 // the selection should now include all items in the range between the
1018 // anchor and the specified item and only them
1021 for ( n
= 0; n
< itemFrom
; n
++ )
1026 for ( ; n
<= itemTo
; n
++ )
1031 unsigned int count
= GetCount();
1032 for ( ; n
< (int)count
; n
++ )
1038 void wxListBox::DoSelect(int item
, bool sel
)
1042 // go to this item first
1043 SetCurrentItem(item
);
1046 // the current item is the one we want to change: either it was just
1047 // changed above to be the same as item or item == -1 in which we case we
1048 // are supposed to use the current one anyhow
1049 if ( m_current
!= -1 )
1052 SetSelection(m_current
, sel
);
1056 void wxListBox::SelectAndNotify(int item
)
1060 SendEvent(wxEVT_COMMAND_LISTBOX_SELECTED
);
1063 void wxListBox::Activate(int item
)
1066 SetCurrentItem(item
);
1070 if ( !(GetWindowStyle() & wxLB_MULTIPLE
) )
1079 SendEvent(wxEVT_COMMAND_LISTBOX_DOUBLECLICKED
);
1083 // ----------------------------------------------------------------------------
1085 // ----------------------------------------------------------------------------
1088 The numArg here is the listbox item index while the strArg is used
1089 differently for the different actions:
1091 a) for wxACTION_LISTBOX_FIND it has the natural meaning: this is the string
1094 b) for wxACTION_LISTBOX_SELECT and wxACTION_LISTBOX_EXTENDSEL it is used
1095 to decide if the listbox should send the notification event (it is empty)
1096 or not (it is not): this allows us to reuse the same action for when the
1097 user is dragging the mouse when it has been released although in the
1098 first case no notification is sent while in the second it is sent.
1100 bool wxListBox::PerformAction(const wxControlAction
& action
,
1102 const wxString
& strArg
)
1104 int item
= (int)numArg
;
1106 if ( action
== wxACTION_LISTBOX_SETFOCUS
)
1108 SetCurrentItem(item
);
1110 else if ( action
== wxACTION_LISTBOX_ACTIVATE
)
1114 else if ( action
== wxACTION_LISTBOX_TOGGLE
)
1119 if ( IsSelected(item
) )
1122 SelectAndNotify(item
);
1124 else if ( action
== wxACTION_LISTBOX_SELECT
)
1128 if ( strArg
.empty() )
1129 SelectAndNotify(item
);
1133 else if ( action
== wxACTION_LISTBOX_SELECTADD
)
1135 else if ( action
== wxACTION_LISTBOX_UNSELECT
)
1137 else if ( action
== wxACTION_LISTBOX_MOVEDOWN
)
1139 else if ( action
== wxACTION_LISTBOX_MOVEUP
)
1141 else if ( action
== wxACTION_LISTBOX_PAGEDOWN
)
1142 ChangeCurrent(GetItemsPerPage());
1143 else if ( action
== wxACTION_LISTBOX_PAGEUP
)
1144 ChangeCurrent(-GetItemsPerPage());
1145 else if ( action
== wxACTION_LISTBOX_START
)
1147 else if ( action
== wxACTION_LISTBOX_END
)
1148 SetCurrentItem(GetCount() - 1);
1149 else if ( action
== wxACTION_LISTBOX_UNSELECTALL
)
1151 else if ( action
== wxACTION_LISTBOX_EXTENDSEL
)
1152 ExtendSelection(item
);
1153 else if ( action
== wxACTION_LISTBOX_FIND
)
1154 FindNextItem(strArg
);
1155 else if ( action
== wxACTION_LISTBOX_ANCHOR
)
1156 AnchorSelection(item
== -1 ? m_current
: item
);
1157 else if ( action
== wxACTION_LISTBOX_SELECTALL
||
1158 action
== wxACTION_LISTBOX_SELTOGGLE
)
1159 wxFAIL_MSG(_T("unimplemented yet"));
1161 return wxControl::PerformAction(action
, numArg
, strArg
);
1167 wxInputHandler
*wxListBox::GetStdInputHandler(wxInputHandler
*handlerDef
)
1169 static wxStdListboxInputHandler
s_handler(handlerDef
);
1174 // ============================================================================
1175 // implementation of wxStdListboxInputHandler
1176 // ============================================================================
1178 wxStdListboxInputHandler::wxStdListboxInputHandler(wxInputHandler
*handler
,
1179 bool toggleOnPressAlways
)
1180 : wxStdInputHandler(handler
)
1183 m_toggleOnPressAlways
= toggleOnPressAlways
;
1184 m_actionMouse
= wxACTION_NONE
;
1185 m_trackMouseOutside
= true;
1188 int wxStdListboxInputHandler::HitTest(const wxListBox
*lbox
,
1189 const wxMouseEvent
& event
)
1191 int item
= HitTestUnsafe(lbox
, event
);
1193 return FixItemIndex(lbox
, item
);
1196 int wxStdListboxInputHandler::HitTestUnsafe(const wxListBox
*lbox
,
1197 const wxMouseEvent
& event
)
1199 wxPoint pt
= event
.GetPosition();
1200 pt
-= lbox
->GetClientAreaOrigin();
1202 lbox
->CalcUnscrolledPosition(0, pt
.y
, NULL
, &y
);
1203 return y
/ lbox
->GetLineHeight();
1206 int wxStdListboxInputHandler::FixItemIndex(const wxListBox
*lbox
,
1211 // mouse is above the first item
1214 else if ( (unsigned int)item
>= lbox
->GetCount() )
1216 // mouse is below the last item
1217 item
= lbox
->GetCount() - 1;
1223 bool wxStdListboxInputHandler::IsValidIndex(const wxListBox
*lbox
, int item
)
1225 return item
>= 0 && (unsigned int)item
< lbox
->GetCount();
1229 wxStdListboxInputHandler::SetupCapture(wxListBox
*lbox
,
1230 const wxMouseEvent
& event
,
1233 // we currently only allow selecting with the left mouse button, if we
1234 // do need to allow using other buttons too we might use the code
1237 m_btnCapture
= event
.LeftDown()
1246 wxControlAction action
;
1247 if ( lbox
->HasMultipleSelection() )
1249 if ( lbox
->GetWindowStyle() & wxLB_MULTIPLE
)
1251 if ( m_toggleOnPressAlways
)
1253 // toggle the item right now
1254 action
= wxACTION_LISTBOX_TOGGLE
;
1258 m_actionMouse
= wxACTION_LISTBOX_SETFOCUS
;
1260 else // wxLB_EXTENDED listbox
1262 // simple click in an extended sel listbox clears the old
1263 // selection and adds the clicked item to it then, ctrl-click
1264 // toggles an item to it and shift-click adds a range between
1265 // the old selection anchor and the clicked item
1266 if ( event
.ControlDown() )
1268 lbox
->PerformAction(wxACTION_LISTBOX_ANCHOR
, item
);
1270 action
= wxACTION_LISTBOX_TOGGLE
;
1272 else if ( event
.ShiftDown() )
1274 action
= wxACTION_LISTBOX_EXTENDSEL
;
1276 else // simple click
1278 lbox
->PerformAction(wxACTION_LISTBOX_ANCHOR
, item
);
1280 action
= wxACTION_LISTBOX_SELECT
;
1283 m_actionMouse
= wxACTION_LISTBOX_EXTENDSEL
;
1286 else // single selection
1289 action
= wxACTION_LISTBOX_SELECT
;
1292 // by default we always do track it
1293 m_trackMouseOutside
= true;
1298 bool wxStdListboxInputHandler::HandleKey(wxInputConsumer
*consumer
,
1299 const wxKeyEvent
& event
,
1302 // we're only interested in the key press events
1303 if ( pressed
&& !event
.AltDown() )
1305 bool isMoveCmd
= true;
1306 int style
= consumer
->GetInputWindow()->GetWindowStyle();
1308 wxControlAction action
;
1311 int keycode
= event
.GetKeyCode();
1316 action
= wxACTION_LISTBOX_MOVEUP
;
1320 action
= wxACTION_LISTBOX_MOVEDOWN
;
1324 action
= wxACTION_LISTBOX_PAGEUP
;
1328 action
= wxACTION_LISTBOX_PAGEDOWN
;
1332 action
= wxACTION_LISTBOX_START
;
1336 action
= wxACTION_LISTBOX_END
;
1341 if ( style
& wxLB_MULTIPLE
)
1343 action
= wxACTION_LISTBOX_TOGGLE
;
1349 action
= wxACTION_LISTBOX_ACTIVATE
;
1354 if ( (keycode
< 255) && wxIsalnum((wxChar
)keycode
) )
1356 action
= wxACTION_LISTBOX_FIND
;
1357 strArg
= (wxChar
)keycode
;
1361 if ( !action
.IsEmpty() )
1363 consumer
->PerformAction(action
, -1, strArg
);
1367 if ( style
& wxLB_SINGLE
)
1369 // the current item is always the one selected
1370 consumer
->PerformAction(wxACTION_LISTBOX_SELECT
);
1372 else if ( style
& wxLB_EXTENDED
)
1374 if ( event
.ShiftDown() )
1375 consumer
->PerformAction(wxACTION_LISTBOX_EXTENDSEL
);
1378 // select the item and make it the new selection anchor
1379 consumer
->PerformAction(wxACTION_LISTBOX_SELECT
);
1380 consumer
->PerformAction(wxACTION_LISTBOX_ANCHOR
);
1383 //else: nothing to do for multiple selection listboxes
1390 return wxStdInputHandler::HandleKey(consumer
, event
, pressed
);
1393 bool wxStdListboxInputHandler::HandleMouse(wxInputConsumer
*consumer
,
1394 const wxMouseEvent
& event
)
1396 wxListBox
*lbox
= wxStaticCast(consumer
->GetInputWindow(), wxListBox
);
1397 int item
= HitTest(lbox
, event
);
1398 wxControlAction action
;
1400 // when the left mouse button is pressed, capture the mouse and track the
1401 // item under mouse (if the mouse leaves the window, we will still be
1402 // getting the mouse move messages generated by wxScrollWindow)
1403 if ( event
.LeftDown() )
1405 // capture the mouse to track the selected item
1406 lbox
->CaptureMouse();
1408 action
= SetupCapture(lbox
, event
, item
);
1410 else if ( m_btnCapture
&& event
.ButtonUp(m_btnCapture
) )
1412 // when the left mouse button is released, release the mouse too
1413 wxWindow
*winCapture
= wxWindow::GetCapture();
1416 winCapture
->ReleaseMouse();
1419 action
= m_actionMouse
;
1421 //else: the mouse wasn't presed over the listbox, only released here
1423 else if ( event
.LeftDClick() )
1425 action
= wxACTION_LISTBOX_ACTIVATE
;
1428 if ( !action
.IsEmpty() )
1430 lbox
->PerformAction(action
, item
);
1435 return wxStdInputHandler::HandleMouse(consumer
, event
);
1438 bool wxStdListboxInputHandler::HandleMouseMove(wxInputConsumer
*consumer
,
1439 const wxMouseEvent
& event
)
1441 wxWindow
*winCapture
= wxWindow::GetCapture();
1442 if ( winCapture
&& (event
.GetEventObject() == winCapture
) )
1444 wxListBox
*lbox
= wxStaticCast(consumer
->GetInputWindow(), wxListBox
);
1446 if ( !m_btnCapture
|| !m_trackMouseOutside
)
1448 // someone captured the mouse for us (we always set m_btnCapture
1449 // when we do it ourselves): in this case we only react to
1450 // the mouse messages when they happen inside the listbox
1451 if ( lbox
->HitTest(event
.GetPosition()) != wxHT_WINDOW_INSIDE
)
1455 int item
= HitTest(lbox
, event
);
1456 if ( !m_btnCapture
)
1458 // now that we have the mouse inside the listbox, do capture it
1459 // normally - but ensure that we will still ignore the outside
1461 SetupCapture(lbox
, event
, item
);
1463 m_trackMouseOutside
= false;
1466 if ( IsValidIndex(lbox
, item
) )
1468 // pass something into strArg to tell the listbox that it shouldn't
1469 // send the notification message: see PerformAction() above
1470 lbox
->PerformAction(m_actionMouse
, item
, _T("no"));
1472 // else: don't pass invalid index to the listbox
1474 else // we don't have capture any more
1478 // if we lost capture unexpectedly (someone else took the capture
1479 // from us), return to a consistent state
1484 return wxStdInputHandler::HandleMouseMove(consumer
, event
);
1487 #endif // wxUSE_LISTBOX