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 bool wxListBox::Create(wxWindow
*parent
,
88 const wxString choices
[],
90 const wxValidator
& validator
,
93 // for compatibility accept both the new and old styles - they mean the
95 if ( style
& wxLB_ALWAYS_SB
)
96 style
|= wxALWAYS_SHOW_SB
;
98 // if we don't have neither multiple nor extended flag, we must have the
99 // single selection listbox
100 if ( !(style
& (wxLB_MULTIPLE
| wxLB_EXTENDED
)) )
101 style
|= wxLB_SINGLE
;
103 #if wxUSE_TWO_WINDOWS
104 style
|= wxVSCROLL
|wxHSCROLL
;
105 if ((style
& wxBORDER_MASK
) == 0)
106 style
|= wxBORDER_SUNKEN
;
109 if ( !wxControl::Create(parent
, id
, pos
, size
, style
,
115 if ( style
& wxLB_SORT
)
116 m_stringsSorted
= new wxSortedArrayString
;
118 m_strings
= new wxArrayString
;
124 CreateInputHandler(wxINP_HANDLER_LISTBOX
);
129 wxListBox::~wxListBox()
131 // call this just to free the client data -- and avoid leaking memory
135 delete m_stringsSorted
;
142 // ----------------------------------------------------------------------------
143 // adding/inserting strings
144 // ----------------------------------------------------------------------------
146 int wxListBox::DoAppend(const wxString
& item
)
152 index
= m_stringsSorted
->Add(item
);
156 index
= m_strings
->GetCount();
157 m_strings
->Add(item
);
160 m_itemsClientData
.Insert(NULL
, index
);
162 m_updateScrollbarY
= TRUE
;
164 if ( HasHorzScrollbar() )
166 // has the max width increased?
168 GetTextExtent(item
, &width
, NULL
);
169 if ( width
> m_maxWidth
)
172 m_maxWidthItem
= index
;
173 m_updateScrollbarX
= TRUE
;
177 RefreshFromItemToEnd(index
);
182 void wxListBox::DoInsertItems(const wxArrayString
& items
, int pos
)
184 // the position of the item being added to a sorted listbox can't be
186 wxCHECK_RET( !IsSorted(), _T("can't insert items into sorted listbox") );
188 size_t count
= items
.GetCount();
189 for ( size_t n
= 0; n
< count
; n
++ )
191 m_strings
->Insert(items
[n
], pos
+ n
);
192 m_itemsClientData
.Insert(NULL
, pos
+ n
);
195 // the number of items has changed so we might have to show the scrollbar
196 m_updateScrollbarY
= TRUE
;
198 // the max width also might have changed - just recalculate it instead of
199 // keeping track of it here, this is probably more efficient for a typical
201 RefreshHorzScrollbar();
203 // note that we have to refresh all the items after the ones we inserted,
204 // not just these items
205 RefreshFromItemToEnd(pos
);
208 void wxListBox::DoSetItems(const wxArrayString
& items
, void **clientData
)
212 size_t count
= items
.GetCount();
216 m_strings
->Alloc(count
);
217 m_itemsClientData
.Alloc(count
);
218 for ( size_t n
= 0; n
< count
; n
++ )
224 index
= m_stringsSorted
->Add(items
[n
]);
228 index
= m_strings
->GetCount();
229 m_strings
->Add(items
[n
]);
232 m_itemsClientData
.Insert(clientData
? clientData
[n
] : NULL
, index
);
235 m_updateScrollbarY
= TRUE
;
240 void wxListBox::SetString(int n
, const wxString
& s
)
242 if ( HasHorzScrollbar() )
244 // we need to update m_maxWidth as changing the string may cause the
245 // horz scrollbar [dis]appear
248 GetTextExtent(s
, &width
, NULL
);
250 // it might have increased if the new string is long
251 if ( width
> m_maxWidth
)
255 m_updateScrollbarX
= TRUE
;
257 // or also decreased if the old string was the longest one
258 else if ( n
== m_maxWidthItem
)
260 RefreshHorzScrollbar();
263 else // no horz scrollbar
271 // ----------------------------------------------------------------------------
273 // ----------------------------------------------------------------------------
275 void wxListBox::DoClear()
279 if ( HasClientObjectData() )
281 size_t count
= m_itemsClientData
.GetCount();
282 for ( size_t n
= 0; n
< count
; n
++ )
284 delete (wxClientData
*) m_itemsClientData
[n
];
288 m_itemsClientData
.Clear();
289 m_selections
.Clear();
294 void wxListBox::Clear()
298 m_updateScrollbarY
= TRUE
;
300 RefreshHorzScrollbar();
305 void wxListBox::Delete(int n
)
307 wxCHECK_RET( n
>= 0 && n
< GetCount(),
308 _T("invalid index in wxListBox::Delete") );
310 // do it before removing the index as otherwise the last item will not be
311 // refreshed (as GetCount() will be decremented)
312 RefreshFromItemToEnd(n
);
314 m_strings
->RemoveAt(n
);
316 if ( HasClientObjectData() )
318 delete (wxClientData
*)m_itemsClientData
[n
];
321 m_itemsClientData
.RemoveAt(n
);
323 // when the item disappears we must not keep using its index
324 if ( n
== m_current
)
328 else if ( n
< m_current
)
332 //else: current item may stay
334 // update the selections array: the indices of all seletected items after
335 // the one being deleted must change and the item itselfm ust be removed
336 int index
= wxNOT_FOUND
;
337 size_t count
= m_selections
.GetCount();
338 for ( size_t item
= 0; item
< count
; item
++ )
340 if ( m_selections
[item
] == n
)
342 // remember to delete it later
345 else if ( m_selections
[item
] > n
)
347 // to account for the index shift
348 m_selections
[item
]--;
350 //else: nothing changed for this one
353 if ( index
!= wxNOT_FOUND
)
355 m_selections
.RemoveAt(index
);
358 // the number of items has changed, hence the scrollbar may disappear
359 m_updateScrollbarY
= TRUE
;
361 // finally, if the longest item was deleted the scrollbar may disappear
362 if ( n
== m_maxWidthItem
)
364 RefreshHorzScrollbar();
368 // ----------------------------------------------------------------------------
369 // client data handling
370 // ----------------------------------------------------------------------------
372 void wxListBox::DoSetItemClientData(int n
, void* clientData
)
374 m_itemsClientData
[n
] = clientData
;
377 void *wxListBox::DoGetItemClientData(int n
) const
379 return m_itemsClientData
[n
];
382 void wxListBox::DoSetItemClientObject(int n
, wxClientData
* clientData
)
384 m_itemsClientData
[n
] = clientData
;
387 wxClientData
* wxListBox::DoGetItemClientObject(int n
) const
389 return (wxClientData
*)m_itemsClientData
[n
];
392 // ----------------------------------------------------------------------------
394 // ----------------------------------------------------------------------------
396 void wxListBox::SetSelection(int n
, bool select
)
400 if ( m_selections
.Index(n
) == wxNOT_FOUND
)
402 if ( !HasMultipleSelection() )
404 // selecting an item in a single selection listbox deselects
413 //else: already selected
417 int index
= m_selections
.Index(n
);
418 if ( index
!= wxNOT_FOUND
)
420 m_selections
.RemoveAt(index
);
427 // sanity check: a single selection listbox can't have more than one item
429 wxASSERT_MSG( HasMultipleSelection() || (m_selections
.GetCount() < 2),
430 _T("multiple selected items in single selection lbox?") );
434 // the newly selected item becomes the current one
439 int wxListBox::GetSelection() const
441 wxCHECK_MSG( !HasMultipleSelection(), -1,
442 _T("use wxListBox::GetSelections for ths listbox") );
444 return m_selections
.IsEmpty() ? -1 : m_selections
[0];
447 int wxCMPFUNC_CONV
wxCompareInts(int *n
, int *m
)
452 int wxListBox::GetSelections(wxArrayInt
& selections
) const
454 // always return sorted array to the user
455 selections
= m_selections
;
456 size_t count
= m_selections
.GetCount();
458 // don't call sort on an empty array
461 selections
.Sort(wxCompareInts
);
467 // ----------------------------------------------------------------------------
468 // refresh logic: we use delayed refreshing which allows to avoid multiple
469 // refreshes (and hence flicker) in case when several listbox items are
470 // added/deleted/changed subsequently
471 // ----------------------------------------------------------------------------
473 void wxListBox::RefreshFromItemToEnd(int from
)
475 RefreshItems(from
, GetCount() - from
);
478 void wxListBox::RefreshItems(int from
, int count
)
480 switch ( m_updateCount
)
484 m_updateCount
= count
;
488 // we refresh everything anyhow
492 // add these items to the others which we have to refresh
493 if ( m_updateFrom
< from
)
495 count
+= from
- m_updateFrom
;
496 if ( m_updateCount
< count
)
497 m_updateCount
= count
;
499 else // m_updateFrom >= from
501 int updateLast
= wxMax(m_updateFrom
+ m_updateCount
,
504 m_updateCount
= updateLast
- m_updateFrom
;
509 void wxListBox::RefreshItem(int n
)
511 switch ( m_updateCount
)
514 // refresh this item only
520 // we refresh everything anyhow
524 // add this item to the others which we have to refresh
525 if ( m_updateFrom
< n
)
527 if ( m_updateCount
< n
- m_updateFrom
+ 1 )
528 m_updateCount
= n
- m_updateFrom
+ 1;
530 else // n <= m_updateFrom
532 m_updateCount
+= m_updateFrom
- n
;
538 void wxListBox::RefreshAll()
543 void wxListBox::RefreshHorzScrollbar()
545 m_maxWidth
= 0; // recalculate it
546 m_updateScrollbarX
= TRUE
;
549 void wxListBox::UpdateScrollbars()
551 wxSize size
= GetClientSize();
553 // is our height enough to show all items?
554 int nLines
= GetCount();
555 wxCoord lineHeight
= GetLineHeight();
556 bool showScrollbarY
= nLines
*lineHeight
> size
.y
;
558 // check the width too if required
559 wxCoord charWidth
, maxWidth
;
561 if ( HasHorzScrollbar() )
563 charWidth
= GetCharWidth();
564 maxWidth
= GetMaxWidth();
565 showScrollbarX
= maxWidth
> size
.x
;
567 else // never show it
569 charWidth
= maxWidth
= 0;
570 showScrollbarX
= FALSE
;
573 // what should be the scrollbar range now?
574 int scrollRangeX
= showScrollbarX
575 ? (maxWidth
+ charWidth
- 1) / charWidth
+ 2 // FIXME
577 int scrollRangeY
= showScrollbarY
579 (size
.y
% lineHeight
+ lineHeight
- 1) / lineHeight
582 // reset scrollbars if something changed: either the visibility status
583 // or the range of a scrollbar which is shown
584 if ( (showScrollbarY
!= m_showScrollbarY
) ||
585 (showScrollbarX
!= m_showScrollbarX
) ||
586 (showScrollbarY
&& (scrollRangeY
!= m_scrollRangeY
)) ||
587 (showScrollbarX
&& (scrollRangeX
!= m_scrollRangeX
)) )
590 GetViewStart(&x
, &y
);
591 SetScrollbars(charWidth
, lineHeight
,
592 scrollRangeX
, scrollRangeY
,
595 m_showScrollbarX
= showScrollbarX
;
596 m_showScrollbarY
= showScrollbarY
;
598 m_scrollRangeX
= scrollRangeX
;
599 m_scrollRangeY
= scrollRangeY
;
603 void wxListBox::UpdateItems()
605 // only refresh the items which must be refreshed
606 if ( m_updateCount
== -1 )
609 wxLogTrace(_T("listbox"), _T("Refreshing all"));
615 wxSize size
= GetClientSize();
618 rect
.height
= size
.y
;
619 rect
.y
+= m_updateFrom
*GetLineHeight();
620 rect
.height
= m_updateCount
*GetLineHeight();
622 // we don't need to calculate x position as we always refresh the
624 CalcScrolledPosition(0, rect
.y
, NULL
, &rect
.y
);
626 wxLogTrace(_T("listbox"), _T("Refreshing items %d..%d (%d-%d)"),
627 m_updateFrom
, m_updateFrom
+ m_updateCount
- 1,
628 rect
.GetTop(), rect
.GetBottom());
630 Refresh(TRUE
, &rect
);
634 void wxListBox::OnInternalIdle()
636 if ( m_updateScrollbarY
|| m_updateScrollbarX
)
641 m_updateScrollbarY
= FALSE
;
644 if ( m_currentChanged
)
646 DoEnsureVisible(m_current
);
648 m_currentChanged
= FALSE
;
657 wxListBoxBase::OnInternalIdle();
660 // ----------------------------------------------------------------------------
662 // ----------------------------------------------------------------------------
664 wxBorder
wxListBox::GetDefaultBorder() const
666 return wxBORDER_SUNKEN
;
669 void wxListBox::DoDraw(wxControlRenderer
*renderer
)
671 // adjust the DC to account for scrolling
672 wxDC
& dc
= renderer
->GetDC();
674 dc
.SetFont(GetFont());
676 // get the update rect
677 wxRect rectUpdate
= GetUpdateClientRect();
680 CalcUnscrolledPosition(0, rectUpdate
.GetTop(), NULL
, &yTop
);
681 CalcUnscrolledPosition(0, rectUpdate
.GetBottom(), NULL
, &yBottom
);
683 // get the items which must be redrawn
684 wxCoord lineHeight
= GetLineHeight();
685 size_t itemFirst
= yTop
/ lineHeight
,
686 itemLast
= (yBottom
+ lineHeight
- 1) / lineHeight
,
687 itemMax
= m_strings
->GetCount();
689 if ( itemFirst
>= itemMax
)
692 if ( itemLast
> itemMax
)
696 wxLogTrace(_T("listbox"), _T("Repainting items %d..%d"),
697 itemFirst
, itemLast
);
699 DoDrawRange(renderer
, itemFirst
, itemLast
);
702 void wxListBox::DoDrawRange(wxControlRenderer
*renderer
,
703 int itemFirst
, int itemLast
)
705 renderer
->DrawItems(this, itemFirst
, itemLast
);
708 // ----------------------------------------------------------------------------
710 // ----------------------------------------------------------------------------
712 bool wxListBox::SetFont(const wxFont
& font
)
714 if ( !wxControl::SetFont(font
) )
724 void wxListBox::CalcItemsPerPage()
726 m_lineHeight
= GetRenderer()->GetListboxItemHeight(GetCharHeight());
727 m_itemsPerPage
= GetClientSize().y
/ m_lineHeight
;
730 int wxListBox::GetItemsPerPage() const
732 if ( !m_itemsPerPage
)
734 wxConstCast(this, wxListBox
)->CalcItemsPerPage();
737 return m_itemsPerPage
;
740 wxCoord
wxListBox::GetLineHeight() const
744 wxConstCast(this, wxListBox
)->CalcItemsPerPage();
750 wxCoord
wxListBox::GetMaxWidth() const
752 if ( m_maxWidth
== 0 )
754 wxListBox
*self
= wxConstCast(this, wxListBox
);
756 size_t count
= m_strings
->GetCount();
757 for ( size_t n
= 0; n
< count
; n
++ )
759 GetTextExtent((*m_strings
)[n
], &width
, NULL
);
760 if ( width
> m_maxWidth
)
762 self
->m_maxWidth
= width
;
763 self
->m_maxWidthItem
= n
;
771 void wxListBox::OnSize(wxSizeEvent
& event
)
773 // recalculate the number of items per page
776 // the scrollbars might [dis]appear
778 m_updateScrollbarY
= TRUE
;
783 void wxListBox::DoSetFirstItem(int n
)
788 void wxListBox::DoSetSize(int x
, int y
,
789 int width
, int height
,
792 if ( GetWindowStyle() & wxLB_INT_HEIGHT
)
794 // we must round up the height to an entire number of rows
796 // the client area must contain an int number of rows, so take borders
798 wxRect rectBorders
= GetRenderer()->GetBorderDimensions(GetBorder());
799 wxCoord hBorders
= rectBorders
.y
+ rectBorders
.height
;
801 wxCoord hLine
= GetLineHeight();
802 height
= ((height
- hBorders
+ hLine
- 1) / hLine
)*hLine
+ hBorders
;
805 wxListBoxBase::DoSetSize(x
, y
, width
, height
, sizeFlags
);
808 wxSize
wxListBox::DoGetBestClientSize() const
813 size_t count
= m_strings
->GetCount();
814 for ( size_t n
= 0; n
< count
; n
++ )
817 GetTextExtent((*m_strings
)[n
], &w
, &h
);
825 // if the listbox is empty, still give it some non zero (even if
826 // arbitrary) size - otherwise, leave small margin around the strings
830 width
+= 3*GetCharWidth();
833 height
= GetCharHeight();
835 // we need the height of the entire listbox, not just of one line
836 height
*= wxMax(count
, 7);
838 return wxSize(width
, height
);
841 // ----------------------------------------------------------------------------
843 // ----------------------------------------------------------------------------
845 bool wxListBox::SendEvent(wxEventType type
, int item
)
847 wxCommandEvent
event(type
, m_windowId
);
848 event
.SetEventObject(this);
850 // use the current item by default
856 // client data and string parameters only make sense if we have an item
859 if ( HasClientObjectData() )
860 event
.SetClientObject(GetClientObject(item
));
861 else if ( HasClientUntypedData() )
862 event
.SetClientData(GetClientData(item
));
864 event
.SetString(GetString(item
));
867 event
.m_commandInt
= item
;
869 return GetEventHandler()->ProcessEvent(event
);
872 void wxListBox::SetCurrentItem(int n
)
874 if ( n
!= m_current
)
876 if ( m_current
!= -1 )
877 RefreshItem(m_current
);
881 if ( m_current
!= -1 )
883 m_currentChanged
= TRUE
;
885 RefreshItem(m_current
);
888 //else: nothing to do
891 bool wxListBox::FindItem(const wxString
& prefix
, bool strictlyAfter
)
893 int count
= GetCount();
896 // empty listbox, we can't find anything in it
900 // start either from the current item or from the next one if strictlyAfter
905 // the following line will set first correctly to 0 if there is no
906 // selection (m_current == -1)
907 first
= m_current
== count
- 1 ? 0 : m_current
+ 1;
909 else // start with the current
911 first
= m_current
== -1 ? 0 : m_current
;
914 int last
= first
== 0 ? count
- 1 : first
- 1;
916 // if this is not true we'd never exit from the loop below!
917 wxASSERT_MSG( first
< count
&& last
< count
, _T("logic error") );
919 // precompute it outside the loop
920 size_t len
= prefix
.length();
922 // loop over all items in the listbox
923 for ( int item
= first
; item
!= last
; item
< count
- 1 ? item
++ : item
= 0 )
925 if ( wxStrnicmp((*m_strings
)[item
], prefix
, len
) == 0 )
927 SetCurrentItem(item
);
929 if ( !(GetWindowStyle() & wxLB_MULTIPLE
) )
932 SelectAndNotify(item
);
934 if ( GetWindowStyle() & wxLB_EXTENDED
)
935 AnchorSelection(item
);
946 void wxListBox::EnsureVisible(int n
)
948 if ( m_updateScrollbarY
)
953 m_updateScrollbarY
= FALSE
;
959 void wxListBox::DoEnsureVisible(int n
)
961 if ( !m_showScrollbarY
)
963 // nothing to do - everything is shown anyhow
968 GetViewStart(0, &first
);
971 // we need to scroll upwards, so make the current item appear on top
972 // of the shown range
977 int last
= first
+ GetClientSize().y
/ GetLineHeight() - 1;
980 // scroll down: the current item appears at the bottom of the
982 Scroll(0, n
- (last
- first
));
987 void wxListBox::ChangeCurrent(int diff
)
989 int current
= m_current
== -1 ? 0 : m_current
;
993 int last
= GetCount() - 1;
996 else if ( current
> last
)
999 SetCurrentItem(current
);
1002 void wxListBox::ExtendSelection(int itemTo
)
1004 // if we don't have the explicit values for selection start/end, make them
1006 if ( m_selAnchor
== -1 )
1007 m_selAnchor
= m_current
;
1012 // swap the start/end of selection range if necessary
1013 int itemFrom
= m_selAnchor
;
1014 if ( itemFrom
> itemTo
)
1016 int itemTmp
= itemFrom
;
1021 // the selection should now include all items in the range between the
1022 // anchor and the specified item and only them
1025 for ( n
= 0; n
< itemFrom
; n
++ )
1030 for ( ; n
<= itemTo
; n
++ )
1035 int count
= GetCount();
1036 for ( ; n
< count
; n
++ )
1042 void wxListBox::DoSelect(int item
, bool sel
)
1046 // go to this item first
1047 SetCurrentItem(item
);
1050 // the current item is the one we want to change: either it was just
1051 // changed above to be the same as item or item == -1 in which we case we
1052 // are supposed to use the current one anyhow
1053 if ( m_current
!= -1 )
1056 SetSelection(m_current
, sel
);
1060 void wxListBox::SelectAndNotify(int item
)
1064 SendEvent(wxEVT_COMMAND_LISTBOX_SELECTED
);
1067 void wxListBox::Activate(int item
)
1070 SetCurrentItem(item
);
1074 if ( !(GetWindowStyle() & wxLB_MULTIPLE
) )
1083 SendEvent(wxEVT_COMMAND_LISTBOX_DOUBLECLICKED
);
1087 // ----------------------------------------------------------------------------
1089 // ----------------------------------------------------------------------------
1092 The numArg here is the listbox item index while the strArg is used
1093 differently for the different actions:
1095 a) for wxACTION_LISTBOX_FIND it has the natural meaning: this is the string
1098 b) for wxACTION_LISTBOX_SELECT and wxACTION_LISTBOX_EXTENDSEL it is used
1099 to decide if the listbox should send the notification event (it is empty)
1100 or not (it is not): this allows us to reuse the same action for when the
1101 user is dragging the mouse when it has been released although in the
1102 first case no notification is sent while in the second it is sent.
1104 bool wxListBox::PerformAction(const wxControlAction
& action
,
1106 const wxString
& strArg
)
1108 int item
= (int)numArg
;
1110 if ( action
== wxACTION_LISTBOX_SETFOCUS
)
1112 SetCurrentItem(item
);
1114 else if ( action
== wxACTION_LISTBOX_ACTIVATE
)
1118 else if ( action
== wxACTION_LISTBOX_TOGGLE
)
1123 if ( IsSelected(item
) )
1126 SelectAndNotify(item
);
1128 else if ( action
== wxACTION_LISTBOX_SELECT
)
1132 if ( strArg
.empty() )
1133 SelectAndNotify(item
);
1137 else if ( action
== wxACTION_LISTBOX_SELECTADD
)
1139 else if ( action
== wxACTION_LISTBOX_UNSELECT
)
1141 else if ( action
== wxACTION_LISTBOX_MOVEDOWN
)
1143 else if ( action
== wxACTION_LISTBOX_MOVEUP
)
1145 else if ( action
== wxACTION_LISTBOX_PAGEDOWN
)
1146 ChangeCurrent(GetItemsPerPage());
1147 else if ( action
== wxACTION_LISTBOX_PAGEUP
)
1148 ChangeCurrent(-GetItemsPerPage());
1149 else if ( action
== wxACTION_LISTBOX_START
)
1151 else if ( action
== wxACTION_LISTBOX_END
)
1152 SetCurrentItem(GetCount() - 1);
1153 else if ( action
== wxACTION_LISTBOX_UNSELECTALL
)
1155 else if ( action
== wxACTION_LISTBOX_EXTENDSEL
)
1156 ExtendSelection(item
);
1157 else if ( action
== wxACTION_LISTBOX_FIND
)
1158 FindNextItem(strArg
);
1159 else if ( action
== wxACTION_LISTBOX_ANCHOR
)
1160 AnchorSelection(item
== -1 ? m_current
: item
);
1161 else if ( action
== wxACTION_LISTBOX_SELECTALL
||
1162 action
== wxACTION_LISTBOX_SELTOGGLE
)
1163 wxFAIL_MSG(_T("unimplemented yet"));
1165 return wxControl::PerformAction(action
, numArg
, strArg
);
1170 // ============================================================================
1171 // implementation of wxStdListboxInputHandler
1172 // ============================================================================
1174 wxStdListboxInputHandler::wxStdListboxInputHandler(wxInputHandler
*handler
,
1175 bool toggleOnPressAlways
)
1176 : wxStdInputHandler(handler
)
1179 m_toggleOnPressAlways
= toggleOnPressAlways
;
1180 m_actionMouse
= wxACTION_NONE
;
1181 m_trackMouseOutside
= TRUE
;
1184 int wxStdListboxInputHandler::HitTest(const wxListBox
*lbox
,
1185 const wxMouseEvent
& event
)
1187 int item
= HitTestUnsafe(lbox
, event
);
1189 return FixItemIndex(lbox
, item
);
1192 int wxStdListboxInputHandler::HitTestUnsafe(const wxListBox
*lbox
,
1193 const wxMouseEvent
& event
)
1195 wxPoint pt
= event
.GetPosition();
1196 pt
-= lbox
->GetClientAreaOrigin();
1198 lbox
->CalcUnscrolledPosition(0, pt
.y
, NULL
, &y
);
1199 return y
/ lbox
->GetLineHeight();
1202 int wxStdListboxInputHandler::FixItemIndex(const wxListBox
*lbox
,
1207 // mouse is above the first item
1210 else if ( item
>= lbox
->GetCount() )
1212 // mouse is below the last item
1213 item
= lbox
->GetCount() - 1;
1219 bool wxStdListboxInputHandler::IsValidIndex(const wxListBox
*lbox
, int item
)
1221 return item
>= 0 && item
< lbox
->GetCount();
1225 wxStdListboxInputHandler::SetupCapture(wxListBox
*lbox
,
1226 const wxMouseEvent
& event
,
1229 // we currently only allow selecting with the left mouse button, if we
1230 // do need to allow using other buttons too we might use the code
1233 m_btnCapture
= event
.LeftDown()
1242 wxControlAction action
;
1243 if ( lbox
->HasMultipleSelection() )
1245 if ( lbox
->GetWindowStyle() & wxLB_MULTIPLE
)
1247 if ( m_toggleOnPressAlways
)
1249 // toggle the item right now
1250 action
= wxACTION_LISTBOX_TOGGLE
;
1254 m_actionMouse
= wxACTION_LISTBOX_SETFOCUS
;
1256 else // wxLB_EXTENDED listbox
1258 // simple click in an extended sel listbox clears the old
1259 // selection and adds the clicked item to it then, ctrl-click
1260 // toggles an item to it and shift-click adds a range between
1261 // the old selection anchor and the clicked item
1262 if ( event
.ControlDown() )
1264 lbox
->PerformAction(wxACTION_LISTBOX_ANCHOR
, item
);
1266 action
= wxACTION_LISTBOX_TOGGLE
;
1268 else if ( event
.ShiftDown() )
1270 action
= wxACTION_LISTBOX_EXTENDSEL
;
1272 else // simple click
1274 lbox
->PerformAction(wxACTION_LISTBOX_ANCHOR
, item
);
1276 action
= wxACTION_LISTBOX_SELECT
;
1279 m_actionMouse
= wxACTION_LISTBOX_EXTENDSEL
;
1282 else // single selection
1285 action
= wxACTION_LISTBOX_SELECT
;
1288 // by default we always do track it
1289 m_trackMouseOutside
= TRUE
;
1294 bool wxStdListboxInputHandler::HandleKey(wxInputConsumer
*consumer
,
1295 const wxKeyEvent
& event
,
1298 // we're only interested in the key press events
1299 if ( pressed
&& !event
.AltDown() )
1301 bool isMoveCmd
= TRUE
;
1302 int style
= consumer
->GetInputWindow()->GetWindowStyle();
1304 wxControlAction action
;
1307 int keycode
= event
.GetKeyCode();
1312 action
= wxACTION_LISTBOX_MOVEUP
;
1316 action
= wxACTION_LISTBOX_MOVEDOWN
;
1322 action
= wxACTION_LISTBOX_PAGEUP
;
1328 action
= wxACTION_LISTBOX_PAGEDOWN
;
1332 action
= wxACTION_LISTBOX_START
;
1336 action
= wxACTION_LISTBOX_END
;
1341 if ( style
& wxLB_MULTIPLE
)
1343 action
= wxACTION_LISTBOX_TOGGLE
;
1349 action
= wxACTION_LISTBOX_ACTIVATE
;
1354 if ( (keycode
< 255) && wxIsalnum(keycode
) )
1356 action
= wxACTION_LISTBOX_FIND
;
1357 strArg
= (wxChar
)keycode
;
1363 consumer
->PerformAction(action
, -1, strArg
);
1367 if ( style
& wxLB_SINGLE
)
1369 // the current item is always the one selected
1370 consumer
->PerformAction(wxACTION_LISTBOX_SELECT
);
1372 else if ( style
& wxLB_EXTENDED
)
1374 if ( event
.ShiftDown() )
1375 consumer
->PerformAction(wxACTION_LISTBOX_EXTENDSEL
);
1378 // select the item and make it the new selection anchor
1379 consumer
->PerformAction(wxACTION_LISTBOX_SELECT
);
1380 consumer
->PerformAction(wxACTION_LISTBOX_ANCHOR
);
1383 //else: nothing to do for multiple selection listboxes
1390 return wxStdInputHandler::HandleKey(consumer
, event
, pressed
);
1393 bool wxStdListboxInputHandler::HandleMouse(wxInputConsumer
*consumer
,
1394 const wxMouseEvent
& event
)
1396 wxListBox
*lbox
= wxStaticCast(consumer
->GetInputWindow(), wxListBox
);
1397 int item
= HitTest(lbox
, event
);
1398 wxControlAction action
;
1400 // when the left mouse button is pressed, capture the mouse and track the
1401 // item under mouse (if the mouse leaves the window, we will still be
1402 // getting the mouse move messages generated by wxScrollWindow)
1403 if ( event
.LeftDown() )
1405 // capture the mouse to track the selected item
1406 lbox
->CaptureMouse();
1408 action
= SetupCapture(lbox
, event
, item
);
1410 else if ( m_btnCapture
&& event
.ButtonUp(m_btnCapture
) )
1412 // when the left mouse button is released, release the mouse too
1413 wxWindow
*winCapture
= wxWindow::GetCapture();
1416 winCapture
->ReleaseMouse();
1419 action
= m_actionMouse
;
1421 //else: the mouse wasn't presed over the listbox, only released here
1423 else if ( event
.LeftDClick() )
1425 action
= wxACTION_LISTBOX_ACTIVATE
;
1430 lbox
->PerformAction(action
, item
);
1435 return wxStdInputHandler::HandleMouse(consumer
, event
);
1438 bool wxStdListboxInputHandler::HandleMouseMove(wxInputConsumer
*consumer
,
1439 const wxMouseEvent
& event
)
1441 wxWindow
*winCapture
= wxWindow::GetCapture();
1442 if ( winCapture
&& (event
.GetEventObject() == winCapture
) )
1444 wxListBox
*lbox
= wxStaticCast(consumer
->GetInputWindow(), wxListBox
);
1446 if ( !m_btnCapture
|| !m_trackMouseOutside
)
1448 // someone captured the mouse for us (we always set m_btnCapture
1449 // when we do it ourselves): in this case we only react to
1450 // the mouse messages when they happen inside the listbox
1451 if ( lbox
->HitTest(event
.GetPosition()) != wxHT_WINDOW_INSIDE
)
1455 int item
= HitTest(lbox
, event
);
1456 if ( !m_btnCapture
)
1458 // now that we have the mouse inside the listbox, do capture it
1459 // normally - but ensure that we will still ignore the outside
1461 SetupCapture(lbox
, event
, item
);
1463 m_trackMouseOutside
= FALSE
;
1466 if ( IsValidIndex(lbox
, item
) )
1468 // pass something into strArg to tell the listbox that it shouldn't
1469 // send the notification message: see PerformAction() above
1470 lbox
->PerformAction(m_actionMouse
, item
, _T("no"));
1472 // else: don't pass invalid index to the listbox
1474 else // we don't have capture any more
1478 // if we lost capture unexpectedly (someone else took the capture
1479 // from us), return to a consistent state
1484 return wxStdInputHandler::HandleMouseMove(consumer
, event
);
1487 #endif // wxUSE_LISTBOX