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 m_strings
= new wxArrayString
;
150 CreateInputHandler(wxINP_HANDLER_LISTBOX
);
155 wxListBox::~wxListBox()
157 // call this just to free the client data -- and avoid leaking memory
165 // ----------------------------------------------------------------------------
166 // adding/inserting strings
167 // ----------------------------------------------------------------------------
169 int wxCMPFUNC_CONV
wxListBoxSortNoCase(wxString
* s1
, wxString
* s2
)
171 return s1
->CmpNoCase(*s2
);
174 int wxListBox::DoAppendOnly(const wxString
& item
)
180 m_strings
->Add(item
);
181 m_strings
->Sort(wxListBoxSortNoCase
);
182 index
= m_strings
->Index(item
);
186 index
= m_strings
->GetCount();
187 m_strings
->Add(item
);
193 int wxListBox::DoAppend(const wxString
& item
)
195 size_t index
= DoAppendOnly( item
);
197 m_itemsClientData
.Insert(NULL
, index
);
199 m_updateScrollbarY
= true;
201 if ( HasHorzScrollbar() )
203 // has the max width increased?
205 GetTextExtent(item
, &width
, NULL
);
206 if ( width
> m_maxWidth
)
209 m_maxWidthItem
= index
;
210 m_updateScrollbarX
= true;
214 RefreshFromItemToEnd(index
);
219 void wxListBox::DoInsertItems(const wxArrayString
& items
, int pos
)
221 // the position of the item being added to a sorted listbox can't be
223 wxCHECK_RET( !IsSorted(), _T("can't insert items into sorted listbox") );
225 size_t count
= items
.GetCount();
226 for ( size_t n
= 0; n
< count
; n
++ )
228 m_strings
->Insert(items
[n
], pos
+ n
);
229 m_itemsClientData
.Insert(NULL
, pos
+ n
);
232 // the number of items has changed so we might have to show the scrollbar
233 m_updateScrollbarY
= true;
235 // the max width also might have changed - just recalculate it instead of
236 // keeping track of it here, this is probably more efficient for a typical
238 RefreshHorzScrollbar();
240 // note that we have to refresh all the items after the ones we inserted,
241 // not just these items
242 RefreshFromItemToEnd(pos
);
245 void wxListBox::DoSetItems(const wxArrayString
& items
, void **clientData
)
249 size_t count
= items
.GetCount();
253 m_strings
->Alloc(count
);
255 m_itemsClientData
.Alloc(count
);
256 for ( size_t n
= 0; n
< count
; n
++ )
258 size_t index
= DoAppendOnly(items
[n
]);
260 m_itemsClientData
.Insert(clientData
? clientData
[n
] : NULL
, index
);
263 m_updateScrollbarY
= true;
268 void wxListBox::SetString(int n
, const wxString
& s
)
270 wxCHECK_RET( !IsSorted(), _T("can't set string in sorted listbox") );
274 if ( HasHorzScrollbar() )
276 // we need to update m_maxWidth as changing the string may cause the
277 // horz scrollbar [dis]appear
280 GetTextExtent(s
, &width
, NULL
);
282 // it might have increased if the new string is long
283 if ( width
> m_maxWidth
)
287 m_updateScrollbarX
= true;
289 // or also decreased if the old string was the longest one
290 else if ( n
== m_maxWidthItem
)
292 RefreshHorzScrollbar();
299 // ----------------------------------------------------------------------------
301 // ----------------------------------------------------------------------------
303 void wxListBox::DoClear()
307 if ( HasClientObjectData() )
309 size_t count
= m_itemsClientData
.GetCount();
310 for ( size_t n
= 0; n
< count
; n
++ )
312 delete (wxClientData
*) m_itemsClientData
[n
];
316 m_itemsClientData
.Clear();
317 m_selections
.Clear();
322 void wxListBox::Clear()
326 m_updateScrollbarY
= true;
328 RefreshHorzScrollbar();
333 void wxListBox::Delete(int n
)
335 wxCHECK_RET( n
>= 0 && n
< GetCount(),
336 _T("invalid index in wxListBox::Delete") );
338 // do it before removing the index as otherwise the last item will not be
339 // refreshed (as GetCount() will be decremented)
340 RefreshFromItemToEnd(n
);
342 m_strings
->RemoveAt(n
);
344 if ( HasClientObjectData() )
346 delete (wxClientData
*)m_itemsClientData
[n
];
349 m_itemsClientData
.RemoveAt(n
);
351 // when the item disappears we must not keep using its index
352 if ( n
== m_current
)
356 else if ( n
< m_current
)
360 //else: current item may stay
362 // update the selections array: the indices of all seletected items after
363 // the one being deleted must change and the item itselfm ust be removed
364 int index
= wxNOT_FOUND
;
365 size_t count
= m_selections
.GetCount();
366 for ( size_t item
= 0; item
< count
; item
++ )
368 if ( m_selections
[item
] == n
)
370 // remember to delete it later
373 else if ( m_selections
[item
] > n
)
375 // to account for the index shift
376 m_selections
[item
]--;
378 //else: nothing changed for this one
381 if ( index
!= wxNOT_FOUND
)
383 m_selections
.RemoveAt(index
);
386 // the number of items has changed, hence the scrollbar may disappear
387 m_updateScrollbarY
= true;
389 // finally, if the longest item was deleted the scrollbar may disappear
390 if ( n
== m_maxWidthItem
)
392 RefreshHorzScrollbar();
396 // ----------------------------------------------------------------------------
397 // client data handling
398 // ----------------------------------------------------------------------------
400 void wxListBox::DoSetItemClientData(int n
, void* clientData
)
402 m_itemsClientData
[n
] = clientData
;
405 void *wxListBox::DoGetItemClientData(int n
) const
407 return m_itemsClientData
[n
];
410 void wxListBox::DoSetItemClientObject(int n
, wxClientData
* clientData
)
412 m_itemsClientData
[n
] = clientData
;
415 wxClientData
* wxListBox::DoGetItemClientObject(int n
) const
417 return (wxClientData
*)m_itemsClientData
[n
];
420 // ----------------------------------------------------------------------------
422 // ----------------------------------------------------------------------------
424 void wxListBox::DoSetSelection(int n
, bool select
)
428 if ( m_selections
.Index(n
) == wxNOT_FOUND
)
430 if ( !HasMultipleSelection() )
432 // selecting an item in a single selection listbox deselects
441 //else: already selected
445 int index
= m_selections
.Index(n
);
446 if ( index
!= wxNOT_FOUND
)
448 m_selections
.RemoveAt(index
);
455 // sanity check: a single selection listbox can't have more than one item
457 wxASSERT_MSG( HasMultipleSelection() || (m_selections
.GetCount() < 2),
458 _T("multiple selected items in single selection lbox?") );
462 // the newly selected item becomes the current one
467 int wxListBox::GetSelection() const
469 wxCHECK_MSG( !HasMultipleSelection(), -1,
470 _T("use wxListBox::GetSelections for ths listbox") );
472 return m_selections
.IsEmpty() ? -1 : m_selections
[0];
475 int wxCMPFUNC_CONV
wxCompareInts(int *n
, int *m
)
480 int wxListBox::GetSelections(wxArrayInt
& selections
) const
482 // always return sorted array to the user
483 selections
= m_selections
;
484 size_t count
= m_selections
.GetCount();
486 // don't call sort on an empty array
489 selections
.Sort(wxCompareInts
);
495 // ----------------------------------------------------------------------------
496 // refresh logic: we use delayed refreshing which allows to avoid multiple
497 // refreshes (and hence flicker) in case when several listbox items are
498 // added/deleted/changed subsequently
499 // ----------------------------------------------------------------------------
501 void wxListBox::RefreshFromItemToEnd(int from
)
503 RefreshItems(from
, GetCount() - from
);
506 void wxListBox::RefreshItems(int from
, int count
)
508 switch ( m_updateCount
)
512 m_updateCount
= count
;
516 // we refresh everything anyhow
520 // add these items to the others which we have to refresh
521 if ( m_updateFrom
< from
)
523 count
+= from
- m_updateFrom
;
524 if ( m_updateCount
< count
)
525 m_updateCount
= count
;
527 else // m_updateFrom >= from
529 int updateLast
= wxMax(m_updateFrom
+ m_updateCount
,
532 m_updateCount
= updateLast
- m_updateFrom
;
537 void wxListBox::RefreshItem(int n
)
539 switch ( m_updateCount
)
542 // refresh this item only
548 // we refresh everything anyhow
552 // add this item to the others which we have to refresh
553 if ( m_updateFrom
< n
)
555 if ( m_updateCount
< n
- m_updateFrom
+ 1 )
556 m_updateCount
= n
- m_updateFrom
+ 1;
558 else // n <= m_updateFrom
560 m_updateCount
+= m_updateFrom
- n
;
566 void wxListBox::RefreshAll()
571 void wxListBox::RefreshHorzScrollbar()
573 m_maxWidth
= 0; // recalculate it
574 m_updateScrollbarX
= true;
577 void wxListBox::UpdateScrollbars()
579 wxSize size
= GetClientSize();
581 // is our height enough to show all items?
582 int nLines
= GetCount();
583 wxCoord lineHeight
= GetLineHeight();
584 bool showScrollbarY
= nLines
*lineHeight
> size
.y
;
586 // check the width too if required
587 wxCoord charWidth
, maxWidth
;
589 if ( HasHorzScrollbar() )
591 charWidth
= GetCharWidth();
592 maxWidth
= GetMaxWidth();
593 showScrollbarX
= maxWidth
> size
.x
;
595 else // never show it
597 charWidth
= maxWidth
= 0;
598 showScrollbarX
= false;
601 // what should be the scrollbar range now?
602 int scrollRangeX
= showScrollbarX
603 ? (maxWidth
+ charWidth
- 1) / charWidth
+ 2 // FIXME
605 int scrollRangeY
= showScrollbarY
607 (size
.y
% lineHeight
+ lineHeight
- 1) / lineHeight
610 // reset scrollbars if something changed: either the visibility status
611 // or the range of a scrollbar which is shown
612 if ( (showScrollbarY
!= m_showScrollbarY
) ||
613 (showScrollbarX
!= m_showScrollbarX
) ||
614 (showScrollbarY
&& (scrollRangeY
!= m_scrollRangeY
)) ||
615 (showScrollbarX
&& (scrollRangeX
!= m_scrollRangeX
)) )
618 GetViewStart(&x
, &y
);
619 SetScrollbars(charWidth
, lineHeight
,
620 scrollRangeX
, scrollRangeY
,
623 m_showScrollbarX
= showScrollbarX
;
624 m_showScrollbarY
= showScrollbarY
;
626 m_scrollRangeX
= scrollRangeX
;
627 m_scrollRangeY
= scrollRangeY
;
631 void wxListBox::UpdateItems()
633 // only refresh the items which must be refreshed
634 if ( m_updateCount
== -1 )
637 wxLogTrace(_T("listbox"), _T("Refreshing all"));
643 wxSize size
= GetClientSize();
646 rect
.height
= size
.y
;
647 rect
.y
+= m_updateFrom
*GetLineHeight();
648 rect
.height
= m_updateCount
*GetLineHeight();
650 // we don't need to calculate x position as we always refresh the
652 CalcScrolledPosition(0, rect
.y
, NULL
, &rect
.y
);
654 wxLogTrace(_T("listbox"), _T("Refreshing items %d..%d (%d-%d)"),
655 m_updateFrom
, m_updateFrom
+ m_updateCount
- 1,
656 rect
.GetTop(), rect
.GetBottom());
658 Refresh(true, &rect
);
662 void wxListBox::OnInternalIdle()
664 if ( m_updateScrollbarY
|| m_updateScrollbarX
)
669 m_updateScrollbarY
= false;
672 if ( m_currentChanged
)
674 DoEnsureVisible(m_current
);
676 m_currentChanged
= false;
685 wxListBoxBase::OnInternalIdle();
688 // ----------------------------------------------------------------------------
690 // ----------------------------------------------------------------------------
692 wxBorder
wxListBox::GetDefaultBorder() const
694 return wxBORDER_SUNKEN
;
697 void wxListBox::DoDraw(wxControlRenderer
*renderer
)
699 // adjust the DC to account for scrolling
700 wxDC
& dc
= renderer
->GetDC();
702 dc
.SetFont(GetFont());
704 // get the update rect
705 wxRect rectUpdate
= GetUpdateClientRect();
708 CalcUnscrolledPosition(0, rectUpdate
.GetTop(), NULL
, &yTop
);
709 CalcUnscrolledPosition(0, rectUpdate
.GetBottom(), NULL
, &yBottom
);
711 // get the items which must be redrawn
712 wxCoord lineHeight
= GetLineHeight();
713 size_t itemFirst
= yTop
/ lineHeight
,
714 itemLast
= (yBottom
+ lineHeight
- 1) / lineHeight
,
715 itemMax
= m_strings
->GetCount();
717 if ( itemFirst
>= itemMax
)
720 if ( itemLast
> itemMax
)
724 wxLogTrace(_T("listbox"), _T("Repainting items %d..%d"),
725 itemFirst
, itemLast
);
727 DoDrawRange(renderer
, itemFirst
, itemLast
);
730 void wxListBox::DoDrawRange(wxControlRenderer
*renderer
,
731 int itemFirst
, int itemLast
)
733 renderer
->DrawItems(this, itemFirst
, itemLast
);
736 // ----------------------------------------------------------------------------
738 // ----------------------------------------------------------------------------
740 bool wxListBox::SetFont(const wxFont
& font
)
742 if ( !wxControl::SetFont(font
) )
752 void wxListBox::CalcItemsPerPage()
754 m_lineHeight
= GetRenderer()->GetListboxItemHeight(GetCharHeight());
755 m_itemsPerPage
= GetClientSize().y
/ m_lineHeight
;
758 int wxListBox::GetItemsPerPage() const
760 if ( !m_itemsPerPage
)
762 wxConstCast(this, wxListBox
)->CalcItemsPerPage();
765 return m_itemsPerPage
;
768 wxCoord
wxListBox::GetLineHeight() const
772 wxConstCast(this, wxListBox
)->CalcItemsPerPage();
778 wxCoord
wxListBox::GetMaxWidth() const
780 if ( m_maxWidth
== 0 )
782 wxListBox
*self
= wxConstCast(this, wxListBox
);
784 size_t count
= m_strings
->GetCount();
785 for ( size_t n
= 0; n
< count
; n
++ )
787 GetTextExtent(this->GetString(n
), &width
, NULL
);
788 if ( width
> m_maxWidth
)
790 self
->m_maxWidth
= width
;
791 self
->m_maxWidthItem
= n
;
799 void wxListBox::OnSize(wxSizeEvent
& event
)
801 // recalculate the number of items per page
804 // the scrollbars might [dis]appear
806 m_updateScrollbarY
= true;
811 void wxListBox::DoSetFirstItem(int n
)
816 void wxListBox::DoSetSize(int x
, int y
,
817 int width
, int height
,
820 if ( GetWindowStyle() & wxLB_INT_HEIGHT
)
822 // we must round up the height to an entire number of rows
824 // the client area must contain an int number of rows, so take borders
826 wxRect rectBorders
= GetRenderer()->GetBorderDimensions(GetBorder());
827 wxCoord hBorders
= rectBorders
.y
+ rectBorders
.height
;
829 wxCoord hLine
= GetLineHeight();
830 height
= ((height
- hBorders
+ hLine
- 1) / hLine
)*hLine
+ hBorders
;
833 wxListBoxBase::DoSetSize(x
, y
, width
, height
, sizeFlags
);
836 wxSize
wxListBox::DoGetBestClientSize() const
841 size_t count
= m_strings
->GetCount();
842 for ( size_t n
= 0; n
< count
; n
++ )
845 GetTextExtent(this->GetString(n
), &w
, &h
);
853 // if the listbox is empty, still give it some non zero (even if
854 // arbitrary) size - otherwise, leave small margin around the strings
858 width
+= 3*GetCharWidth();
861 height
= GetCharHeight();
863 // we need the height of the entire listbox, not just of one line
864 height
*= wxMax(count
, 7);
866 return wxSize(width
, height
);
869 // ----------------------------------------------------------------------------
871 // ----------------------------------------------------------------------------
873 bool wxListBox::SendEvent(wxEventType type
, int item
)
875 wxCommandEvent
event(type
, m_windowId
);
876 event
.SetEventObject(this);
878 // use the current item by default
884 // client data and string parameters only make sense if we have an item
887 if ( HasClientObjectData() )
888 event
.SetClientObject(GetClientObject(item
));
889 else if ( HasClientUntypedData() )
890 event
.SetClientData(GetClientData(item
));
892 event
.SetString(GetString(item
));
897 return GetEventHandler()->ProcessEvent(event
);
900 void wxListBox::SetCurrentItem(int n
)
902 if ( n
!= m_current
)
904 if ( m_current
!= -1 )
905 RefreshItem(m_current
);
909 if ( m_current
!= -1 )
911 m_currentChanged
= true;
913 RefreshItem(m_current
);
916 //else: nothing to do
919 bool wxListBox::FindItem(const wxString
& prefix
, bool strictlyAfter
)
921 int count
= GetCount();
924 // empty listbox, we can't find anything in it
928 // start either from the current item or from the next one if strictlyAfter
933 // the following line will set first correctly to 0 if there is no
934 // selection (m_current == -1)
935 first
= m_current
== count
- 1 ? 0 : m_current
+ 1;
937 else // start with the current
939 first
= m_current
== -1 ? 0 : m_current
;
942 int last
= first
== 0 ? count
- 1 : first
- 1;
944 // if this is not true we'd never exit from the loop below!
945 wxASSERT_MSG( first
< count
&& last
< count
, _T("logic error") );
947 // precompute it outside the loop
948 size_t len
= prefix
.length();
950 // loop over all items in the listbox
951 for ( int item
= first
; item
!= last
; item
< count
- 1 ? item
++ : item
= 0 )
953 if ( wxStrnicmp(this->GetString(item
).c_str(), prefix
, len
) == 0 )
955 SetCurrentItem(item
);
957 if ( !(GetWindowStyle() & wxLB_MULTIPLE
) )
960 SelectAndNotify(item
);
962 if ( GetWindowStyle() & wxLB_EXTENDED
)
963 AnchorSelection(item
);
974 void wxListBox::EnsureVisible(int n
)
976 if ( m_updateScrollbarY
)
981 m_updateScrollbarY
= false;
987 void wxListBox::DoEnsureVisible(int n
)
989 if ( !m_showScrollbarY
)
991 // nothing to do - everything is shown anyhow
996 GetViewStart(0, &first
);
999 // we need to scroll upwards, so make the current item appear on top
1000 // of the shown range
1005 int last
= first
+ GetClientSize().y
/ GetLineHeight() - 1;
1008 // scroll down: the current item appears at the bottom of the
1010 Scroll(0, n
- (last
- first
));
1015 void wxListBox::ChangeCurrent(int diff
)
1017 int current
= m_current
== -1 ? 0 : m_current
;
1021 int last
= GetCount() - 1;
1024 else if ( current
> last
)
1027 SetCurrentItem(current
);
1030 void wxListBox::ExtendSelection(int itemTo
)
1032 // if we don't have the explicit values for selection start/end, make them
1034 if ( m_selAnchor
== -1 )
1035 m_selAnchor
= m_current
;
1040 // swap the start/end of selection range if necessary
1041 int itemFrom
= m_selAnchor
;
1042 if ( itemFrom
> itemTo
)
1044 int itemTmp
= itemFrom
;
1049 // the selection should now include all items in the range between the
1050 // anchor and the specified item and only them
1053 for ( n
= 0; n
< itemFrom
; n
++ )
1058 for ( ; n
<= itemTo
; n
++ )
1063 int count
= GetCount();
1064 for ( ; n
< count
; n
++ )
1070 void wxListBox::DoSelect(int item
, bool sel
)
1074 // go to this item first
1075 SetCurrentItem(item
);
1078 // the current item is the one we want to change: either it was just
1079 // changed above to be the same as item or item == -1 in which we case we
1080 // are supposed to use the current one anyhow
1081 if ( m_current
!= -1 )
1084 SetSelection(m_current
, sel
);
1088 void wxListBox::SelectAndNotify(int item
)
1092 SendEvent(wxEVT_COMMAND_LISTBOX_SELECTED
);
1095 void wxListBox::Activate(int item
)
1098 SetCurrentItem(item
);
1102 if ( !(GetWindowStyle() & wxLB_MULTIPLE
) )
1111 SendEvent(wxEVT_COMMAND_LISTBOX_DOUBLECLICKED
);
1115 // ----------------------------------------------------------------------------
1117 // ----------------------------------------------------------------------------
1120 The numArg here is the listbox item index while the strArg is used
1121 differently for the different actions:
1123 a) for wxACTION_LISTBOX_FIND it has the natural meaning: this is the string
1126 b) for wxACTION_LISTBOX_SELECT and wxACTION_LISTBOX_EXTENDSEL it is used
1127 to decide if the listbox should send the notification event (it is empty)
1128 or not (it is not): this allows us to reuse the same action for when the
1129 user is dragging the mouse when it has been released although in the
1130 first case no notification is sent while in the second it is sent.
1132 bool wxListBox::PerformAction(const wxControlAction
& action
,
1134 const wxString
& strArg
)
1136 int item
= (int)numArg
;
1138 if ( action
== wxACTION_LISTBOX_SETFOCUS
)
1140 SetCurrentItem(item
);
1142 else if ( action
== wxACTION_LISTBOX_ACTIVATE
)
1146 else if ( action
== wxACTION_LISTBOX_TOGGLE
)
1151 if ( IsSelected(item
) )
1154 SelectAndNotify(item
);
1156 else if ( action
== wxACTION_LISTBOX_SELECT
)
1160 if ( strArg
.empty() )
1161 SelectAndNotify(item
);
1165 else if ( action
== wxACTION_LISTBOX_SELECTADD
)
1167 else if ( action
== wxACTION_LISTBOX_UNSELECT
)
1169 else if ( action
== wxACTION_LISTBOX_MOVEDOWN
)
1171 else if ( action
== wxACTION_LISTBOX_MOVEUP
)
1173 else if ( action
== wxACTION_LISTBOX_PAGEDOWN
)
1174 ChangeCurrent(GetItemsPerPage());
1175 else if ( action
== wxACTION_LISTBOX_PAGEUP
)
1176 ChangeCurrent(-GetItemsPerPage());
1177 else if ( action
== wxACTION_LISTBOX_START
)
1179 else if ( action
== wxACTION_LISTBOX_END
)
1180 SetCurrentItem(GetCount() - 1);
1181 else if ( action
== wxACTION_LISTBOX_UNSELECTALL
)
1183 else if ( action
== wxACTION_LISTBOX_EXTENDSEL
)
1184 ExtendSelection(item
);
1185 else if ( action
== wxACTION_LISTBOX_FIND
)
1186 FindNextItem(strArg
);
1187 else if ( action
== wxACTION_LISTBOX_ANCHOR
)
1188 AnchorSelection(item
== -1 ? m_current
: item
);
1189 else if ( action
== wxACTION_LISTBOX_SELECTALL
||
1190 action
== wxACTION_LISTBOX_SELTOGGLE
)
1191 wxFAIL_MSG(_T("unimplemented yet"));
1193 return wxControl::PerformAction(action
, numArg
, strArg
);
1198 // ============================================================================
1199 // implementation of wxStdListboxInputHandler
1200 // ============================================================================
1202 wxStdListboxInputHandler::wxStdListboxInputHandler(wxInputHandler
*handler
,
1203 bool toggleOnPressAlways
)
1204 : wxStdInputHandler(handler
)
1207 m_toggleOnPressAlways
= toggleOnPressAlways
;
1208 m_actionMouse
= wxACTION_NONE
;
1209 m_trackMouseOutside
= true;
1212 int wxStdListboxInputHandler::HitTest(const wxListBox
*lbox
,
1213 const wxMouseEvent
& event
)
1215 int item
= HitTestUnsafe(lbox
, event
);
1217 return FixItemIndex(lbox
, item
);
1220 int wxStdListboxInputHandler::HitTestUnsafe(const wxListBox
*lbox
,
1221 const wxMouseEvent
& event
)
1223 wxPoint pt
= event
.GetPosition();
1224 pt
-= lbox
->GetClientAreaOrigin();
1226 lbox
->CalcUnscrolledPosition(0, pt
.y
, NULL
, &y
);
1227 return y
/ lbox
->GetLineHeight();
1230 int wxStdListboxInputHandler::FixItemIndex(const wxListBox
*lbox
,
1235 // mouse is above the first item
1238 else if ( item
>= lbox
->GetCount() )
1240 // mouse is below the last item
1241 item
= lbox
->GetCount() - 1;
1247 bool wxStdListboxInputHandler::IsValidIndex(const wxListBox
*lbox
, int item
)
1249 return item
>= 0 && item
< lbox
->GetCount();
1253 wxStdListboxInputHandler::SetupCapture(wxListBox
*lbox
,
1254 const wxMouseEvent
& event
,
1257 // we currently only allow selecting with the left mouse button, if we
1258 // do need to allow using other buttons too we might use the code
1261 m_btnCapture
= event
.LeftDown()
1270 wxControlAction action
;
1271 if ( lbox
->HasMultipleSelection() )
1273 if ( lbox
->GetWindowStyle() & wxLB_MULTIPLE
)
1275 if ( m_toggleOnPressAlways
)
1277 // toggle the item right now
1278 action
= wxACTION_LISTBOX_TOGGLE
;
1282 m_actionMouse
= wxACTION_LISTBOX_SETFOCUS
;
1284 else // wxLB_EXTENDED listbox
1286 // simple click in an extended sel listbox clears the old
1287 // selection and adds the clicked item to it then, ctrl-click
1288 // toggles an item to it and shift-click adds a range between
1289 // the old selection anchor and the clicked item
1290 if ( event
.ControlDown() )
1292 lbox
->PerformAction(wxACTION_LISTBOX_ANCHOR
, item
);
1294 action
= wxACTION_LISTBOX_TOGGLE
;
1296 else if ( event
.ShiftDown() )
1298 action
= wxACTION_LISTBOX_EXTENDSEL
;
1300 else // simple click
1302 lbox
->PerformAction(wxACTION_LISTBOX_ANCHOR
, item
);
1304 action
= wxACTION_LISTBOX_SELECT
;
1307 m_actionMouse
= wxACTION_LISTBOX_EXTENDSEL
;
1310 else // single selection
1313 action
= wxACTION_LISTBOX_SELECT
;
1316 // by default we always do track it
1317 m_trackMouseOutside
= true;
1322 bool wxStdListboxInputHandler::HandleKey(wxInputConsumer
*consumer
,
1323 const wxKeyEvent
& event
,
1326 // we're only interested in the key press events
1327 if ( pressed
&& !event
.AltDown() )
1329 bool isMoveCmd
= true;
1330 int style
= consumer
->GetInputWindow()->GetWindowStyle();
1332 wxControlAction action
;
1335 int keycode
= event
.GetKeyCode();
1340 action
= wxACTION_LISTBOX_MOVEUP
;
1344 action
= wxACTION_LISTBOX_MOVEDOWN
;
1350 action
= wxACTION_LISTBOX_PAGEUP
;
1356 action
= wxACTION_LISTBOX_PAGEDOWN
;
1360 action
= wxACTION_LISTBOX_START
;
1364 action
= wxACTION_LISTBOX_END
;
1369 if ( style
& wxLB_MULTIPLE
)
1371 action
= wxACTION_LISTBOX_TOGGLE
;
1377 action
= wxACTION_LISTBOX_ACTIVATE
;
1382 if ( (keycode
< 255) && wxIsalnum((wxChar
)keycode
) )
1384 action
= wxACTION_LISTBOX_FIND
;
1385 strArg
= (wxChar
)keycode
;
1389 if ( !action
.IsEmpty() )
1391 consumer
->PerformAction(action
, -1, strArg
);
1395 if ( style
& wxLB_SINGLE
)
1397 // the current item is always the one selected
1398 consumer
->PerformAction(wxACTION_LISTBOX_SELECT
);
1400 else if ( style
& wxLB_EXTENDED
)
1402 if ( event
.ShiftDown() )
1403 consumer
->PerformAction(wxACTION_LISTBOX_EXTENDSEL
);
1406 // select the item and make it the new selection anchor
1407 consumer
->PerformAction(wxACTION_LISTBOX_SELECT
);
1408 consumer
->PerformAction(wxACTION_LISTBOX_ANCHOR
);
1411 //else: nothing to do for multiple selection listboxes
1418 return wxStdInputHandler::HandleKey(consumer
, event
, pressed
);
1421 bool wxStdListboxInputHandler::HandleMouse(wxInputConsumer
*consumer
,
1422 const wxMouseEvent
& event
)
1424 wxListBox
*lbox
= wxStaticCast(consumer
->GetInputWindow(), wxListBox
);
1425 int item
= HitTest(lbox
, event
);
1426 wxControlAction action
;
1428 // when the left mouse button is pressed, capture the mouse and track the
1429 // item under mouse (if the mouse leaves the window, we will still be
1430 // getting the mouse move messages generated by wxScrollWindow)
1431 if ( event
.LeftDown() )
1433 // capture the mouse to track the selected item
1434 lbox
->CaptureMouse();
1436 action
= SetupCapture(lbox
, event
, item
);
1438 else if ( m_btnCapture
&& event
.ButtonUp(m_btnCapture
) )
1440 // when the left mouse button is released, release the mouse too
1441 wxWindow
*winCapture
= wxWindow::GetCapture();
1444 winCapture
->ReleaseMouse();
1447 action
= m_actionMouse
;
1449 //else: the mouse wasn't presed over the listbox, only released here
1451 else if ( event
.LeftDClick() )
1453 action
= wxACTION_LISTBOX_ACTIVATE
;
1456 if ( !action
.IsEmpty() )
1458 lbox
->PerformAction(action
, item
);
1463 return wxStdInputHandler::HandleMouse(consumer
, event
);
1466 bool wxStdListboxInputHandler::HandleMouseMove(wxInputConsumer
*consumer
,
1467 const wxMouseEvent
& event
)
1469 wxWindow
*winCapture
= wxWindow::GetCapture();
1470 if ( winCapture
&& (event
.GetEventObject() == winCapture
) )
1472 wxListBox
*lbox
= wxStaticCast(consumer
->GetInputWindow(), wxListBox
);
1474 if ( !m_btnCapture
|| !m_trackMouseOutside
)
1476 // someone captured the mouse for us (we always set m_btnCapture
1477 // when we do it ourselves): in this case we only react to
1478 // the mouse messages when they happen inside the listbox
1479 if ( lbox
->HitTest(event
.GetPosition()) != wxHT_WINDOW_INSIDE
)
1483 int item
= HitTest(lbox
, event
);
1484 if ( !m_btnCapture
)
1486 // now that we have the mouse inside the listbox, do capture it
1487 // normally - but ensure that we will still ignore the outside
1489 SetupCapture(lbox
, event
, item
);
1491 m_trackMouseOutside
= false;
1494 if ( IsValidIndex(lbox
, item
) )
1496 // pass something into strArg to tell the listbox that it shouldn't
1497 // send the notification message: see PerformAction() above
1498 lbox
->PerformAction(m_actionMouse
, item
, _T("no"));
1500 // else: don't pass invalid index to the listbox
1502 else // we don't have capture any more
1506 // if we lost capture unexpectedly (someone else took the capture
1507 // from us), return to a consistent state
1512 return wxStdInputHandler::HandleMouseMove(consumer
, event
);
1515 #endif // wxUSE_LISTBOX