1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/univ/listbox.cpp
3 // Purpose: wxListBox implementation
4 // Author: Vadim Zeitlin
7 // Copyright: (c) 2000 SciTech Software, Inc. (www.scitechsoft.com)
8 // Licence: wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
11 // ============================================================================
13 // ============================================================================
15 // ----------------------------------------------------------------------------
17 // ----------------------------------------------------------------------------
19 #include "wx/wxprec.h"
30 #include "wx/dcclient.h"
31 #include "wx/listbox.h"
32 #include "wx/validate.h"
35 #include "wx/univ/renderer.h"
36 #include "wx/univ/inphand.h"
37 #include "wx/univ/theme.h"
39 // ----------------------------------------------------------------------------
40 // wxStdListboxInputHandler: handles mouse and kbd in a single or multi
42 // ----------------------------------------------------------------------------
44 class WXDLLEXPORT wxStdListboxInputHandler
: public wxStdInputHandler
47 // if pressing the mouse button in a multiselection listbox should toggle
48 // the item under mouse immediately, then specify true as the second
49 // parameter (this is the standard behaviour, under GTK the item is toggled
50 // only when the mouse is released in the multi selection listbox)
51 wxStdListboxInputHandler(wxInputHandler
*inphand
,
52 bool toggleOnPressAlways
= true);
55 virtual bool HandleKey(wxInputConsumer
*consumer
,
56 const wxKeyEvent
& event
,
58 virtual bool HandleMouse(wxInputConsumer
*consumer
,
59 const wxMouseEvent
& event
);
60 virtual bool HandleMouseMove(wxInputConsumer
*consumer
,
61 const wxMouseEvent
& event
);
64 // return the item under mouse, 0 if the mouse is above the listbox or
65 // GetCount() if it is below it
66 int HitTest(const wxListBox
*listbox
, const wxMouseEvent
& event
);
68 // parts of HitTest(): first finds the pseudo (because not in range) index
69 // of the item and the second one adjusts it if necessary - that is if the
70 // third one returns false
71 int HitTestUnsafe(const wxListBox
*listbox
, const wxMouseEvent
& event
);
72 int FixItemIndex(const wxListBox
*listbox
, int item
);
73 bool IsValidIndex(const wxListBox
*listbox
, int item
);
75 // init m_btnCapture and m_actionMouse
76 wxControlAction
SetupCapture(wxListBox
*lbox
,
77 const wxMouseEvent
& event
,
80 wxRenderer
*m_renderer
;
82 // the button which initiated the mouse capture (currently 0 or 1)
85 // the action to perform when the mouse moves while we capture it
86 wxControlAction m_actionMouse
;
88 // the ctor parameter toggleOnPressAlways (see comments near it)
89 bool m_toggleOnPressAlways
;
91 // do we track the mouse outside the window when it is captured?
92 bool m_trackMouseOutside
;
95 // ============================================================================
96 // implementation of wxListBox
97 // ============================================================================
99 BEGIN_EVENT_TABLE(wxListBox
, wxListBoxBase
)
100 EVT_SIZE(wxListBox::OnSize
)
103 // ----------------------------------------------------------------------------
105 // ----------------------------------------------------------------------------
107 void wxListBox::Init()
109 // will be calculated later when needed
115 m_strings
.unsorted
= NULL
;
117 // no items hence no current item
120 m_currentChanged
= false;
122 // no need to update anything initially
125 // no scrollbars to show nor update
129 m_showScrollbarY
= false;
132 wxListBox::wxListBox(wxWindow
*parent
,
136 const wxArrayString
& choices
,
138 const wxValidator
& validator
,
139 const wxString
&name
)
140 :wxScrollHelper(this)
144 Create(parent
, id
, pos
, size
, choices
, style
, validator
, name
);
147 bool wxListBox::Create(wxWindow
*parent
,
151 const wxArrayString
& choices
,
153 const wxValidator
& validator
,
154 const wxString
&name
)
156 wxCArrayString
chs(choices
);
158 return Create(parent
, id
, pos
, size
, chs
.GetCount(), chs
.GetStrings(),
159 style
, validator
, name
);
162 bool wxListBox::Create(wxWindow
*parent
,
167 const wxString choices
[],
169 const wxValidator
& validator
,
170 const wxString
&name
)
172 // for compatibility accept both the new and old styles - they mean the
174 if ( style
& wxLB_ALWAYS_SB
)
175 style
|= wxALWAYS_SHOW_SB
;
177 // if we don't have neither multiple nor extended flag, we must have the
178 // single selection listbox
179 if ( !(style
& (wxLB_MULTIPLE
| wxLB_EXTENDED
)) )
180 style
|= wxLB_SINGLE
;
182 #if wxUSE_TWO_WINDOWS
183 style
|= wxVSCROLL
|wxHSCROLL
;
184 if ((style
& wxBORDER_MASK
) == 0)
185 style
|= wxBORDER_SUNKEN
;
188 if ( !wxControl::Create(parent
, id
, pos
, size
, style
,
193 m_strings
.sorted
= new wxSortedArrayString
;
195 m_strings
.unsorted
= 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
212 delete m_strings
.sorted
;
214 delete m_strings
.unsorted
;
216 m_strings
.sorted
= NULL
;
219 // ----------------------------------------------------------------------------
221 // ----------------------------------------------------------------------------
223 unsigned int wxListBox::GetCount() const
225 return IsSorted() ? m_strings
.sorted
->size()
226 : m_strings
.unsorted
->size();
229 wxString
wxListBox::GetString(unsigned int n
) const
231 return IsSorted() ? m_strings
.sorted
->Item(n
)
232 : m_strings
.unsorted
->Item(n
);
235 int wxListBox::FindString(const wxString
& s
, bool bCase
) const
237 return IsSorted() ? m_strings
.sorted
->Index(s
, bCase
)
238 : m_strings
.unsorted
->Index(s
, bCase
);
241 // ----------------------------------------------------------------------------
242 // adding/inserting strings
243 // ----------------------------------------------------------------------------
245 int wxListBox::DoInsertItems(const wxArrayStringsAdapter
& items
,
248 wxClientDataType type
)
250 int idx
= wxNOT_FOUND
;
252 const unsigned int numItems
= items
.GetCount();
253 for ( unsigned int i
= 0; i
< numItems
; ++i
)
255 const wxString
& item
= items
[i
];
256 idx
= IsSorted() ? m_strings
.sorted
->Add(item
)
257 : (m_strings
.unsorted
->Insert(item
, pos
), pos
++);
259 m_itemsClientData
.Insert(NULL
, idx
);
260 AssignNewItemClientData(idx
, clientData
, i
, type
);
262 // call the wxCheckListBox hook
266 // the number of items has changed so we might have to show the scrollbar
267 m_updateScrollbarY
= true;
269 // the max width also might have changed - just recalculate it instead of
270 // keeping track of it here, this is probably more efficient for a typical
272 RefreshHorzScrollbar();
274 // note that we have to refresh all the items after the ones we inserted,
275 // not just these items
276 RefreshFromItemToEnd(pos
);
281 void wxListBox::SetString(unsigned int n
, const wxString
& s
)
283 wxCHECK_RET( !IsSorted(), wxT("can't set string in sorted listbox") );
286 (*m_strings
.sorted
)[n
] = s
;
288 (*m_strings
.unsorted
)[n
] = s
;
290 if ( HasHorzScrollbar() )
292 // we need to update m_maxWidth as changing the string may cause the
293 // horz scrollbar [dis]appear
296 GetTextExtent(s
, &width
, NULL
);
298 // it might have increased if the new string is long
299 if ( width
> m_maxWidth
)
303 m_updateScrollbarX
= true;
305 // or also decreased if the old string was the longest one
306 else if ( n
== (unsigned int)m_maxWidthItem
)
308 RefreshHorzScrollbar();
315 // ----------------------------------------------------------------------------
317 // ----------------------------------------------------------------------------
319 void wxListBox::DoClear()
322 m_strings
.sorted
->Clear();
324 m_strings
.unsorted
->Clear();
326 m_itemsClientData
.Clear();
327 m_selections
.Clear();
331 m_updateScrollbarY
= true;
333 RefreshHorzScrollbar();
338 void wxListBox::DoDeleteOneItem(unsigned int n
)
340 wxCHECK_RET( IsValid(n
),
341 wxT("invalid index in wxListBox::Delete") );
343 // do it before removing the index as otherwise the last item will not be
344 // refreshed (as GetCount() will be decremented)
345 RefreshFromItemToEnd(n
);
348 m_strings
.sorted
->RemoveAt(n
);
350 m_strings
.unsorted
->RemoveAt(n
);
352 m_itemsClientData
.RemoveAt(n
);
354 // when the item disappears we must not keep using its index
355 if ( (int)n
== m_current
)
359 else if ( (int)n
< m_current
)
363 //else: current item may stay
365 // update the selections array: the indices of all seletected items after
366 // the one being deleted must change and the item itselfm ust be removed
367 int index
= wxNOT_FOUND
;
368 unsigned int count
= m_selections
.GetCount();
369 for ( unsigned int item
= 0; item
< count
; item
++ )
371 if ( m_selections
[item
] == (int)n
)
373 // remember to delete it later
376 else if ( m_selections
[item
] > (int)n
)
378 // to account for the index shift
379 m_selections
[item
]--;
381 //else: nothing changed for this one
384 if ( index
!= wxNOT_FOUND
)
386 m_selections
.RemoveAt(index
);
389 // the number of items has changed, hence the scrollbar may disappear
390 m_updateScrollbarY
= true;
392 // finally, if the longest item was deleted the scrollbar may disappear
393 if ( (int)n
== m_maxWidthItem
)
395 RefreshHorzScrollbar();
399 // ----------------------------------------------------------------------------
400 // client data handling
401 // ----------------------------------------------------------------------------
403 void wxListBox::DoSetItemClientData(unsigned int n
, void* clientData
)
405 m_itemsClientData
[n
] = clientData
;
408 void *wxListBox::DoGetItemClientData(unsigned int n
) const
410 return m_itemsClientData
[n
];
413 // ----------------------------------------------------------------------------
415 // ----------------------------------------------------------------------------
417 void wxListBox::DoSetSelection(int n
, bool select
)
421 if ( n
== wxNOT_FOUND
)
423 if ( !HasMultipleSelection() )
425 // selecting wxNOT_FOUND is documented to deselect all items
430 else if ( m_selections
.Index(n
) == wxNOT_FOUND
)
432 if ( !HasMultipleSelection() )
434 // selecting an item in a single selection listbox deselects
443 //else: already selected
447 int index
= m_selections
.Index(n
);
448 if ( index
!= wxNOT_FOUND
)
450 m_selections
.RemoveAt(index
);
457 // sanity check: a single selection listbox can't have more than one item
459 wxASSERT_MSG( HasMultipleSelection() || (m_selections
.GetCount() < 2),
460 wxT("multiple selected items in single selection lbox?") );
464 // the newly selected item becomes the current one
469 int wxListBox::GetSelection() const
471 wxCHECK_MSG( !HasMultipleSelection(), wxNOT_FOUND
,
472 wxT("use wxListBox::GetSelections for ths listbox") );
474 return m_selections
.IsEmpty() ? wxNOT_FOUND
: m_selections
[0];
477 static int wxCMPFUNC_CONV
wxCompareInts(int *n
, int *m
)
482 int wxListBox::GetSelections(wxArrayInt
& selections
) const
484 // always return sorted array to the user
485 selections
= m_selections
;
486 unsigned int count
= m_selections
.GetCount();
488 // don't call sort on an empty array
491 selections
.Sort(wxCompareInts
);
497 // ----------------------------------------------------------------------------
498 // refresh logic: we use delayed refreshing which allows to avoid multiple
499 // refreshes (and hence flicker) in case when several listbox items are
500 // added/deleted/changed subsequently
501 // ----------------------------------------------------------------------------
503 void wxListBox::RefreshFromItemToEnd(int from
)
505 RefreshItems(from
, GetCount() - from
);
508 void wxListBox::RefreshItems(int from
, int count
)
510 switch ( m_updateCount
)
514 m_updateCount
= count
;
518 // we refresh everything anyhow
522 // add these items to the others which we have to refresh
523 if ( m_updateFrom
< from
)
525 count
+= from
- m_updateFrom
;
526 if ( m_updateCount
< count
)
527 m_updateCount
= count
;
529 else // m_updateFrom >= from
531 int updateLast
= wxMax(m_updateFrom
+ m_updateCount
,
534 m_updateCount
= updateLast
- m_updateFrom
;
539 void wxListBox::RefreshItem(int n
)
541 switch ( m_updateCount
)
544 // refresh this item only
550 // we refresh everything anyhow
554 // add this item to the others which we have to refresh
555 if ( m_updateFrom
< n
)
557 if ( m_updateCount
< n
- m_updateFrom
+ 1 )
558 m_updateCount
= n
- m_updateFrom
+ 1;
560 else // n <= m_updateFrom
562 m_updateCount
+= m_updateFrom
- n
;
568 void wxListBox::RefreshAll()
573 void wxListBox::RefreshHorzScrollbar()
575 m_maxWidth
= 0; // recalculate it
576 m_updateScrollbarX
= true;
579 void wxListBox::UpdateScrollbars()
581 wxSize size
= GetClientSize();
583 // is our height enough to show all items?
584 unsigned int nLines
= GetCount();
585 wxCoord lineHeight
= GetLineHeight();
586 bool showScrollbarY
= (int)nLines
*lineHeight
> size
.y
;
588 // check the width too if required
589 wxCoord charWidth
, maxWidth
;
591 if ( HasHorzScrollbar() )
593 charWidth
= GetCharWidth();
594 maxWidth
= GetMaxWidth();
595 showScrollbarX
= maxWidth
> size
.x
;
597 else // never show it
599 charWidth
= maxWidth
= 0;
600 showScrollbarX
= false;
603 // what should be the scrollbar range now?
604 int scrollRangeX
= showScrollbarX
605 ? (maxWidth
+ charWidth
- 1) / charWidth
+ 2 // FIXME
607 int scrollRangeY
= showScrollbarY
609 (size
.y
% lineHeight
+ lineHeight
- 1) / lineHeight
612 // reset scrollbars if something changed: either the visibility status
613 // or the range of a scrollbar which is shown
614 if ( (showScrollbarY
!= m_showScrollbarY
) ||
615 (showScrollbarX
!= m_showScrollbarX
) ||
616 (showScrollbarY
&& (scrollRangeY
!= m_scrollRangeY
)) ||
617 (showScrollbarX
&& (scrollRangeX
!= m_scrollRangeX
)) )
620 GetViewStart(&x
, &y
);
621 SetScrollbars(charWidth
, lineHeight
,
622 scrollRangeX
, scrollRangeY
,
625 m_showScrollbarX
= showScrollbarX
;
626 m_showScrollbarY
= showScrollbarY
;
628 m_scrollRangeX
= scrollRangeX
;
629 m_scrollRangeY
= scrollRangeY
;
633 void wxListBox::UpdateItems()
635 // only refresh the items which must be refreshed
636 if ( m_updateCount
== -1 )
639 wxLogTrace(wxT("listbox"), wxT("Refreshing all"));
645 wxSize size
= GetClientSize();
648 rect
.height
= size
.y
;
649 rect
.y
+= m_updateFrom
*GetLineHeight();
650 rect
.height
= m_updateCount
*GetLineHeight();
652 // we don't need to calculate x position as we always refresh the
654 CalcScrolledPosition(0, rect
.y
, NULL
, &rect
.y
);
656 wxLogTrace(wxT("listbox"), wxT("Refreshing items %d..%d (%d-%d)"),
657 m_updateFrom
, m_updateFrom
+ m_updateCount
- 1,
658 rect
.GetTop(), rect
.GetBottom());
660 Refresh(true, &rect
);
664 void wxListBox::OnInternalIdle()
666 if ( m_updateScrollbarY
|| m_updateScrollbarX
)
671 m_updateScrollbarY
= false;
674 if ( m_currentChanged
)
676 DoEnsureVisible(m_current
);
678 m_currentChanged
= false;
687 wxListBoxBase::OnInternalIdle();
690 // ----------------------------------------------------------------------------
692 // ----------------------------------------------------------------------------
694 wxBorder
wxListBox::GetDefaultBorder() const
696 return wxBORDER_SUNKEN
;
699 void wxListBox::DoDraw(wxControlRenderer
*renderer
)
701 // adjust the DC to account for scrolling
702 wxDC
& dc
= renderer
->GetDC();
704 dc
.SetFont(GetFont());
706 // get the update rect
707 wxRect rectUpdate
= GetUpdateClientRect();
710 CalcUnscrolledPosition(0, rectUpdate
.GetTop(), NULL
, &yTop
);
711 CalcUnscrolledPosition(0, rectUpdate
.GetBottom(), NULL
, &yBottom
);
713 // get the items which must be redrawn
714 wxCoord lineHeight
= GetLineHeight();
715 unsigned int itemFirst
= yTop
/ lineHeight
,
716 itemLast
= (yBottom
+ lineHeight
- 1) / lineHeight
,
717 itemMax
= GetCount();
719 if ( itemFirst
>= itemMax
)
722 if ( itemLast
> itemMax
)
726 wxLogTrace(wxT("listbox"), wxT("Repainting items %d..%d"),
727 itemFirst
, itemLast
);
729 DoDrawRange(renderer
, itemFirst
, itemLast
);
732 void wxListBox::DoDrawRange(wxControlRenderer
*renderer
,
733 int itemFirst
, int itemLast
)
735 renderer
->DrawItems(this, itemFirst
, itemLast
);
738 // ----------------------------------------------------------------------------
740 // ----------------------------------------------------------------------------
742 bool wxListBox::SetFont(const wxFont
& font
)
744 if ( !wxControl::SetFont(font
) )
754 void wxListBox::CalcItemsPerPage()
756 m_lineHeight
= GetRenderer()->GetListboxItemHeight(GetCharHeight());
757 m_itemsPerPage
= GetClientSize().y
/ m_lineHeight
;
760 int wxListBox::GetItemsPerPage() const
762 if ( !m_itemsPerPage
)
764 wxConstCast(this, wxListBox
)->CalcItemsPerPage();
767 return m_itemsPerPage
;
770 wxCoord
wxListBox::GetLineHeight() const
774 wxConstCast(this, wxListBox
)->CalcItemsPerPage();
780 wxCoord
wxListBox::GetMaxWidth() const
782 if ( m_maxWidth
== 0 )
784 wxListBox
*self
= wxConstCast(this, wxListBox
);
786 unsigned int count
= GetCount();
787 for ( unsigned int n
= 0; n
< count
; n
++ )
789 GetTextExtent(this->GetString(n
), &width
, NULL
);
790 if ( width
> m_maxWidth
)
792 self
->m_maxWidth
= width
;
793 self
->m_maxWidthItem
= n
;
801 void wxListBox::OnSize(wxSizeEvent
& event
)
803 // recalculate the number of items per page
806 // the scrollbars might [dis]appear
808 m_updateScrollbarY
= true;
813 void wxListBox::DoSetFirstItem(int n
)
818 void wxListBox::DoSetSize(int x
, int y
,
819 int width
, int height
,
822 if ( GetWindowStyle() & wxLB_INT_HEIGHT
)
824 // we must round up the height to an entire number of rows
826 // the client area must contain an int number of rows, so take borders
828 wxRect rectBorders
= GetRenderer()->GetBorderDimensions(GetBorder());
829 wxCoord hBorders
= rectBorders
.y
+ rectBorders
.height
;
831 wxCoord hLine
= GetLineHeight();
832 height
= ((height
- hBorders
+ hLine
- 1) / hLine
)*hLine
+ hBorders
;
835 wxListBoxBase::DoSetSize(x
, y
, width
, height
, sizeFlags
);
838 wxSize
wxListBox::DoGetBestClientSize() const
843 unsigned int count
= GetCount();
844 for ( unsigned int n
= 0; n
< count
; n
++ )
847 GetTextExtent(this->GetString(n
), &w
, &h
);
855 // if the listbox is empty, still give it some non zero (even if
856 // arbitrary) size - otherwise, leave small margin around the strings
860 width
+= 3*GetCharWidth();
863 height
= GetCharHeight();
865 // we need the height of the entire listbox, not just of one line
866 height
*= wxMax(count
, 7);
868 return wxSize(width
, height
);
871 // ----------------------------------------------------------------------------
873 // ----------------------------------------------------------------------------
875 bool wxListBox::SendEvent(wxEventType type
, int item
)
877 wxCommandEvent
event(type
, m_windowId
);
878 event
.SetEventObject(this);
880 // use the current item by default
886 // client data and string parameters only make sense if we have an item
889 if ( HasClientObjectData() )
890 event
.SetClientObject(GetClientObject(item
));
891 else if ( HasClientUntypedData() )
892 event
.SetClientData(GetClientData(item
));
894 event
.SetString(GetString(item
));
899 return GetEventHandler()->ProcessEvent(event
);
902 void wxListBox::SetCurrentItem(int n
)
904 if ( n
!= m_current
)
906 if ( m_current
!= -1 )
907 RefreshItem(m_current
);
911 if ( m_current
!= -1 )
913 m_currentChanged
= true;
915 RefreshItem(m_current
);
918 //else: nothing to do
921 bool wxListBox::FindItem(const wxString
& prefix
, bool strictlyAfter
)
923 unsigned int count
= GetCount();
926 // empty listbox, we can't find anything in it
930 // start either from the current item or from the next one if strictlyAfter
935 // the following line will set first correctly to 0 if there is no
936 // selection (m_current == -1)
937 first
= m_current
== (int)(count
- 1) ? 0 : m_current
+ 1;
939 else // start with the current
941 first
= m_current
== -1 ? 0 : m_current
;
944 int last
= first
== 0 ? count
- 1 : first
- 1;
946 // if this is not true we'd never exit from the loop below!
947 wxASSERT_MSG( first
< (int)count
&& last
< (int)count
, wxT("logic error") );
949 // precompute it outside the loop
950 size_t len
= prefix
.length();
952 // loop over all items in the listbox
953 for ( int item
= first
; item
!= (int)last
; item
< (int)(count
- 1) ? item
++ : item
= 0 )
955 if ( wxStrnicmp(this->GetString(item
).c_str(), prefix
, len
) == 0 )
957 SetCurrentItem(item
);
959 if ( !(GetWindowStyle() & wxLB_MULTIPLE
) )
962 SelectAndNotify(item
);
964 if ( GetWindowStyle() & wxLB_EXTENDED
)
965 AnchorSelection(item
);
976 void wxListBox::EnsureVisible(int n
)
978 if ( m_updateScrollbarY
)
983 m_updateScrollbarY
= false;
989 void wxListBox::DoEnsureVisible(int n
)
991 if ( !m_showScrollbarY
)
993 // nothing to do - everything is shown anyhow
998 GetViewStart(0, &first
);
1001 // we need to scroll upwards, so make the current item appear on top
1002 // of the shown range
1007 int last
= first
+ GetClientSize().y
/ GetLineHeight() - 1;
1010 // scroll down: the current item appears at the bottom of the
1012 Scroll(0, n
- (last
- first
));
1017 void wxListBox::ChangeCurrent(int diff
)
1019 int current
= m_current
== -1 ? 0 : m_current
;
1023 int last
= GetCount() - 1;
1026 else if ( current
> last
)
1029 SetCurrentItem(current
);
1032 void wxListBox::ExtendSelection(int itemTo
)
1034 // if we don't have the explicit values for selection start/end, make them
1036 if ( m_selAnchor
== -1 )
1037 m_selAnchor
= m_current
;
1042 // swap the start/end of selection range if necessary
1043 int itemFrom
= m_selAnchor
;
1044 if ( itemFrom
> itemTo
)
1046 int itemTmp
= itemFrom
;
1051 // the selection should now include all items in the range between the
1052 // anchor and the specified item and only them
1055 for ( n
= 0; n
< itemFrom
; n
++ )
1060 for ( ; n
<= itemTo
; n
++ )
1065 unsigned int count
= GetCount();
1066 for ( ; n
< (int)count
; n
++ )
1072 void wxListBox::DoSelect(int item
, bool sel
)
1076 // go to this item first
1077 SetCurrentItem(item
);
1080 // the current item is the one we want to change: either it was just
1081 // changed above to be the same as item or item == -1 in which we case we
1082 // are supposed to use the current one anyhow
1083 if ( m_current
!= -1 )
1086 SetSelection(m_current
, sel
);
1090 void wxListBox::SelectAndNotify(int item
)
1094 SendEvent(wxEVT_LISTBOX
);
1097 void wxListBox::Activate(int item
)
1100 SetCurrentItem(item
);
1104 if ( !(GetWindowStyle() & wxLB_MULTIPLE
) )
1113 SendEvent(wxEVT_LISTBOX_DCLICK
);
1117 // ----------------------------------------------------------------------------
1119 // ----------------------------------------------------------------------------
1122 The numArg here is the listbox item index while the strArg is used
1123 differently for the different actions:
1125 a) for wxACTION_LISTBOX_FIND it has the natural meaning: this is the string
1128 b) for wxACTION_LISTBOX_SELECT and wxACTION_LISTBOX_EXTENDSEL it is used
1129 to decide if the listbox should send the notification event (it is empty)
1130 or not (it is not): this allows us to reuse the same action for when the
1131 user is dragging the mouse when it has been released although in the
1132 first case no notification is sent while in the second it is sent.
1134 bool wxListBox::PerformAction(const wxControlAction
& action
,
1136 const wxString
& strArg
)
1138 int item
= (int)numArg
;
1140 if ( action
== wxACTION_LISTBOX_SETFOCUS
)
1142 SetCurrentItem(item
);
1144 else if ( action
== wxACTION_LISTBOX_ACTIVATE
)
1148 else if ( action
== wxACTION_LISTBOX_TOGGLE
)
1153 if ( IsSelected(item
) )
1156 SelectAndNotify(item
);
1158 else if ( action
== wxACTION_LISTBOX_SELECT
)
1162 if ( strArg
.empty() )
1163 SelectAndNotify(item
);
1167 else if ( action
== wxACTION_LISTBOX_SELECTADD
)
1169 else if ( action
== wxACTION_LISTBOX_UNSELECT
)
1171 else if ( action
== wxACTION_LISTBOX_MOVEDOWN
)
1173 else if ( action
== wxACTION_LISTBOX_MOVEUP
)
1175 else if ( action
== wxACTION_LISTBOX_PAGEDOWN
)
1176 ChangeCurrent(GetItemsPerPage());
1177 else if ( action
== wxACTION_LISTBOX_PAGEUP
)
1178 ChangeCurrent(-GetItemsPerPage());
1179 else if ( action
== wxACTION_LISTBOX_START
)
1181 else if ( action
== wxACTION_LISTBOX_END
)
1182 SetCurrentItem(GetCount() - 1);
1183 else if ( action
== wxACTION_LISTBOX_UNSELECTALL
)
1185 else if ( action
== wxACTION_LISTBOX_EXTENDSEL
)
1186 ExtendSelection(item
);
1187 else if ( action
== wxACTION_LISTBOX_FIND
)
1188 FindNextItem(strArg
);
1189 else if ( action
== wxACTION_LISTBOX_ANCHOR
)
1190 AnchorSelection(item
== -1 ? m_current
: item
);
1191 else if ( action
== wxACTION_LISTBOX_SELECTALL
||
1192 action
== wxACTION_LISTBOX_SELTOGGLE
)
1193 wxFAIL_MSG(wxT("unimplemented yet"));
1195 return wxControl::PerformAction(action
, numArg
, strArg
);
1201 wxInputHandler
*wxListBox::GetStdInputHandler(wxInputHandler
*handlerDef
)
1203 static wxStdListboxInputHandler
s_handler(handlerDef
);
1208 // ============================================================================
1209 // implementation of wxStdListboxInputHandler
1210 // ============================================================================
1212 wxStdListboxInputHandler::wxStdListboxInputHandler(wxInputHandler
*handler
,
1213 bool toggleOnPressAlways
)
1214 : wxStdInputHandler(handler
)
1217 m_toggleOnPressAlways
= toggleOnPressAlways
;
1218 m_actionMouse
= wxACTION_NONE
;
1219 m_trackMouseOutside
= true;
1222 int wxStdListboxInputHandler::HitTest(const wxListBox
*lbox
,
1223 const wxMouseEvent
& event
)
1225 int item
= HitTestUnsafe(lbox
, event
);
1227 return FixItemIndex(lbox
, item
);
1230 int wxStdListboxInputHandler::HitTestUnsafe(const wxListBox
*lbox
,
1231 const wxMouseEvent
& event
)
1233 wxPoint pt
= event
.GetPosition();
1234 pt
-= lbox
->GetClientAreaOrigin();
1236 lbox
->CalcUnscrolledPosition(0, pt
.y
, NULL
, &y
);
1237 return y
/ lbox
->GetLineHeight();
1240 int wxStdListboxInputHandler::FixItemIndex(const wxListBox
*lbox
,
1245 // mouse is above the first item
1248 else if ( (unsigned int)item
>= lbox
->GetCount() )
1250 // mouse is below the last item
1251 item
= lbox
->GetCount() - 1;
1257 bool wxStdListboxInputHandler::IsValidIndex(const wxListBox
*lbox
, int item
)
1259 return item
>= 0 && (unsigned int)item
< lbox
->GetCount();
1263 wxStdListboxInputHandler::SetupCapture(wxListBox
*lbox
,
1264 const wxMouseEvent
& event
,
1267 // we currently only allow selecting with the left mouse button, if we
1268 // do need to allow using other buttons too we might use the code
1271 m_btnCapture
= event
.LeftDown()
1280 wxControlAction action
;
1281 if ( lbox
->HasMultipleSelection() )
1283 if ( lbox
->GetWindowStyle() & wxLB_MULTIPLE
)
1285 if ( m_toggleOnPressAlways
)
1287 // toggle the item right now
1288 action
= wxACTION_LISTBOX_TOGGLE
;
1292 m_actionMouse
= wxACTION_LISTBOX_SETFOCUS
;
1294 else // wxLB_EXTENDED listbox
1296 // simple click in an extended sel listbox clears the old
1297 // selection and adds the clicked item to it then, ctrl-click
1298 // toggles an item to it and shift-click adds a range between
1299 // the old selection anchor and the clicked item
1300 if ( event
.ControlDown() )
1302 lbox
->PerformAction(wxACTION_LISTBOX_ANCHOR
, item
);
1304 action
= wxACTION_LISTBOX_TOGGLE
;
1306 else if ( event
.ShiftDown() )
1308 action
= wxACTION_LISTBOX_EXTENDSEL
;
1310 else // simple click
1312 lbox
->PerformAction(wxACTION_LISTBOX_ANCHOR
, item
);
1314 action
= wxACTION_LISTBOX_SELECT
;
1317 m_actionMouse
= wxACTION_LISTBOX_EXTENDSEL
;
1320 else // single selection
1323 action
= wxACTION_LISTBOX_SELECT
;
1326 // by default we always do track it
1327 m_trackMouseOutside
= true;
1332 bool wxStdListboxInputHandler::HandleKey(wxInputConsumer
*consumer
,
1333 const wxKeyEvent
& event
,
1336 // we're only interested in the key press events
1337 if ( pressed
&& !event
.AltDown() )
1339 bool isMoveCmd
= true;
1340 int style
= consumer
->GetInputWindow()->GetWindowStyle();
1342 wxControlAction action
;
1345 int keycode
= event
.GetKeyCode();
1350 action
= wxACTION_LISTBOX_MOVEUP
;
1354 action
= wxACTION_LISTBOX_MOVEDOWN
;
1358 action
= wxACTION_LISTBOX_PAGEUP
;
1362 action
= wxACTION_LISTBOX_PAGEDOWN
;
1366 action
= wxACTION_LISTBOX_START
;
1370 action
= wxACTION_LISTBOX_END
;
1375 if ( style
& wxLB_MULTIPLE
)
1377 action
= wxACTION_LISTBOX_TOGGLE
;
1383 action
= wxACTION_LISTBOX_ACTIVATE
;
1388 if ( (keycode
< 255) && wxIsalnum((wxChar
)keycode
) )
1390 action
= wxACTION_LISTBOX_FIND
;
1391 strArg
= (wxChar
)keycode
;
1395 if ( !action
.IsEmpty() )
1397 consumer
->PerformAction(action
, -1, strArg
);
1401 if ( style
& wxLB_SINGLE
)
1403 // the current item is always the one selected
1404 consumer
->PerformAction(wxACTION_LISTBOX_SELECT
);
1406 else if ( style
& wxLB_EXTENDED
)
1408 if ( event
.ShiftDown() )
1409 consumer
->PerformAction(wxACTION_LISTBOX_EXTENDSEL
);
1412 // select the item and make it the new selection anchor
1413 consumer
->PerformAction(wxACTION_LISTBOX_SELECT
);
1414 consumer
->PerformAction(wxACTION_LISTBOX_ANCHOR
);
1417 //else: nothing to do for multiple selection listboxes
1424 return wxStdInputHandler::HandleKey(consumer
, event
, pressed
);
1427 bool wxStdListboxInputHandler::HandleMouse(wxInputConsumer
*consumer
,
1428 const wxMouseEvent
& event
)
1430 wxListBox
*lbox
= wxStaticCast(consumer
->GetInputWindow(), wxListBox
);
1431 int item
= HitTest(lbox
, event
);
1432 wxControlAction action
;
1434 // when the left mouse button is pressed, capture the mouse and track the
1435 // item under mouse (if the mouse leaves the window, we will still be
1436 // getting the mouse move messages generated by wxScrollWindow)
1437 if ( event
.LeftDown() )
1439 // capture the mouse to track the selected item
1440 lbox
->CaptureMouse();
1442 action
= SetupCapture(lbox
, event
, item
);
1444 else if ( m_btnCapture
&& event
.ButtonUp(m_btnCapture
) )
1446 // when the left mouse button is released, release the mouse too
1447 wxWindow
*winCapture
= wxWindow::GetCapture();
1450 winCapture
->ReleaseMouse();
1453 action
= m_actionMouse
;
1455 //else: the mouse wasn't presed over the listbox, only released here
1457 else if ( event
.LeftDClick() )
1459 action
= wxACTION_LISTBOX_ACTIVATE
;
1462 if ( !action
.IsEmpty() )
1464 lbox
->PerformAction(action
, item
);
1469 return wxStdInputHandler::HandleMouse(consumer
, event
);
1472 bool wxStdListboxInputHandler::HandleMouseMove(wxInputConsumer
*consumer
,
1473 const wxMouseEvent
& event
)
1475 wxWindow
*winCapture
= wxWindow::GetCapture();
1476 if ( winCapture
&& (event
.GetEventObject() == winCapture
) )
1478 wxListBox
*lbox
= wxStaticCast(consumer
->GetInputWindow(), wxListBox
);
1480 if ( !m_btnCapture
|| !m_trackMouseOutside
)
1482 // someone captured the mouse for us (we always set m_btnCapture
1483 // when we do it ourselves): in this case we only react to
1484 // the mouse messages when they happen inside the listbox
1485 if ( lbox
->HitTest(event
.GetPosition()) != wxHT_WINDOW_INSIDE
)
1489 int item
= HitTest(lbox
, event
);
1490 if ( !m_btnCapture
)
1492 // now that we have the mouse inside the listbox, do capture it
1493 // normally - but ensure that we will still ignore the outside
1495 SetupCapture(lbox
, event
, item
);
1497 m_trackMouseOutside
= false;
1500 if ( IsValidIndex(lbox
, item
) )
1502 // pass something into strArg to tell the listbox that it shouldn't
1503 // send the notification message: see PerformAction() above
1504 lbox
->PerformAction(m_actionMouse
, item
, wxT("no"));
1506 // else: don't pass invalid index to the listbox
1508 else // we don't have capture any more
1512 // if we lost capture unexpectedly (someone else took the capture
1513 // from us), return to a consistent state
1518 return wxStdInputHandler::HandleMouseMove(consumer
, event
);
1521 #endif // wxUSE_LISTBOX