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 // implementation of wxListBox
42 // ============================================================================
44 IMPLEMENT_DYNAMIC_CLASS(wxListBox
, wxControl
)
46 BEGIN_EVENT_TABLE(wxListBox
, wxListBoxBase
)
47 EVT_SIZE(wxListBox::OnSize
)
50 // ----------------------------------------------------------------------------
52 // ----------------------------------------------------------------------------
54 void wxListBox::Init()
56 // will be calculated later when needed
64 // no items hence no current item
67 m_currentChanged
= false;
69 // no need to update anything initially
72 // no scrollbars to show nor update
76 m_showScrollbarY
= false;
79 wxListBox::wxListBox(wxWindow
*parent
,
83 const wxArrayString
& choices
,
85 const wxValidator
& validator
,
91 Create(parent
, id
, pos
, size
, choices
, style
, validator
, name
);
94 bool wxListBox::Create(wxWindow
*parent
,
98 const wxArrayString
& choices
,
100 const wxValidator
& validator
,
101 const wxString
&name
)
103 wxCArrayString
chs(choices
);
105 return Create(parent
, id
, pos
, size
, chs
.GetCount(), chs
.GetStrings(),
106 style
, validator
, name
);
109 bool wxListBox::Create(wxWindow
*parent
,
114 const wxString choices
[],
116 const wxValidator
& validator
,
117 const wxString
&name
)
119 // for compatibility accept both the new and old styles - they mean the
121 if ( style
& wxLB_ALWAYS_SB
)
122 style
|= wxALWAYS_SHOW_SB
;
124 // if we don't have neither multiple nor extended flag, we must have the
125 // single selection listbox
126 if ( !(style
& (wxLB_MULTIPLE
| wxLB_EXTENDED
)) )
127 style
|= wxLB_SINGLE
;
129 #if wxUSE_TWO_WINDOWS
130 style
|= wxVSCROLL
|wxHSCROLL
;
131 if ((style
& wxBORDER_MASK
) == 0)
132 style
|= wxBORDER_SUNKEN
;
135 if ( !wxControl::Create(parent
, id
, pos
, size
, style
,
139 m_strings
= new wxArrayString
;
145 CreateInputHandler(wxINP_HANDLER_LISTBOX
);
150 wxListBox::~wxListBox()
152 // call this just to free the client data -- and avoid leaking memory
160 // ----------------------------------------------------------------------------
161 // adding/inserting strings
162 // ----------------------------------------------------------------------------
164 int wxCMPFUNC_CONV
wxListBoxSortNoCase(wxString
* s1
, wxString
* s2
)
166 return s1
->CmpNoCase(*s2
);
169 int wxListBox::DoAppendOnly(const wxString
& item
)
175 m_strings
->Add(item
);
176 m_strings
->Sort(wxListBoxSortNoCase
);
177 index
= m_strings
->Index(item
);
181 index
= m_strings
->GetCount();
182 m_strings
->Add(item
);
188 int wxListBox::DoAppend(const wxString
& item
)
190 size_t index
= DoAppendOnly( item
);
192 m_itemsClientData
.Insert(NULL
, index
);
194 m_updateScrollbarY
= true;
196 if ( HasHorzScrollbar() )
198 // has the max width increased?
200 GetTextExtent(item
, &width
, NULL
);
201 if ( width
> m_maxWidth
)
204 m_maxWidthItem
= index
;
205 m_updateScrollbarX
= true;
209 RefreshFromItemToEnd(index
);
214 void wxListBox::DoInsertItems(const wxArrayString
& items
, unsigned int pos
)
216 // the position of the item being added to a sorted listbox can't be
218 wxCHECK_RET( !IsSorted(), _T("can't insert items into sorted listbox") );
220 unsigned int count
= items
.GetCount();
221 for ( unsigned int n
= 0; n
< count
; n
++ )
223 m_strings
->Insert(items
[n
], pos
+ n
);
224 m_itemsClientData
.Insert(NULL
, pos
+ n
);
227 // the number of items has changed so we might have to show the scrollbar
228 m_updateScrollbarY
= true;
230 // the max width also might have changed - just recalculate it instead of
231 // keeping track of it here, this is probably more efficient for a typical
233 RefreshHorzScrollbar();
235 // note that we have to refresh all the items after the ones we inserted,
236 // not just these items
237 RefreshFromItemToEnd(pos
);
240 void wxListBox::DoSetItems(const wxArrayString
& items
, void **clientData
)
244 unsigned int count
= items
.GetCount();
248 m_strings
->Alloc(count
);
250 m_itemsClientData
.Alloc(count
);
251 for ( unsigned int n
= 0; n
< count
; n
++ )
253 unsigned int index
= DoAppendOnly(items
[n
]);
255 m_itemsClientData
.Insert(clientData
? clientData
[n
] : NULL
, index
);
258 m_updateScrollbarY
= true;
263 void wxListBox::SetString(unsigned int n
, const wxString
& s
)
265 wxCHECK_RET( !IsSorted(), _T("can't set string in sorted listbox") );
269 if ( HasHorzScrollbar() )
271 // we need to update m_maxWidth as changing the string may cause the
272 // horz scrollbar [dis]appear
275 GetTextExtent(s
, &width
, NULL
);
277 // it might have increased if the new string is long
278 if ( width
> m_maxWidth
)
282 m_updateScrollbarX
= true;
284 // or also decreased if the old string was the longest one
285 else if ( n
== (unsigned int)m_maxWidthItem
)
287 RefreshHorzScrollbar();
294 // ----------------------------------------------------------------------------
296 // ----------------------------------------------------------------------------
298 void wxListBox::DoClear()
302 if ( HasClientObjectData() )
304 unsigned int count
= m_itemsClientData
.GetCount();
305 for ( unsigned int n
= 0; n
< count
; n
++ )
307 delete (wxClientData
*) m_itemsClientData
[n
];
311 m_itemsClientData
.Clear();
312 m_selections
.Clear();
317 void wxListBox::Clear()
321 m_updateScrollbarY
= true;
323 RefreshHorzScrollbar();
328 void wxListBox::Delete(unsigned int n
)
330 wxCHECK_RET( IsValid(n
),
331 _T("invalid index in wxListBox::Delete") );
333 // do it before removing the index as otherwise the last item will not be
334 // refreshed (as GetCount() will be decremented)
335 RefreshFromItemToEnd(n
);
337 m_strings
->RemoveAt(n
);
339 if ( HasClientObjectData() )
341 delete (wxClientData
*)m_itemsClientData
[n
];
344 m_itemsClientData
.RemoveAt(n
);
346 // when the item disappears we must not keep using its index
347 if ( (int)n
== m_current
)
351 else if ( (int)n
< m_current
)
355 //else: current item may stay
357 // update the selections array: the indices of all seletected items after
358 // the one being deleted must change and the item itselfm ust be removed
359 int index
= wxNOT_FOUND
;
360 unsigned int count
= m_selections
.GetCount();
361 for ( unsigned int item
= 0; item
< count
; item
++ )
363 if ( m_selections
[item
] == (int)n
)
365 // remember to delete it later
368 else if ( m_selections
[item
] > (int)n
)
370 // to account for the index shift
371 m_selections
[item
]--;
373 //else: nothing changed for this one
376 if ( index
!= wxNOT_FOUND
)
378 m_selections
.RemoveAt(index
);
381 // the number of items has changed, hence the scrollbar may disappear
382 m_updateScrollbarY
= true;
384 // finally, if the longest item was deleted the scrollbar may disappear
385 if ( (int)n
== m_maxWidthItem
)
387 RefreshHorzScrollbar();
391 // ----------------------------------------------------------------------------
392 // client data handling
393 // ----------------------------------------------------------------------------
395 void wxListBox::DoSetItemClientData(unsigned int n
, void* clientData
)
397 m_itemsClientData
[n
] = clientData
;
400 void *wxListBox::DoGetItemClientData(unsigned int n
) const
402 return m_itemsClientData
[n
];
405 void wxListBox::DoSetItemClientObject(unsigned int n
, wxClientData
* clientData
)
407 m_itemsClientData
[n
] = clientData
;
410 wxClientData
* wxListBox::DoGetItemClientObject(unsigned int n
) const
412 return (wxClientData
*)m_itemsClientData
[n
];
415 // ----------------------------------------------------------------------------
417 // ----------------------------------------------------------------------------
419 void wxListBox::DoSetSelection(int n
, bool select
)
423 if ( m_selections
.Index(n
) == wxNOT_FOUND
)
425 if ( !HasMultipleSelection() )
427 // selecting an item in a single selection listbox deselects
436 //else: already selected
440 int index
= m_selections
.Index(n
);
441 if ( index
!= wxNOT_FOUND
)
443 m_selections
.RemoveAt(index
);
450 // sanity check: a single selection listbox can't have more than one item
452 wxASSERT_MSG( HasMultipleSelection() || (m_selections
.GetCount() < 2),
453 _T("multiple selected items in single selection lbox?") );
457 // the newly selected item becomes the current one
462 int wxListBox::GetSelection() const
464 wxCHECK_MSG( !HasMultipleSelection(), wxNOT_FOUND
,
465 _T("use wxListBox::GetSelections for ths listbox") );
467 return m_selections
.IsEmpty() ? wxNOT_FOUND
: m_selections
[0];
470 int wxCMPFUNC_CONV
wxCompareInts(int *n
, int *m
)
475 int wxListBox::GetSelections(wxArrayInt
& selections
) const
477 // always return sorted array to the user
478 selections
= m_selections
;
479 unsigned int count
= m_selections
.GetCount();
481 // don't call sort on an empty array
484 selections
.Sort(wxCompareInts
);
490 // ----------------------------------------------------------------------------
491 // refresh logic: we use delayed refreshing which allows to avoid multiple
492 // refreshes (and hence flicker) in case when several listbox items are
493 // added/deleted/changed subsequently
494 // ----------------------------------------------------------------------------
496 void wxListBox::RefreshFromItemToEnd(int from
)
498 RefreshItems(from
, GetCount() - from
);
501 void wxListBox::RefreshItems(int from
, int count
)
503 switch ( m_updateCount
)
507 m_updateCount
= count
;
511 // we refresh everything anyhow
515 // add these items to the others which we have to refresh
516 if ( m_updateFrom
< from
)
518 count
+= from
- m_updateFrom
;
519 if ( m_updateCount
< count
)
520 m_updateCount
= count
;
522 else // m_updateFrom >= from
524 int updateLast
= wxMax(m_updateFrom
+ m_updateCount
,
527 m_updateCount
= updateLast
- m_updateFrom
;
532 void wxListBox::RefreshItem(int n
)
534 switch ( m_updateCount
)
537 // refresh this item only
543 // we refresh everything anyhow
547 // add this item to the others which we have to refresh
548 if ( m_updateFrom
< n
)
550 if ( m_updateCount
< n
- m_updateFrom
+ 1 )
551 m_updateCount
= n
- m_updateFrom
+ 1;
553 else // n <= m_updateFrom
555 m_updateCount
+= m_updateFrom
- n
;
561 void wxListBox::RefreshAll()
566 void wxListBox::RefreshHorzScrollbar()
568 m_maxWidth
= 0; // recalculate it
569 m_updateScrollbarX
= true;
572 void wxListBox::UpdateScrollbars()
574 wxSize size
= GetClientSize();
576 // is our height enough to show all items?
577 unsigned int nLines
= GetCount();
578 wxCoord lineHeight
= GetLineHeight();
579 bool showScrollbarY
= (int)nLines
*lineHeight
> size
.y
;
581 // check the width too if required
582 wxCoord charWidth
, maxWidth
;
584 if ( HasHorzScrollbar() )
586 charWidth
= GetCharWidth();
587 maxWidth
= GetMaxWidth();
588 showScrollbarX
= maxWidth
> size
.x
;
590 else // never show it
592 charWidth
= maxWidth
= 0;
593 showScrollbarX
= false;
596 // what should be the scrollbar range now?
597 int scrollRangeX
= showScrollbarX
598 ? (maxWidth
+ charWidth
- 1) / charWidth
+ 2 // FIXME
600 int scrollRangeY
= showScrollbarY
602 (size
.y
% lineHeight
+ lineHeight
- 1) / lineHeight
605 // reset scrollbars if something changed: either the visibility status
606 // or the range of a scrollbar which is shown
607 if ( (showScrollbarY
!= m_showScrollbarY
) ||
608 (showScrollbarX
!= m_showScrollbarX
) ||
609 (showScrollbarY
&& (scrollRangeY
!= m_scrollRangeY
)) ||
610 (showScrollbarX
&& (scrollRangeX
!= m_scrollRangeX
)) )
613 GetViewStart(&x
, &y
);
614 SetScrollbars(charWidth
, lineHeight
,
615 scrollRangeX
, scrollRangeY
,
618 m_showScrollbarX
= showScrollbarX
;
619 m_showScrollbarY
= showScrollbarY
;
621 m_scrollRangeX
= scrollRangeX
;
622 m_scrollRangeY
= scrollRangeY
;
626 void wxListBox::UpdateItems()
628 // only refresh the items which must be refreshed
629 if ( m_updateCount
== -1 )
632 wxLogTrace(_T("listbox"), _T("Refreshing all"));
638 wxSize size
= GetClientSize();
641 rect
.height
= size
.y
;
642 rect
.y
+= m_updateFrom
*GetLineHeight();
643 rect
.height
= m_updateCount
*GetLineHeight();
645 // we don't need to calculate x position as we always refresh the
647 CalcScrolledPosition(0, rect
.y
, NULL
, &rect
.y
);
649 wxLogTrace(_T("listbox"), _T("Refreshing items %d..%d (%d-%d)"),
650 m_updateFrom
, m_updateFrom
+ m_updateCount
- 1,
651 rect
.GetTop(), rect
.GetBottom());
653 Refresh(true, &rect
);
657 void wxListBox::OnInternalIdle()
659 if ( m_updateScrollbarY
|| m_updateScrollbarX
)
664 m_updateScrollbarY
= false;
667 if ( m_currentChanged
)
669 DoEnsureVisible(m_current
);
671 m_currentChanged
= false;
680 wxListBoxBase::OnInternalIdle();
683 // ----------------------------------------------------------------------------
685 // ----------------------------------------------------------------------------
687 wxBorder
wxListBox::GetDefaultBorder() const
689 return wxBORDER_SUNKEN
;
692 void wxListBox::DoDraw(wxControlRenderer
*renderer
)
694 // adjust the DC to account for scrolling
695 wxDC
& dc
= renderer
->GetDC();
697 dc
.SetFont(GetFont());
699 // get the update rect
700 wxRect rectUpdate
= GetUpdateClientRect();
703 CalcUnscrolledPosition(0, rectUpdate
.GetTop(), NULL
, &yTop
);
704 CalcUnscrolledPosition(0, rectUpdate
.GetBottom(), NULL
, &yBottom
);
706 // get the items which must be redrawn
707 wxCoord lineHeight
= GetLineHeight();
708 unsigned int itemFirst
= yTop
/ lineHeight
,
709 itemLast
= (yBottom
+ lineHeight
- 1) / lineHeight
,
710 itemMax
= m_strings
->GetCount();
712 if ( itemFirst
>= itemMax
)
715 if ( itemLast
> itemMax
)
719 wxLogTrace(_T("listbox"), _T("Repainting items %d..%d"),
720 itemFirst
, itemLast
);
722 DoDrawRange(renderer
, itemFirst
, itemLast
);
725 void wxListBox::DoDrawRange(wxControlRenderer
*renderer
,
726 int itemFirst
, int itemLast
)
728 renderer
->DrawItems(this, itemFirst
, itemLast
);
731 // ----------------------------------------------------------------------------
733 // ----------------------------------------------------------------------------
735 bool wxListBox::SetFont(const wxFont
& font
)
737 if ( !wxControl::SetFont(font
) )
747 void wxListBox::CalcItemsPerPage()
749 m_lineHeight
= GetRenderer()->GetListboxItemHeight(GetCharHeight());
750 m_itemsPerPage
= GetClientSize().y
/ m_lineHeight
;
753 int wxListBox::GetItemsPerPage() const
755 if ( !m_itemsPerPage
)
757 wxConstCast(this, wxListBox
)->CalcItemsPerPage();
760 return m_itemsPerPage
;
763 wxCoord
wxListBox::GetLineHeight() const
767 wxConstCast(this, wxListBox
)->CalcItemsPerPage();
773 wxCoord
wxListBox::GetMaxWidth() const
775 if ( m_maxWidth
== 0 )
777 wxListBox
*self
= wxConstCast(this, wxListBox
);
779 unsigned int count
= m_strings
->GetCount();
780 for ( unsigned int n
= 0; n
< count
; n
++ )
782 GetTextExtent(this->GetString(n
), &width
, NULL
);
783 if ( width
> m_maxWidth
)
785 self
->m_maxWidth
= width
;
786 self
->m_maxWidthItem
= n
;
794 void wxListBox::OnSize(wxSizeEvent
& event
)
796 // recalculate the number of items per page
799 // the scrollbars might [dis]appear
801 m_updateScrollbarY
= true;
806 void wxListBox::DoSetFirstItem(int n
)
811 void wxListBox::DoSetSize(int x
, int y
,
812 int width
, int height
,
815 if ( GetWindowStyle() & wxLB_INT_HEIGHT
)
817 // we must round up the height to an entire number of rows
819 // the client area must contain an int number of rows, so take borders
821 wxRect rectBorders
= GetRenderer()->GetBorderDimensions(GetBorder());
822 wxCoord hBorders
= rectBorders
.y
+ rectBorders
.height
;
824 wxCoord hLine
= GetLineHeight();
825 height
= ((height
- hBorders
+ hLine
- 1) / hLine
)*hLine
+ hBorders
;
828 wxListBoxBase::DoSetSize(x
, y
, width
, height
, sizeFlags
);
831 wxSize
wxListBox::DoGetBestClientSize() const
836 unsigned int count
= m_strings
->GetCount();
837 for ( unsigned int n
= 0; n
< count
; n
++ )
840 GetTextExtent(this->GetString(n
), &w
, &h
);
848 // if the listbox is empty, still give it some non zero (even if
849 // arbitrary) size - otherwise, leave small margin around the strings
853 width
+= 3*GetCharWidth();
856 height
= GetCharHeight();
858 // we need the height of the entire listbox, not just of one line
859 height
*= wxMax(count
, 7);
861 return wxSize(width
, height
);
864 // ----------------------------------------------------------------------------
866 // ----------------------------------------------------------------------------
868 bool wxListBox::SendEvent(wxEventType type
, int item
)
870 wxCommandEvent
event(type
, m_windowId
);
871 event
.SetEventObject(this);
873 // use the current item by default
879 // client data and string parameters only make sense if we have an item
882 if ( HasClientObjectData() )
883 event
.SetClientObject(GetClientObject(item
));
884 else if ( HasClientUntypedData() )
885 event
.SetClientData(GetClientData(item
));
887 event
.SetString(GetString(item
));
892 return GetEventHandler()->ProcessEvent(event
);
895 void wxListBox::SetCurrentItem(int n
)
897 if ( n
!= m_current
)
899 if ( m_current
!= -1 )
900 RefreshItem(m_current
);
904 if ( m_current
!= -1 )
906 m_currentChanged
= true;
908 RefreshItem(m_current
);
911 //else: nothing to do
914 bool wxListBox::FindItem(const wxString
& prefix
, bool strictlyAfter
)
916 unsigned int count
= GetCount();
919 // empty listbox, we can't find anything in it
923 // start either from the current item or from the next one if strictlyAfter
928 // the following line will set first correctly to 0 if there is no
929 // selection (m_current == -1)
930 first
= m_current
== (int)(count
- 1) ? 0 : m_current
+ 1;
932 else // start with the current
934 first
= m_current
== -1 ? 0 : m_current
;
937 int last
= first
== 0 ? count
- 1 : first
- 1;
939 // if this is not true we'd never exit from the loop below!
940 wxASSERT_MSG( first
< (int)count
&& last
< (int)count
, _T("logic error") );
942 // precompute it outside the loop
943 size_t len
= prefix
.length();
945 // loop over all items in the listbox
946 for ( int item
= first
; item
!= (int)last
; item
< (int)(count
- 1) ? item
++ : item
= 0 )
948 if ( wxStrnicmp(this->GetString(item
).c_str(), prefix
, len
) == 0 )
950 SetCurrentItem(item
);
952 if ( !(GetWindowStyle() & wxLB_MULTIPLE
) )
955 SelectAndNotify(item
);
957 if ( GetWindowStyle() & wxLB_EXTENDED
)
958 AnchorSelection(item
);
969 void wxListBox::EnsureVisible(int n
)
971 if ( m_updateScrollbarY
)
976 m_updateScrollbarY
= false;
982 void wxListBox::DoEnsureVisible(int n
)
984 if ( !m_showScrollbarY
)
986 // nothing to do - everything is shown anyhow
991 GetViewStart(0, &first
);
994 // we need to scroll upwards, so make the current item appear on top
995 // of the shown range
1000 int last
= first
+ GetClientSize().y
/ GetLineHeight() - 1;
1003 // scroll down: the current item appears at the bottom of the
1005 Scroll(0, n
- (last
- first
));
1010 void wxListBox::ChangeCurrent(int diff
)
1012 int current
= m_current
== -1 ? 0 : m_current
;
1016 int last
= GetCount() - 1;
1019 else if ( current
> last
)
1022 SetCurrentItem(current
);
1025 void wxListBox::ExtendSelection(int itemTo
)
1027 // if we don't have the explicit values for selection start/end, make them
1029 if ( m_selAnchor
== -1 )
1030 m_selAnchor
= m_current
;
1035 // swap the start/end of selection range if necessary
1036 int itemFrom
= m_selAnchor
;
1037 if ( itemFrom
> itemTo
)
1039 int itemTmp
= itemFrom
;
1044 // the selection should now include all items in the range between the
1045 // anchor and the specified item and only them
1048 for ( n
= 0; n
< itemFrom
; n
++ )
1053 for ( ; n
<= itemTo
; n
++ )
1058 unsigned int count
= GetCount();
1059 for ( ; n
< (int)count
; n
++ )
1065 void wxListBox::DoSelect(int item
, bool sel
)
1069 // go to this item first
1070 SetCurrentItem(item
);
1073 // the current item is the one we want to change: either it was just
1074 // changed above to be the same as item or item == -1 in which we case we
1075 // are supposed to use the current one anyhow
1076 if ( m_current
!= -1 )
1079 SetSelection(m_current
, sel
);
1083 void wxListBox::SelectAndNotify(int item
)
1087 SendEvent(wxEVT_COMMAND_LISTBOX_SELECTED
);
1090 void wxListBox::Activate(int item
)
1093 SetCurrentItem(item
);
1097 if ( !(GetWindowStyle() & wxLB_MULTIPLE
) )
1106 SendEvent(wxEVT_COMMAND_LISTBOX_DOUBLECLICKED
);
1110 // ----------------------------------------------------------------------------
1112 // ----------------------------------------------------------------------------
1115 The numArg here is the listbox item index while the strArg is used
1116 differently for the different actions:
1118 a) for wxACTION_LISTBOX_FIND it has the natural meaning: this is the string
1121 b) for wxACTION_LISTBOX_SELECT and wxACTION_LISTBOX_EXTENDSEL it is used
1122 to decide if the listbox should send the notification event (it is empty)
1123 or not (it is not): this allows us to reuse the same action for when the
1124 user is dragging the mouse when it has been released although in the
1125 first case no notification is sent while in the second it is sent.
1127 bool wxListBox::PerformAction(const wxControlAction
& action
,
1129 const wxString
& strArg
)
1131 int item
= (int)numArg
;
1133 if ( action
== wxACTION_LISTBOX_SETFOCUS
)
1135 SetCurrentItem(item
);
1137 else if ( action
== wxACTION_LISTBOX_ACTIVATE
)
1141 else if ( action
== wxACTION_LISTBOX_TOGGLE
)
1146 if ( IsSelected(item
) )
1149 SelectAndNotify(item
);
1151 else if ( action
== wxACTION_LISTBOX_SELECT
)
1155 if ( strArg
.empty() )
1156 SelectAndNotify(item
);
1160 else if ( action
== wxACTION_LISTBOX_SELECTADD
)
1162 else if ( action
== wxACTION_LISTBOX_UNSELECT
)
1164 else if ( action
== wxACTION_LISTBOX_MOVEDOWN
)
1166 else if ( action
== wxACTION_LISTBOX_MOVEUP
)
1168 else if ( action
== wxACTION_LISTBOX_PAGEDOWN
)
1169 ChangeCurrent(GetItemsPerPage());
1170 else if ( action
== wxACTION_LISTBOX_PAGEUP
)
1171 ChangeCurrent(-GetItemsPerPage());
1172 else if ( action
== wxACTION_LISTBOX_START
)
1174 else if ( action
== wxACTION_LISTBOX_END
)
1175 SetCurrentItem(GetCount() - 1);
1176 else if ( action
== wxACTION_LISTBOX_UNSELECTALL
)
1178 else if ( action
== wxACTION_LISTBOX_EXTENDSEL
)
1179 ExtendSelection(item
);
1180 else if ( action
== wxACTION_LISTBOX_FIND
)
1181 FindNextItem(strArg
);
1182 else if ( action
== wxACTION_LISTBOX_ANCHOR
)
1183 AnchorSelection(item
== -1 ? m_current
: item
);
1184 else if ( action
== wxACTION_LISTBOX_SELECTALL
||
1185 action
== wxACTION_LISTBOX_SELTOGGLE
)
1186 wxFAIL_MSG(_T("unimplemented yet"));
1188 return wxControl::PerformAction(action
, numArg
, strArg
);
1193 // ============================================================================
1194 // implementation of wxStdListboxInputHandler
1195 // ============================================================================
1197 wxStdListboxInputHandler::wxStdListboxInputHandler(wxInputHandler
*handler
,
1198 bool toggleOnPressAlways
)
1199 : wxStdInputHandler(handler
)
1202 m_toggleOnPressAlways
= toggleOnPressAlways
;
1203 m_actionMouse
= wxACTION_NONE
;
1204 m_trackMouseOutside
= true;
1207 int wxStdListboxInputHandler::HitTest(const wxListBox
*lbox
,
1208 const wxMouseEvent
& event
)
1210 int item
= HitTestUnsafe(lbox
, event
);
1212 return FixItemIndex(lbox
, item
);
1215 int wxStdListboxInputHandler::HitTestUnsafe(const wxListBox
*lbox
,
1216 const wxMouseEvent
& event
)
1218 wxPoint pt
= event
.GetPosition();
1219 pt
-= lbox
->GetClientAreaOrigin();
1221 lbox
->CalcUnscrolledPosition(0, pt
.y
, NULL
, &y
);
1222 return y
/ lbox
->GetLineHeight();
1225 int wxStdListboxInputHandler::FixItemIndex(const wxListBox
*lbox
,
1230 // mouse is above the first item
1233 else if ( (unsigned int)item
>= lbox
->GetCount() )
1235 // mouse is below the last item
1236 item
= lbox
->GetCount() - 1;
1242 bool wxStdListboxInputHandler::IsValidIndex(const wxListBox
*lbox
, int item
)
1244 return item
>= 0 && (unsigned int)item
< lbox
->GetCount();
1248 wxStdListboxInputHandler::SetupCapture(wxListBox
*lbox
,
1249 const wxMouseEvent
& event
,
1252 // we currently only allow selecting with the left mouse button, if we
1253 // do need to allow using other buttons too we might use the code
1256 m_btnCapture
= event
.LeftDown()
1265 wxControlAction action
;
1266 if ( lbox
->HasMultipleSelection() )
1268 if ( lbox
->GetWindowStyle() & wxLB_MULTIPLE
)
1270 if ( m_toggleOnPressAlways
)
1272 // toggle the item right now
1273 action
= wxACTION_LISTBOX_TOGGLE
;
1277 m_actionMouse
= wxACTION_LISTBOX_SETFOCUS
;
1279 else // wxLB_EXTENDED listbox
1281 // simple click in an extended sel listbox clears the old
1282 // selection and adds the clicked item to it then, ctrl-click
1283 // toggles an item to it and shift-click adds a range between
1284 // the old selection anchor and the clicked item
1285 if ( event
.ControlDown() )
1287 lbox
->PerformAction(wxACTION_LISTBOX_ANCHOR
, item
);
1289 action
= wxACTION_LISTBOX_TOGGLE
;
1291 else if ( event
.ShiftDown() )
1293 action
= wxACTION_LISTBOX_EXTENDSEL
;
1295 else // simple click
1297 lbox
->PerformAction(wxACTION_LISTBOX_ANCHOR
, item
);
1299 action
= wxACTION_LISTBOX_SELECT
;
1302 m_actionMouse
= wxACTION_LISTBOX_EXTENDSEL
;
1305 else // single selection
1308 action
= wxACTION_LISTBOX_SELECT
;
1311 // by default we always do track it
1312 m_trackMouseOutside
= true;
1317 bool wxStdListboxInputHandler::HandleKey(wxInputConsumer
*consumer
,
1318 const wxKeyEvent
& event
,
1321 // we're only interested in the key press events
1322 if ( pressed
&& !event
.AltDown() )
1324 bool isMoveCmd
= true;
1325 int style
= consumer
->GetInputWindow()->GetWindowStyle();
1327 wxControlAction action
;
1330 int keycode
= event
.GetKeyCode();
1335 action
= wxACTION_LISTBOX_MOVEUP
;
1339 action
= wxACTION_LISTBOX_MOVEDOWN
;
1343 action
= wxACTION_LISTBOX_PAGEUP
;
1347 action
= wxACTION_LISTBOX_PAGEDOWN
;
1351 action
= wxACTION_LISTBOX_START
;
1355 action
= wxACTION_LISTBOX_END
;
1360 if ( style
& wxLB_MULTIPLE
)
1362 action
= wxACTION_LISTBOX_TOGGLE
;
1368 action
= wxACTION_LISTBOX_ACTIVATE
;
1373 if ( (keycode
< 255) && wxIsalnum((wxChar
)keycode
) )
1375 action
= wxACTION_LISTBOX_FIND
;
1376 strArg
= (wxChar
)keycode
;
1380 if ( !action
.IsEmpty() )
1382 consumer
->PerformAction(action
, -1, strArg
);
1386 if ( style
& wxLB_SINGLE
)
1388 // the current item is always the one selected
1389 consumer
->PerformAction(wxACTION_LISTBOX_SELECT
);
1391 else if ( style
& wxLB_EXTENDED
)
1393 if ( event
.ShiftDown() )
1394 consumer
->PerformAction(wxACTION_LISTBOX_EXTENDSEL
);
1397 // select the item and make it the new selection anchor
1398 consumer
->PerformAction(wxACTION_LISTBOX_SELECT
);
1399 consumer
->PerformAction(wxACTION_LISTBOX_ANCHOR
);
1402 //else: nothing to do for multiple selection listboxes
1409 return wxStdInputHandler::HandleKey(consumer
, event
, pressed
);
1412 bool wxStdListboxInputHandler::HandleMouse(wxInputConsumer
*consumer
,
1413 const wxMouseEvent
& event
)
1415 wxListBox
*lbox
= wxStaticCast(consumer
->GetInputWindow(), wxListBox
);
1416 int item
= HitTest(lbox
, event
);
1417 wxControlAction action
;
1419 // when the left mouse button is pressed, capture the mouse and track the
1420 // item under mouse (if the mouse leaves the window, we will still be
1421 // getting the mouse move messages generated by wxScrollWindow)
1422 if ( event
.LeftDown() )
1424 // capture the mouse to track the selected item
1425 lbox
->CaptureMouse();
1427 action
= SetupCapture(lbox
, event
, item
);
1429 else if ( m_btnCapture
&& event
.ButtonUp(m_btnCapture
) )
1431 // when the left mouse button is released, release the mouse too
1432 wxWindow
*winCapture
= wxWindow::GetCapture();
1435 winCapture
->ReleaseMouse();
1438 action
= m_actionMouse
;
1440 //else: the mouse wasn't presed over the listbox, only released here
1442 else if ( event
.LeftDClick() )
1444 action
= wxACTION_LISTBOX_ACTIVATE
;
1447 if ( !action
.IsEmpty() )
1449 lbox
->PerformAction(action
, item
);
1454 return wxStdInputHandler::HandleMouse(consumer
, event
);
1457 bool wxStdListboxInputHandler::HandleMouseMove(wxInputConsumer
*consumer
,
1458 const wxMouseEvent
& event
)
1460 wxWindow
*winCapture
= wxWindow::GetCapture();
1461 if ( winCapture
&& (event
.GetEventObject() == winCapture
) )
1463 wxListBox
*lbox
= wxStaticCast(consumer
->GetInputWindow(), wxListBox
);
1465 if ( !m_btnCapture
|| !m_trackMouseOutside
)
1467 // someone captured the mouse for us (we always set m_btnCapture
1468 // when we do it ourselves): in this case we only react to
1469 // the mouse messages when they happen inside the listbox
1470 if ( lbox
->HitTest(event
.GetPosition()) != wxHT_WINDOW_INSIDE
)
1474 int item
= HitTest(lbox
, event
);
1475 if ( !m_btnCapture
)
1477 // now that we have the mouse inside the listbox, do capture it
1478 // normally - but ensure that we will still ignore the outside
1480 SetupCapture(lbox
, event
, item
);
1482 m_trackMouseOutside
= false;
1485 if ( IsValidIndex(lbox
, item
) )
1487 // pass something into strArg to tell the listbox that it shouldn't
1488 // send the notification message: see PerformAction() above
1489 lbox
->PerformAction(m_actionMouse
, item
, _T("no"));
1491 // else: don't pass invalid index to the listbox
1493 else // we don't have capture any more
1497 // if we lost capture unexpectedly (someone else took the capture
1498 // from us), return to a consistent state
1503 return wxStdInputHandler::HandleMouseMove(consumer
, event
);
1506 #endif // wxUSE_LISTBOX