1 /////////////////////////////////////////////////////////////////////////////
2 // Name: 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 #if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
21 #pragma implementation "univlistbox.h"
24 #include "wx/wxprec.h"
35 #include "wx/dcclient.h"
36 #include "wx/listbox.h"
37 #include "wx/validate.h"
40 #include "wx/univ/renderer.h"
41 #include "wx/univ/inphand.h"
42 #include "wx/univ/theme.h"
44 // ============================================================================
45 // implementation of wxListBox
46 // ============================================================================
48 IMPLEMENT_DYNAMIC_CLASS(wxListBox
, wxControl
)
50 BEGIN_EVENT_TABLE(wxListBox
, wxListBoxBase
)
51 EVT_SIZE(wxListBox::OnSize
)
54 // ----------------------------------------------------------------------------
56 // ----------------------------------------------------------------------------
58 void wxListBox::Init()
60 // will be calculated later when needed
68 // no items hence no current item
71 m_currentChanged
= false;
73 // no need to update anything initially
76 // no scrollbars to show nor update
80 m_showScrollbarY
= false;
83 wxListBox::wxListBox(wxWindow
*parent
,
87 const wxArrayString
& choices
,
89 const wxValidator
& validator
,
94 Create(parent
, id
, pos
, size
, choices
, style
, validator
, name
);
97 bool wxListBox::Create(wxWindow
*parent
,
101 const wxArrayString
& choices
,
103 const wxValidator
& validator
,
104 const wxString
&name
)
106 wxCArrayString
chs(choices
);
108 return Create(parent
, id
, pos
, size
, chs
.GetCount(), chs
.GetStrings(),
109 style
, validator
, name
);
112 bool wxListBox::Create(wxWindow
*parent
,
117 const wxString choices
[],
119 const wxValidator
& validator
,
120 const wxString
&name
)
122 // for compatibility accept both the new and old styles - they mean the
124 if ( style
& wxLB_ALWAYS_SB
)
125 style
|= wxALWAYS_SHOW_SB
;
127 // if we don't have neither multiple nor extended flag, we must have the
128 // single selection listbox
129 if ( !(style
& (wxLB_MULTIPLE
| wxLB_EXTENDED
)) )
130 style
|= wxLB_SINGLE
;
132 #if wxUSE_TWO_WINDOWS
133 style
|= wxVSCROLL
|wxHSCROLL
;
134 if ((style
& wxBORDER_MASK
) == 0)
135 style
|= wxBORDER_SUNKEN
;
138 if ( !wxControl::Create(parent
, id
, pos
, size
, style
,
144 if ( style
& wxLB_SORT
)
145 m_stringsSorted
= new wxSortedArrayString
;
147 m_strings
= new wxArrayString
;
153 CreateInputHandler(wxINP_HANDLER_LISTBOX
);
158 wxListBox::~wxListBox()
160 // call this just to free the client data -- and avoid leaking memory
164 delete m_stringsSorted
;
171 // ----------------------------------------------------------------------------
172 // adding/inserting strings
173 // ----------------------------------------------------------------------------
175 int wxListBox::DoAppend(const wxString
& item
)
181 index
= m_stringsSorted
->Add(item
);
185 index
= m_strings
->GetCount();
186 m_strings
->Add(item
);
189 m_itemsClientData
.Insert(NULL
, index
);
191 m_updateScrollbarY
= true;
193 if ( HasHorzScrollbar() )
195 // has the max width increased?
197 GetTextExtent(item
, &width
, NULL
);
198 if ( width
> m_maxWidth
)
201 m_maxWidthItem
= index
;
202 m_updateScrollbarX
= true;
206 RefreshFromItemToEnd(index
);
211 void wxListBox::DoInsertItems(const wxArrayString
& items
, int pos
)
213 // the position of the item being added to a sorted listbox can't be
215 wxCHECK_RET( !IsSorted(), _T("can't insert items into sorted listbox") );
217 size_t count
= items
.GetCount();
218 for ( size_t n
= 0; n
< count
; n
++ )
220 m_strings
->Insert(items
[n
], pos
+ n
);
221 m_itemsClientData
.Insert(NULL
, pos
+ n
);
224 // the number of items has changed so we might have to show the scrollbar
225 m_updateScrollbarY
= true;
227 // the max width also might have changed - just recalculate it instead of
228 // keeping track of it here, this is probably more efficient for a typical
230 RefreshHorzScrollbar();
232 // note that we have to refresh all the items after the ones we inserted,
233 // not just these items
234 RefreshFromItemToEnd(pos
);
237 void wxListBox::DoSetItems(const wxArrayString
& items
, void **clientData
)
241 size_t count
= items
.GetCount();
245 m_strings
->Alloc(count
);
246 m_itemsClientData
.Alloc(count
);
247 for ( size_t n
= 0; n
< count
; n
++ )
253 index
= m_stringsSorted
->Add(items
[n
]);
257 index
= m_strings
->GetCount();
258 m_strings
->Add(items
[n
]);
261 m_itemsClientData
.Insert(clientData
? clientData
[n
] : NULL
, index
);
264 m_updateScrollbarY
= true;
269 void wxListBox::SetString(int n
, const wxString
& s
)
271 if ( HasHorzScrollbar() )
273 // we need to update m_maxWidth as changing the string may cause the
274 // horz scrollbar [dis]appear
277 GetTextExtent(s
, &width
, NULL
);
279 // it might have increased if the new string is long
280 if ( width
> m_maxWidth
)
284 m_updateScrollbarX
= true;
286 // or also decreased if the old string was the longest one
287 else if ( n
== m_maxWidthItem
)
289 RefreshHorzScrollbar();
292 else // no horz scrollbar
300 // ----------------------------------------------------------------------------
302 // ----------------------------------------------------------------------------
304 void wxListBox::DoClear()
308 if ( HasClientObjectData() )
310 size_t count
= m_itemsClientData
.GetCount();
311 for ( size_t n
= 0; n
< count
; n
++ )
313 delete (wxClientData
*) m_itemsClientData
[n
];
317 m_itemsClientData
.Clear();
318 m_selections
.Clear();
323 void wxListBox::Clear()
327 m_updateScrollbarY
= true;
329 RefreshHorzScrollbar();
334 void wxListBox::Delete(int n
)
336 wxCHECK_RET( n
>= 0 && n
< GetCount(),
337 _T("invalid index in wxListBox::Delete") );
339 // do it before removing the index as otherwise the last item will not be
340 // refreshed (as GetCount() will be decremented)
341 RefreshFromItemToEnd(n
);
343 m_strings
->RemoveAt(n
);
345 if ( HasClientObjectData() )
347 delete (wxClientData
*)m_itemsClientData
[n
];
350 m_itemsClientData
.RemoveAt(n
);
352 // when the item disappears we must not keep using its index
353 if ( n
== m_current
)
357 else if ( n
< m_current
)
361 //else: current item may stay
363 // update the selections array: the indices of all seletected items after
364 // the one being deleted must change and the item itselfm ust be removed
365 int index
= wxNOT_FOUND
;
366 size_t count
= m_selections
.GetCount();
367 for ( size_t item
= 0; item
< count
; item
++ )
369 if ( m_selections
[item
] == n
)
371 // remember to delete it later
374 else if ( m_selections
[item
] > n
)
376 // to account for the index shift
377 m_selections
[item
]--;
379 //else: nothing changed for this one
382 if ( index
!= wxNOT_FOUND
)
384 m_selections
.RemoveAt(index
);
387 // the number of items has changed, hence the scrollbar may disappear
388 m_updateScrollbarY
= true;
390 // finally, if the longest item was deleted the scrollbar may disappear
391 if ( n
== m_maxWidthItem
)
393 RefreshHorzScrollbar();
397 // ----------------------------------------------------------------------------
398 // client data handling
399 // ----------------------------------------------------------------------------
401 void wxListBox::DoSetItemClientData(int n
, void* clientData
)
403 m_itemsClientData
[n
] = clientData
;
406 void *wxListBox::DoGetItemClientData(int n
) const
408 return m_itemsClientData
[n
];
411 void wxListBox::DoSetItemClientObject(int n
, wxClientData
* clientData
)
413 m_itemsClientData
[n
] = clientData
;
416 wxClientData
* wxListBox::DoGetItemClientObject(int n
) const
418 return (wxClientData
*)m_itemsClientData
[n
];
421 // ----------------------------------------------------------------------------
423 // ----------------------------------------------------------------------------
425 void wxListBox::SetSelection(int n
, bool select
)
429 if ( m_selections
.Index(n
) == wxNOT_FOUND
)
431 if ( !HasMultipleSelection() )
433 // selecting an item in a single selection listbox deselects
442 //else: already selected
446 int index
= m_selections
.Index(n
);
447 if ( index
!= wxNOT_FOUND
)
449 m_selections
.RemoveAt(index
);
456 // sanity check: a single selection listbox can't have more than one item
458 wxASSERT_MSG( HasMultipleSelection() || (m_selections
.GetCount() < 2),
459 _T("multiple selected items in single selection lbox?") );
463 // the newly selected item becomes the current one
468 int wxListBox::GetSelection() const
470 wxCHECK_MSG( !HasMultipleSelection(), -1,
471 _T("use wxListBox::GetSelections for ths listbox") );
473 return m_selections
.IsEmpty() ? -1 : m_selections
[0];
476 int wxCMPFUNC_CONV
wxCompareInts(int *n
, int *m
)
481 int wxListBox::GetSelections(wxArrayInt
& selections
) const
483 // always return sorted array to the user
484 selections
= m_selections
;
485 size_t count
= m_selections
.GetCount();
487 // don't call sort on an empty array
490 selections
.Sort(wxCompareInts
);
496 // ----------------------------------------------------------------------------
497 // refresh logic: we use delayed refreshing which allows to avoid multiple
498 // refreshes (and hence flicker) in case when several listbox items are
499 // added/deleted/changed subsequently
500 // ----------------------------------------------------------------------------
502 void wxListBox::RefreshFromItemToEnd(int from
)
504 RefreshItems(from
, GetCount() - from
);
507 void wxListBox::RefreshItems(int from
, int count
)
509 switch ( m_updateCount
)
513 m_updateCount
= count
;
517 // we refresh everything anyhow
521 // add these items to the others which we have to refresh
522 if ( m_updateFrom
< from
)
524 count
+= from
- m_updateFrom
;
525 if ( m_updateCount
< count
)
526 m_updateCount
= count
;
528 else // m_updateFrom >= from
530 int updateLast
= wxMax(m_updateFrom
+ m_updateCount
,
533 m_updateCount
= updateLast
- m_updateFrom
;
538 void wxListBox::RefreshItem(int n
)
540 switch ( m_updateCount
)
543 // refresh this item only
549 // we refresh everything anyhow
553 // add this item to the others which we have to refresh
554 if ( m_updateFrom
< n
)
556 if ( m_updateCount
< n
- m_updateFrom
+ 1 )
557 m_updateCount
= n
- m_updateFrom
+ 1;
559 else // n <= m_updateFrom
561 m_updateCount
+= m_updateFrom
- n
;
567 void wxListBox::RefreshAll()
572 void wxListBox::RefreshHorzScrollbar()
574 m_maxWidth
= 0; // recalculate it
575 m_updateScrollbarX
= true;
578 void wxListBox::UpdateScrollbars()
580 wxSize size
= GetClientSize();
582 // is our height enough to show all items?
583 int nLines
= GetCount();
584 wxCoord lineHeight
= GetLineHeight();
585 bool showScrollbarY
= nLines
*lineHeight
> size
.y
;
587 // check the width too if required
588 wxCoord charWidth
, maxWidth
;
590 if ( HasHorzScrollbar() )
592 charWidth
= GetCharWidth();
593 maxWidth
= GetMaxWidth();
594 showScrollbarX
= maxWidth
> size
.x
;
596 else // never show it
598 charWidth
= maxWidth
= 0;
599 showScrollbarX
= false;
602 // what should be the scrollbar range now?
603 int scrollRangeX
= showScrollbarX
604 ? (maxWidth
+ charWidth
- 1) / charWidth
+ 2 // FIXME
606 int scrollRangeY
= showScrollbarY
608 (size
.y
% lineHeight
+ lineHeight
- 1) / lineHeight
611 // reset scrollbars if something changed: either the visibility status
612 // or the range of a scrollbar which is shown
613 if ( (showScrollbarY
!= m_showScrollbarY
) ||
614 (showScrollbarX
!= m_showScrollbarX
) ||
615 (showScrollbarY
&& (scrollRangeY
!= m_scrollRangeY
)) ||
616 (showScrollbarX
&& (scrollRangeX
!= m_scrollRangeX
)) )
619 GetViewStart(&x
, &y
);
620 SetScrollbars(charWidth
, lineHeight
,
621 scrollRangeX
, scrollRangeY
,
624 m_showScrollbarX
= showScrollbarX
;
625 m_showScrollbarY
= showScrollbarY
;
627 m_scrollRangeX
= scrollRangeX
;
628 m_scrollRangeY
= scrollRangeY
;
632 void wxListBox::UpdateItems()
634 // only refresh the items which must be refreshed
635 if ( m_updateCount
== -1 )
638 wxLogTrace(_T("listbox"), _T("Refreshing all"));
644 wxSize size
= GetClientSize();
647 rect
.height
= size
.y
;
648 rect
.y
+= m_updateFrom
*GetLineHeight();
649 rect
.height
= m_updateCount
*GetLineHeight();
651 // we don't need to calculate x position as we always refresh the
653 CalcScrolledPosition(0, rect
.y
, NULL
, &rect
.y
);
655 wxLogTrace(_T("listbox"), _T("Refreshing items %d..%d (%d-%d)"),
656 m_updateFrom
, m_updateFrom
+ m_updateCount
- 1,
657 rect
.GetTop(), rect
.GetBottom());
659 Refresh(true, &rect
);
663 void wxListBox::OnInternalIdle()
665 if ( m_updateScrollbarY
|| m_updateScrollbarX
)
670 m_updateScrollbarY
= false;
673 if ( m_currentChanged
)
675 DoEnsureVisible(m_current
);
677 m_currentChanged
= false;
686 wxListBoxBase::OnInternalIdle();
689 // ----------------------------------------------------------------------------
691 // ----------------------------------------------------------------------------
693 wxBorder
wxListBox::GetDefaultBorder() const
695 return wxBORDER_SUNKEN
;
698 void wxListBox::DoDraw(wxControlRenderer
*renderer
)
700 // adjust the DC to account for scrolling
701 wxDC
& dc
= renderer
->GetDC();
703 dc
.SetFont(GetFont());
705 // get the update rect
706 wxRect rectUpdate
= GetUpdateClientRect();
709 CalcUnscrolledPosition(0, rectUpdate
.GetTop(), NULL
, &yTop
);
710 CalcUnscrolledPosition(0, rectUpdate
.GetBottom(), NULL
, &yBottom
);
712 // get the items which must be redrawn
713 wxCoord lineHeight
= GetLineHeight();
714 size_t itemFirst
= yTop
/ lineHeight
,
715 itemLast
= (yBottom
+ lineHeight
- 1) / lineHeight
,
716 itemMax
= m_strings
->GetCount();
718 if ( itemFirst
>= itemMax
)
721 if ( itemLast
> itemMax
)
725 wxLogTrace(_T("listbox"), _T("Repainting items %d..%d"),
726 itemFirst
, itemLast
);
728 DoDrawRange(renderer
, itemFirst
, itemLast
);
731 void wxListBox::DoDrawRange(wxControlRenderer
*renderer
,
732 int itemFirst
, int itemLast
)
734 renderer
->DrawItems(this, itemFirst
, itemLast
);
737 // ----------------------------------------------------------------------------
739 // ----------------------------------------------------------------------------
741 bool wxListBox::SetFont(const wxFont
& font
)
743 if ( !wxControl::SetFont(font
) )
753 void wxListBox::CalcItemsPerPage()
755 m_lineHeight
= GetRenderer()->GetListboxItemHeight(GetCharHeight());
756 m_itemsPerPage
= GetClientSize().y
/ m_lineHeight
;
759 int wxListBox::GetItemsPerPage() const
761 if ( !m_itemsPerPage
)
763 wxConstCast(this, wxListBox
)->CalcItemsPerPage();
766 return m_itemsPerPage
;
769 wxCoord
wxListBox::GetLineHeight() const
773 wxConstCast(this, wxListBox
)->CalcItemsPerPage();
779 wxCoord
wxListBox::GetMaxWidth() const
781 if ( m_maxWidth
== 0 )
783 wxListBox
*self
= wxConstCast(this, wxListBox
);
785 size_t count
= m_strings
->GetCount();
786 for ( size_t n
= 0; n
< count
; n
++ )
788 GetTextExtent((*m_strings
)[n
], &width
, NULL
);
789 if ( width
> m_maxWidth
)
791 self
->m_maxWidth
= width
;
792 self
->m_maxWidthItem
= n
;
800 void wxListBox::OnSize(wxSizeEvent
& event
)
802 // recalculate the number of items per page
805 // the scrollbars might [dis]appear
807 m_updateScrollbarY
= true;
812 void wxListBox::DoSetFirstItem(int n
)
817 void wxListBox::DoSetSize(int x
, int y
,
818 int width
, int height
,
821 if ( GetWindowStyle() & wxLB_INT_HEIGHT
)
823 // we must round up the height to an entire number of rows
825 // the client area must contain an int number of rows, so take borders
827 wxRect rectBorders
= GetRenderer()->GetBorderDimensions(GetBorder());
828 wxCoord hBorders
= rectBorders
.y
+ rectBorders
.height
;
830 wxCoord hLine
= GetLineHeight();
831 height
= ((height
- hBorders
+ hLine
- 1) / hLine
)*hLine
+ hBorders
;
834 wxListBoxBase::DoSetSize(x
, y
, width
, height
, sizeFlags
);
837 wxSize
wxListBox::DoGetBestClientSize() const
842 size_t count
= m_strings
->GetCount();
843 for ( size_t n
= 0; n
< count
; n
++ )
846 GetTextExtent((*m_strings
)[n
], &w
, &h
);
854 // if the listbox is empty, still give it some non zero (even if
855 // arbitrary) size - otherwise, leave small margin around the strings
859 width
+= 3*GetCharWidth();
862 height
= GetCharHeight();
864 // we need the height of the entire listbox, not just of one line
865 height
*= wxMax(count
, 7);
867 return wxSize(width
, height
);
870 // ----------------------------------------------------------------------------
872 // ----------------------------------------------------------------------------
874 bool wxListBox::SendEvent(wxEventType type
, int item
)
876 wxCommandEvent
event(type
, m_windowId
);
877 event
.SetEventObject(this);
879 // use the current item by default
885 // client data and string parameters only make sense if we have an item
888 if ( HasClientObjectData() )
889 event
.SetClientObject(GetClientObject(item
));
890 else if ( HasClientUntypedData() )
891 event
.SetClientData(GetClientData(item
));
893 event
.SetString(GetString(item
));
896 event
.m_commandInt
= item
;
898 return GetEventHandler()->ProcessEvent(event
);
901 void wxListBox::SetCurrentItem(int n
)
903 if ( n
!= m_current
)
905 if ( m_current
!= -1 )
906 RefreshItem(m_current
);
910 if ( m_current
!= -1 )
912 m_currentChanged
= true;
914 RefreshItem(m_current
);
917 //else: nothing to do
920 bool wxListBox::FindItem(const wxString
& prefix
, bool strictlyAfter
)
922 int count
= GetCount();
925 // empty listbox, we can't find anything in it
929 // start either from the current item or from the next one if strictlyAfter
934 // the following line will set first correctly to 0 if there is no
935 // selection (m_current == -1)
936 first
= m_current
== count
- 1 ? 0 : m_current
+ 1;
938 else // start with the current
940 first
= m_current
== -1 ? 0 : m_current
;
943 int last
= first
== 0 ? count
- 1 : first
- 1;
945 // if this is not true we'd never exit from the loop below!
946 wxASSERT_MSG( first
< count
&& last
< count
, _T("logic error") );
948 // precompute it outside the loop
949 size_t len
= prefix
.length();
951 // loop over all items in the listbox
952 for ( int item
= first
; item
!= last
; item
< count
- 1 ? item
++ : item
= 0 )
954 if ( wxStrnicmp((*m_strings
)[item
], prefix
, len
) == 0 )
956 SetCurrentItem(item
);
958 if ( !(GetWindowStyle() & wxLB_MULTIPLE
) )
961 SelectAndNotify(item
);
963 if ( GetWindowStyle() & wxLB_EXTENDED
)
964 AnchorSelection(item
);
975 void wxListBox::EnsureVisible(int n
)
977 if ( m_updateScrollbarY
)
982 m_updateScrollbarY
= false;
988 void wxListBox::DoEnsureVisible(int n
)
990 if ( !m_showScrollbarY
)
992 // nothing to do - everything is shown anyhow
997 GetViewStart(0, &first
);
1000 // we need to scroll upwards, so make the current item appear on top
1001 // of the shown range
1006 int last
= first
+ GetClientSize().y
/ GetLineHeight() - 1;
1009 // scroll down: the current item appears at the bottom of the
1011 Scroll(0, n
- (last
- first
));
1016 void wxListBox::ChangeCurrent(int diff
)
1018 int current
= m_current
== -1 ? 0 : m_current
;
1022 int last
= GetCount() - 1;
1025 else if ( current
> last
)
1028 SetCurrentItem(current
);
1031 void wxListBox::ExtendSelection(int itemTo
)
1033 // if we don't have the explicit values for selection start/end, make them
1035 if ( m_selAnchor
== -1 )
1036 m_selAnchor
= m_current
;
1041 // swap the start/end of selection range if necessary
1042 int itemFrom
= m_selAnchor
;
1043 if ( itemFrom
> itemTo
)
1045 int itemTmp
= itemFrom
;
1050 // the selection should now include all items in the range between the
1051 // anchor and the specified item and only them
1054 for ( n
= 0; n
< itemFrom
; n
++ )
1059 for ( ; n
<= itemTo
; n
++ )
1064 int count
= GetCount();
1065 for ( ; n
< count
; n
++ )
1071 void wxListBox::DoSelect(int item
, bool sel
)
1075 // go to this item first
1076 SetCurrentItem(item
);
1079 // the current item is the one we want to change: either it was just
1080 // changed above to be the same as item or item == -1 in which we case we
1081 // are supposed to use the current one anyhow
1082 if ( m_current
!= -1 )
1085 SetSelection(m_current
, sel
);
1089 void wxListBox::SelectAndNotify(int item
)
1093 SendEvent(wxEVT_COMMAND_LISTBOX_SELECTED
);
1096 void wxListBox::Activate(int item
)
1099 SetCurrentItem(item
);
1103 if ( !(GetWindowStyle() & wxLB_MULTIPLE
) )
1112 SendEvent(wxEVT_COMMAND_LISTBOX_DOUBLECLICKED
);
1116 // ----------------------------------------------------------------------------
1118 // ----------------------------------------------------------------------------
1121 The numArg here is the listbox item index while the strArg is used
1122 differently for the different actions:
1124 a) for wxACTION_LISTBOX_FIND it has the natural meaning: this is the string
1127 b) for wxACTION_LISTBOX_SELECT and wxACTION_LISTBOX_EXTENDSEL it is used
1128 to decide if the listbox should send the notification event (it is empty)
1129 or not (it is not): this allows us to reuse the same action for when the
1130 user is dragging the mouse when it has been released although in the
1131 first case no notification is sent while in the second it is sent.
1133 bool wxListBox::PerformAction(const wxControlAction
& action
,
1135 const wxString
& strArg
)
1137 int item
= (int)numArg
;
1139 if ( action
== wxACTION_LISTBOX_SETFOCUS
)
1141 SetCurrentItem(item
);
1143 else if ( action
== wxACTION_LISTBOX_ACTIVATE
)
1147 else if ( action
== wxACTION_LISTBOX_TOGGLE
)
1152 if ( IsSelected(item
) )
1155 SelectAndNotify(item
);
1157 else if ( action
== wxACTION_LISTBOX_SELECT
)
1161 if ( strArg
.empty() )
1162 SelectAndNotify(item
);
1166 else if ( action
== wxACTION_LISTBOX_SELECTADD
)
1168 else if ( action
== wxACTION_LISTBOX_UNSELECT
)
1170 else if ( action
== wxACTION_LISTBOX_MOVEDOWN
)
1172 else if ( action
== wxACTION_LISTBOX_MOVEUP
)
1174 else if ( action
== wxACTION_LISTBOX_PAGEDOWN
)
1175 ChangeCurrent(GetItemsPerPage());
1176 else if ( action
== wxACTION_LISTBOX_PAGEUP
)
1177 ChangeCurrent(-GetItemsPerPage());
1178 else if ( action
== wxACTION_LISTBOX_START
)
1180 else if ( action
== wxACTION_LISTBOX_END
)
1181 SetCurrentItem(GetCount() - 1);
1182 else if ( action
== wxACTION_LISTBOX_UNSELECTALL
)
1184 else if ( action
== wxACTION_LISTBOX_EXTENDSEL
)
1185 ExtendSelection(item
);
1186 else if ( action
== wxACTION_LISTBOX_FIND
)
1187 FindNextItem(strArg
);
1188 else if ( action
== wxACTION_LISTBOX_ANCHOR
)
1189 AnchorSelection(item
== -1 ? m_current
: item
);
1190 else if ( action
== wxACTION_LISTBOX_SELECTALL
||
1191 action
== wxACTION_LISTBOX_SELTOGGLE
)
1192 wxFAIL_MSG(_T("unimplemented yet"));
1194 return wxControl::PerformAction(action
, numArg
, strArg
);
1199 // ============================================================================
1200 // implementation of wxStdListboxInputHandler
1201 // ============================================================================
1203 wxStdListboxInputHandler::wxStdListboxInputHandler(wxInputHandler
*handler
,
1204 bool toggleOnPressAlways
)
1205 : wxStdInputHandler(handler
)
1208 m_toggleOnPressAlways
= toggleOnPressAlways
;
1209 m_actionMouse
= wxACTION_NONE
;
1210 m_trackMouseOutside
= true;
1213 int wxStdListboxInputHandler::HitTest(const wxListBox
*lbox
,
1214 const wxMouseEvent
& event
)
1216 int item
= HitTestUnsafe(lbox
, event
);
1218 return FixItemIndex(lbox
, item
);
1221 int wxStdListboxInputHandler::HitTestUnsafe(const wxListBox
*lbox
,
1222 const wxMouseEvent
& event
)
1224 wxPoint pt
= event
.GetPosition();
1225 pt
-= lbox
->GetClientAreaOrigin();
1227 lbox
->CalcUnscrolledPosition(0, pt
.y
, NULL
, &y
);
1228 return y
/ lbox
->GetLineHeight();
1231 int wxStdListboxInputHandler::FixItemIndex(const wxListBox
*lbox
,
1236 // mouse is above the first item
1239 else if ( item
>= lbox
->GetCount() )
1241 // mouse is below the last item
1242 item
= lbox
->GetCount() - 1;
1248 bool wxStdListboxInputHandler::IsValidIndex(const wxListBox
*lbox
, int item
)
1250 return item
>= 0 && item
< lbox
->GetCount();
1254 wxStdListboxInputHandler::SetupCapture(wxListBox
*lbox
,
1255 const wxMouseEvent
& event
,
1258 // we currently only allow selecting with the left mouse button, if we
1259 // do need to allow using other buttons too we might use the code
1262 m_btnCapture
= event
.LeftDown()
1271 wxControlAction action
;
1272 if ( lbox
->HasMultipleSelection() )
1274 if ( lbox
->GetWindowStyle() & wxLB_MULTIPLE
)
1276 if ( m_toggleOnPressAlways
)
1278 // toggle the item right now
1279 action
= wxACTION_LISTBOX_TOGGLE
;
1283 m_actionMouse
= wxACTION_LISTBOX_SETFOCUS
;
1285 else // wxLB_EXTENDED listbox
1287 // simple click in an extended sel listbox clears the old
1288 // selection and adds the clicked item to it then, ctrl-click
1289 // toggles an item to it and shift-click adds a range between
1290 // the old selection anchor and the clicked item
1291 if ( event
.ControlDown() )
1293 lbox
->PerformAction(wxACTION_LISTBOX_ANCHOR
, item
);
1295 action
= wxACTION_LISTBOX_TOGGLE
;
1297 else if ( event
.ShiftDown() )
1299 action
= wxACTION_LISTBOX_EXTENDSEL
;
1301 else // simple click
1303 lbox
->PerformAction(wxACTION_LISTBOX_ANCHOR
, item
);
1305 action
= wxACTION_LISTBOX_SELECT
;
1308 m_actionMouse
= wxACTION_LISTBOX_EXTENDSEL
;
1311 else // single selection
1314 action
= wxACTION_LISTBOX_SELECT
;
1317 // by default we always do track it
1318 m_trackMouseOutside
= true;
1323 bool wxStdListboxInputHandler::HandleKey(wxInputConsumer
*consumer
,
1324 const wxKeyEvent
& event
,
1327 // we're only interested in the key press events
1328 if ( pressed
&& !event
.AltDown() )
1330 bool isMoveCmd
= true;
1331 int style
= consumer
->GetInputWindow()->GetWindowStyle();
1333 wxControlAction action
;
1336 int keycode
= event
.GetKeyCode();
1341 action
= wxACTION_LISTBOX_MOVEUP
;
1345 action
= wxACTION_LISTBOX_MOVEDOWN
;
1351 action
= wxACTION_LISTBOX_PAGEUP
;
1357 action
= wxACTION_LISTBOX_PAGEDOWN
;
1361 action
= wxACTION_LISTBOX_START
;
1365 action
= wxACTION_LISTBOX_END
;
1370 if ( style
& wxLB_MULTIPLE
)
1372 action
= wxACTION_LISTBOX_TOGGLE
;
1378 action
= wxACTION_LISTBOX_ACTIVATE
;
1383 if ( (keycode
< 255) && wxIsalnum((wxChar
)keycode
) )
1385 action
= wxACTION_LISTBOX_FIND
;
1386 strArg
= (wxChar
)keycode
;
1390 if ( !action
.IsEmpty() )
1392 consumer
->PerformAction(action
, -1, strArg
);
1396 if ( style
& wxLB_SINGLE
)
1398 // the current item is always the one selected
1399 consumer
->PerformAction(wxACTION_LISTBOX_SELECT
);
1401 else if ( style
& wxLB_EXTENDED
)
1403 if ( event
.ShiftDown() )
1404 consumer
->PerformAction(wxACTION_LISTBOX_EXTENDSEL
);
1407 // select the item and make it the new selection anchor
1408 consumer
->PerformAction(wxACTION_LISTBOX_SELECT
);
1409 consumer
->PerformAction(wxACTION_LISTBOX_ANCHOR
);
1412 //else: nothing to do for multiple selection listboxes
1419 return wxStdInputHandler::HandleKey(consumer
, event
, pressed
);
1422 bool wxStdListboxInputHandler::HandleMouse(wxInputConsumer
*consumer
,
1423 const wxMouseEvent
& event
)
1425 wxListBox
*lbox
= wxStaticCast(consumer
->GetInputWindow(), wxListBox
);
1426 int item
= HitTest(lbox
, event
);
1427 wxControlAction action
;
1429 // when the left mouse button is pressed, capture the mouse and track the
1430 // item under mouse (if the mouse leaves the window, we will still be
1431 // getting the mouse move messages generated by wxScrollWindow)
1432 if ( event
.LeftDown() )
1434 // capture the mouse to track the selected item
1435 lbox
->CaptureMouse();
1437 action
= SetupCapture(lbox
, event
, item
);
1439 else if ( m_btnCapture
&& event
.ButtonUp(m_btnCapture
) )
1441 // when the left mouse button is released, release the mouse too
1442 wxWindow
*winCapture
= wxWindow::GetCapture();
1445 winCapture
->ReleaseMouse();
1448 action
= m_actionMouse
;
1450 //else: the mouse wasn't presed over the listbox, only released here
1452 else if ( event
.LeftDClick() )
1454 action
= wxACTION_LISTBOX_ACTIVATE
;
1457 if ( !action
.IsEmpty() )
1459 lbox
->PerformAction(action
, item
);
1464 return wxStdInputHandler::HandleMouse(consumer
, event
);
1467 bool wxStdListboxInputHandler::HandleMouseMove(wxInputConsumer
*consumer
,
1468 const wxMouseEvent
& event
)
1470 wxWindow
*winCapture
= wxWindow::GetCapture();
1471 if ( winCapture
&& (event
.GetEventObject() == winCapture
) )
1473 wxListBox
*lbox
= wxStaticCast(consumer
->GetInputWindow(), wxListBox
);
1475 if ( !m_btnCapture
|| !m_trackMouseOutside
)
1477 // someone captured the mouse for us (we always set m_btnCapture
1478 // when we do it ourselves): in this case we only react to
1479 // the mouse messages when they happen inside the listbox
1480 if ( lbox
->HitTest(event
.GetPosition()) != wxHT_WINDOW_INSIDE
)
1484 int item
= HitTest(lbox
, event
);
1485 if ( !m_btnCapture
)
1487 // now that we have the mouse inside the listbox, do capture it
1488 // normally - but ensure that we will still ignore the outside
1490 SetupCapture(lbox
, event
, item
);
1492 m_trackMouseOutside
= false;
1495 if ( IsValidIndex(lbox
, item
) )
1497 // pass something into strArg to tell the listbox that it shouldn't
1498 // send the notification message: see PerformAction() above
1499 lbox
->PerformAction(m_actionMouse
, item
, _T("no"));
1501 // else: don't pass invalid index to the listbox
1503 else // we don't have capture any more
1507 // if we lost capture unexpectedly (someone else took the capture
1508 // from us), return to a consistent state
1513 return wxStdInputHandler::HandleMouseMove(consumer
, event
);
1516 #endif // wxUSE_LISTBOX