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 wxListBox::wxListBox()
63 wxListBox::wxListBox(wxWindow
*parent
,
68 const wxString choices
[],
70 const wxValidator
& validator
,
75 Create(parent
, id
, pos
, size
, n
, choices
, style
, validator
, name
);
78 void wxListBox::Init()
80 // will be calculated later when needed
88 // no items hence no current item
91 m_currentChanged
= FALSE
;
93 // no need to update anything initially
96 // no scrollbars to show nor update
100 m_showScrollbarY
= FALSE
;
103 bool wxListBox::Create(wxWindow
*parent
,
108 const wxString choices
[],
110 const wxValidator
& validator
,
111 const wxString
&name
)
113 // for compatibility accept both the new and old styles - they mean the
115 if ( style
& wxLB_ALWAYS_SB
)
116 style
|= wxALWAYS_SHOW_SB
;
118 // if we don't have neither multiple nor extended flag, we must have the
119 // single selection listbox
120 if ( !(style
& (wxLB_MULTIPLE
| wxLB_EXTENDED
)) )
121 style
|= wxLB_SINGLE
;
123 #if wxUSE_TWO_WINDOWS
124 style
|= wxVSCROLL
|wxHSCROLL
;
125 if ((style
& wxBORDER_MASK
) == 0)
126 style
|= wxBORDER_SUNKEN
;
129 if ( !wxControl::Create(parent
, id
, pos
, size
, style
,
135 if ( style
& wxLB_SORT
)
136 m_stringsSorted
= new wxSortedArrayString
;
138 m_strings
= new wxArrayString
;
144 CreateInputHandler(wxINP_HANDLER_LISTBOX
);
149 wxListBox::~wxListBox()
151 // call this just to free the client data -- and avoid leaking memory
155 delete m_stringsSorted
;
162 // ----------------------------------------------------------------------------
163 // adding/inserting strings
164 // ----------------------------------------------------------------------------
166 int wxListBox::DoAppend(const wxString
& item
)
172 index
= m_stringsSorted
->Add(item
);
176 index
= m_strings
->GetCount();
177 m_strings
->Add(item
);
180 m_itemsClientData
.Insert(NULL
, index
);
182 m_updateScrollbarY
= TRUE
;
184 if ( HasHorzScrollbar() )
186 // has the max width increased?
188 GetTextExtent(item
, &width
, NULL
);
189 if ( width
> m_maxWidth
)
192 m_maxWidthItem
= index
;
193 m_updateScrollbarX
= TRUE
;
197 RefreshFromItemToEnd(index
);
202 void wxListBox::DoInsertItems(const wxArrayString
& items
, int pos
)
204 // the position of the item being added to a sorted listbox can't be
206 wxCHECK_RET( !IsSorted(), _T("can't insert items into sorted listbox") );
208 size_t count
= items
.GetCount();
209 for ( size_t n
= 0; n
< count
; n
++ )
211 m_strings
->Insert(items
[n
], pos
+ n
);
212 m_itemsClientData
.Insert(NULL
, pos
+ n
);
215 // the number of items has changed so we might have to show the scrollbar
216 m_updateScrollbarY
= TRUE
;
218 // the max width also might have changed - just recalculate it instead of
219 // keeping track of it here, this is probably more efficient for a typical
221 RefreshHorzScrollbar();
223 // note that we have to refresh all the items after the ones we inserted,
224 // not just these items
225 RefreshFromItemToEnd(pos
);
228 void wxListBox::DoSetItems(const wxArrayString
& items
, void **clientData
)
232 size_t count
= items
.GetCount();
236 m_strings
->Alloc(count
);
237 m_itemsClientData
.Alloc(count
);
238 for ( size_t n
= 0; n
< count
; n
++ )
244 index
= m_stringsSorted
->Add(items
[n
]);
248 index
= m_strings
->GetCount();
249 m_strings
->Add(items
[n
]);
252 m_itemsClientData
.Insert(clientData
? clientData
[n
] : NULL
, index
);
255 m_updateScrollbarY
= TRUE
;
260 void wxListBox::SetString(int n
, const wxString
& s
)
262 if ( HasHorzScrollbar() )
264 // we need to update m_maxWidth as changing the string may cause the
265 // horz scrollbar [dis]appear
268 GetTextExtent(s
, &width
, NULL
);
270 // it might have increased if the new string is long
271 if ( width
> m_maxWidth
)
275 m_updateScrollbarX
= TRUE
;
277 // or also decreased if the old string was the longest one
278 else if ( n
== m_maxWidthItem
)
280 RefreshHorzScrollbar();
283 else // no horz scrollbar
291 // ----------------------------------------------------------------------------
293 // ----------------------------------------------------------------------------
295 void wxListBox::DoClear()
299 if ( HasClientObjectData() )
301 size_t count
= m_itemsClientData
.GetCount();
302 for ( size_t n
= 0; n
< count
; n
++ )
304 delete (wxClientData
*) m_itemsClientData
[n
];
308 m_itemsClientData
.Clear();
309 m_selections
.Clear();
314 void wxListBox::Clear()
318 m_updateScrollbarY
= TRUE
;
320 RefreshHorzScrollbar();
325 void wxListBox::Delete(int n
)
327 wxCHECK_RET( n
>= 0 && n
< GetCount(),
328 _T("invalid index in wxListBox::Delete") );
330 // do it before removing the index as otherwise the last item will not be
331 // refreshed (as GetCount() will be decremented)
332 RefreshFromItemToEnd(n
);
334 m_strings
->RemoveAt(n
);
336 if ( HasClientObjectData() )
338 delete (wxClientData
*)m_itemsClientData
[n
];
341 m_itemsClientData
.RemoveAt(n
);
343 // when the item disappears we must not keep using its index
344 if ( n
== m_current
)
348 else if ( n
< m_current
)
352 //else: current item may stay
354 // update the selections array: the indices of all seletected items after
355 // the one being deleted must change and the item itselfm ust be removed
356 int index
= wxNOT_FOUND
;
357 size_t count
= m_selections
.GetCount();
358 for ( size_t item
= 0; item
< count
; item
++ )
360 if ( m_selections
[item
] == n
)
362 // remember to delete it later
365 else if ( m_selections
[item
] > n
)
367 // to account for the index shift
368 m_selections
[item
]--;
370 //else: nothing changed for this one
373 if ( index
!= wxNOT_FOUND
)
375 m_selections
.RemoveAt(index
);
378 // the number of items has changed, hence the scrollbar may disappear
379 m_updateScrollbarY
= TRUE
;
381 // finally, if the longest item was deleted the scrollbar may disappear
382 if ( n
== m_maxWidthItem
)
384 RefreshHorzScrollbar();
388 // ----------------------------------------------------------------------------
389 // client data handling
390 // ----------------------------------------------------------------------------
392 void wxListBox::DoSetItemClientData(int n
, void* clientData
)
394 m_itemsClientData
[n
] = clientData
;
397 void *wxListBox::DoGetItemClientData(int n
) const
399 return m_itemsClientData
[n
];
402 void wxListBox::DoSetItemClientObject(int n
, wxClientData
* clientData
)
404 m_itemsClientData
[n
] = clientData
;
407 wxClientData
* wxListBox::DoGetItemClientObject(int n
) const
409 return (wxClientData
*)m_itemsClientData
[n
];
412 // ----------------------------------------------------------------------------
414 // ----------------------------------------------------------------------------
416 void wxListBox::SetSelection(int n
, bool select
)
420 if ( m_selections
.Index(n
) == wxNOT_FOUND
)
422 if ( !HasMultipleSelection() )
424 // selecting an item in a single selection listbox deselects
433 //else: already selected
437 int index
= m_selections
.Index(n
);
438 if ( index
!= wxNOT_FOUND
)
440 m_selections
.RemoveAt(index
);
447 // sanity check: a single selection listbox can't have more than one item
449 wxASSERT_MSG( HasMultipleSelection() || (m_selections
.GetCount() < 2),
450 _T("multiple selected items in single selection lbox?") );
454 // the newly selected item becomes the current one
459 int wxListBox::GetSelection() const
461 wxCHECK_MSG( !HasMultipleSelection(), -1,
462 _T("use wxListBox::GetSelections for ths listbox") );
464 return m_selections
.IsEmpty() ? -1 : m_selections
[0];
467 int wxCMPFUNC_CONV
wxCompareInts(int *n
, int *m
)
472 int wxListBox::GetSelections(wxArrayInt
& selections
) const
474 // always return sorted array to the user
475 selections
= m_selections
;
476 size_t count
= m_selections
.GetCount();
478 // don't call sort on an empty array
481 selections
.Sort(wxCompareInts
);
487 // ----------------------------------------------------------------------------
488 // refresh logic: we use delayed refreshing which allows to avoid multiple
489 // refreshes (and hence flicker) in case when several listbox items are
490 // added/deleted/changed subsequently
491 // ----------------------------------------------------------------------------
493 void wxListBox::RefreshFromItemToEnd(int from
)
495 RefreshItems(from
, GetCount() - from
);
498 void wxListBox::RefreshItems(int from
, int count
)
500 switch ( m_updateCount
)
504 m_updateCount
= count
;
508 // we refresh everything anyhow
512 // add these items to the others which we have to refresh
513 if ( m_updateFrom
< from
)
515 count
+= from
- m_updateFrom
;
516 if ( m_updateCount
< count
)
517 m_updateCount
= count
;
519 else // m_updateFrom >= from
521 int updateLast
= wxMax(m_updateFrom
+ m_updateCount
,
524 m_updateCount
= updateLast
- m_updateFrom
;
529 void wxListBox::RefreshItem(int n
)
531 switch ( m_updateCount
)
534 // refresh this item only
540 // we refresh everything anyhow
544 // add this item to the others which we have to refresh
545 if ( m_updateFrom
< n
)
547 if ( m_updateCount
< n
- m_updateFrom
+ 1 )
548 m_updateCount
= n
- m_updateFrom
+ 1;
550 else // n <= m_updateFrom
552 m_updateCount
+= m_updateFrom
- n
;
558 void wxListBox::RefreshAll()
563 void wxListBox::RefreshHorzScrollbar()
565 m_maxWidth
= 0; // recalculate it
566 m_updateScrollbarX
= TRUE
;
569 void wxListBox::UpdateScrollbars()
571 wxSize size
= GetClientSize();
573 // is our height enough to show all items?
574 int nLines
= GetCount();
575 wxCoord lineHeight
= GetLineHeight();
576 bool showScrollbarY
= nLines
*lineHeight
> size
.y
;
578 // check the width too if required
579 wxCoord charWidth
, maxWidth
;
581 if ( HasHorzScrollbar() )
583 charWidth
= GetCharWidth();
584 maxWidth
= GetMaxWidth();
585 showScrollbarX
= maxWidth
> size
.x
;
587 else // never show it
589 charWidth
= maxWidth
= 0;
590 showScrollbarX
= FALSE
;
593 // what should be the scrollbar range now?
594 int scrollRangeX
= showScrollbarX
595 ? (maxWidth
+ charWidth
- 1) / charWidth
+ 2 // FIXME
597 int scrollRangeY
= showScrollbarY
599 (size
.y
% lineHeight
+ lineHeight
- 1) / lineHeight
602 // reset scrollbars if something changed: either the visibility status
603 // or the range of a scrollbar which is shown
604 if ( (showScrollbarY
!= m_showScrollbarY
) ||
605 (showScrollbarX
!= m_showScrollbarX
) ||
606 (showScrollbarY
&& (scrollRangeY
!= m_scrollRangeY
)) ||
607 (showScrollbarX
&& (scrollRangeX
!= m_scrollRangeX
)) )
610 GetViewStart(&x
, &y
);
611 SetScrollbars(charWidth
, lineHeight
,
612 scrollRangeX
, scrollRangeY
,
615 m_showScrollbarX
= showScrollbarX
;
616 m_showScrollbarY
= showScrollbarY
;
618 m_scrollRangeX
= scrollRangeX
;
619 m_scrollRangeY
= scrollRangeY
;
623 void wxListBox::UpdateItems()
625 // only refresh the items which must be refreshed
626 if ( m_updateCount
== -1 )
629 wxLogTrace(_T("listbox"), _T("Refreshing all"));
635 wxSize size
= GetClientSize();
638 rect
.height
= size
.y
;
639 rect
.y
+= m_updateFrom
*GetLineHeight();
640 rect
.height
= m_updateCount
*GetLineHeight();
642 // we don't need to calculate x position as we always refresh the
644 CalcScrolledPosition(0, rect
.y
, NULL
, &rect
.y
);
646 wxLogTrace(_T("listbox"), _T("Refreshing items %d..%d (%d-%d)"),
647 m_updateFrom
, m_updateFrom
+ m_updateCount
- 1,
648 rect
.GetTop(), rect
.GetBottom());
650 Refresh(TRUE
, &rect
);
654 void wxListBox::OnInternalIdle()
656 if ( m_updateScrollbarY
|| m_updateScrollbarX
)
661 m_updateScrollbarY
= FALSE
;
664 if ( m_currentChanged
)
666 DoEnsureVisible(m_current
);
668 m_currentChanged
= FALSE
;
677 wxListBoxBase::OnInternalIdle();
680 // ----------------------------------------------------------------------------
682 // ----------------------------------------------------------------------------
684 wxBorder
wxListBox::GetDefaultBorder() const
686 return wxBORDER_SUNKEN
;
689 void wxListBox::DoDraw(wxControlRenderer
*renderer
)
691 // adjust the DC to account for scrolling
692 wxDC
& dc
= renderer
->GetDC();
694 dc
.SetFont(GetFont());
696 // get the update rect
697 wxRect rectUpdate
= GetUpdateClientRect();
700 CalcUnscrolledPosition(0, rectUpdate
.GetTop(), NULL
, &yTop
);
701 CalcUnscrolledPosition(0, rectUpdate
.GetBottom(), NULL
, &yBottom
);
703 // get the items which must be redrawn
704 wxCoord lineHeight
= GetLineHeight();
705 size_t itemFirst
= yTop
/ lineHeight
,
706 itemLast
= (yBottom
+ lineHeight
- 1) / lineHeight
,
707 itemMax
= m_strings
->GetCount();
709 if ( itemFirst
>= itemMax
)
712 if ( itemLast
> itemMax
)
716 wxLogTrace(_T("listbox"), _T("Repainting items %d..%d"),
717 itemFirst
, itemLast
);
719 DoDrawRange(renderer
, itemFirst
, itemLast
);
722 void wxListBox::DoDrawRange(wxControlRenderer
*renderer
,
723 int itemFirst
, int itemLast
)
725 renderer
->DrawItems(this, itemFirst
, itemLast
);
728 // ----------------------------------------------------------------------------
730 // ----------------------------------------------------------------------------
732 bool wxListBox::SetFont(const wxFont
& font
)
734 if ( !wxControl::SetFont(font
) )
744 void wxListBox::CalcItemsPerPage()
746 m_lineHeight
= GetRenderer()->GetListboxItemHeight(GetCharHeight());
747 m_itemsPerPage
= GetClientSize().y
/ m_lineHeight
;
750 int wxListBox::GetItemsPerPage() const
752 if ( !m_itemsPerPage
)
754 wxConstCast(this, wxListBox
)->CalcItemsPerPage();
757 return m_itemsPerPage
;
760 wxCoord
wxListBox::GetLineHeight() const
764 wxConstCast(this, wxListBox
)->CalcItemsPerPage();
770 wxCoord
wxListBox::GetMaxWidth() const
772 if ( m_maxWidth
== 0 )
774 wxListBox
*self
= wxConstCast(this, wxListBox
);
776 size_t count
= m_strings
->GetCount();
777 for ( size_t n
= 0; n
< count
; n
++ )
779 GetTextExtent((*m_strings
)[n
], &width
, NULL
);
780 if ( width
> m_maxWidth
)
782 self
->m_maxWidth
= width
;
783 self
->m_maxWidthItem
= n
;
791 void wxListBox::OnSize(wxSizeEvent
& event
)
793 // recalculate the number of items per page
796 // the scrollbars might [dis]appear
798 m_updateScrollbarY
= TRUE
;
803 void wxListBox::DoSetFirstItem(int n
)
808 void wxListBox::DoSetSize(int x
, int y
,
809 int width
, int height
,
812 if ( GetWindowStyle() & wxLB_INT_HEIGHT
)
814 // we must round up the height to an entire number of rows
816 // the client area must contain an int number of rows, so take borders
818 wxRect rectBorders
= GetRenderer()->GetBorderDimensions(GetBorder());
819 wxCoord hBorders
= rectBorders
.y
+ rectBorders
.height
;
821 wxCoord hLine
= GetLineHeight();
822 height
= ((height
- hBorders
+ hLine
- 1) / hLine
)*hLine
+ hBorders
;
825 wxListBoxBase::DoSetSize(x
, y
, width
, height
, sizeFlags
);
828 wxSize
wxListBox::DoGetBestClientSize() const
833 size_t count
= m_strings
->GetCount();
834 for ( size_t n
= 0; n
< count
; n
++ )
837 GetTextExtent((*m_strings
)[n
], &w
, &h
);
845 // if the listbox is empty, still give it some non zero (even if
846 // arbitrary) size - otherwise, leave small margin around the strings
850 width
+= 3*GetCharWidth();
853 height
= GetCharHeight();
855 // we need the height of the entire listbox, not just of one line
856 height
*= wxMax(count
, 7);
858 return wxSize(width
, height
);
861 // ----------------------------------------------------------------------------
863 // ----------------------------------------------------------------------------
865 bool wxListBox::SendEvent(wxEventType type
, int item
)
867 wxCommandEvent
event(type
, m_windowId
);
868 event
.SetEventObject(this);
870 // use the current item by default
876 // client data and string parameters only make sense if we have an item
879 if ( HasClientObjectData() )
880 event
.SetClientObject(GetClientObject(item
));
881 else if ( HasClientUntypedData() )
882 event
.SetClientData(GetClientData(item
));
884 event
.SetString(GetString(item
));
887 event
.m_commandInt
= item
;
889 return GetEventHandler()->ProcessEvent(event
);
892 void wxListBox::SetCurrentItem(int n
)
894 if ( n
!= m_current
)
896 if ( m_current
!= -1 )
897 RefreshItem(m_current
);
901 if ( m_current
!= -1 )
903 m_currentChanged
= TRUE
;
905 RefreshItem(m_current
);
908 //else: nothing to do
911 bool wxListBox::FindItem(const wxString
& prefix
, bool strictlyAfter
)
913 int count
= GetCount();
916 // empty listbox, we can't find anything in it
920 // start either from the current item or from the next one if strictlyAfter
925 // the following line will set first correctly to 0 if there is no
926 // selection (m_current == -1)
927 first
= m_current
== count
- 1 ? 0 : m_current
+ 1;
929 else // start with the current
931 first
= m_current
== -1 ? 0 : m_current
;
934 int last
= first
== 0 ? count
- 1 : first
- 1;
936 // if this is not true we'd never exit from the loop below!
937 wxASSERT_MSG( first
< count
&& last
< count
, _T("logic error") );
939 // precompute it outside the loop
940 size_t len
= prefix
.length();
942 // loop over all items in the listbox
943 for ( int item
= first
; item
!= last
; item
< count
- 1 ? item
++ : item
= 0 )
945 if ( wxStrnicmp((*m_strings
)[item
], prefix
, len
) == 0 )
947 SetCurrentItem(item
);
949 if ( !(GetWindowStyle() & wxLB_MULTIPLE
) )
952 SelectAndNotify(item
);
954 if ( GetWindowStyle() & wxLB_EXTENDED
)
955 AnchorSelection(item
);
966 void wxListBox::EnsureVisible(int n
)
968 if ( m_updateScrollbarY
)
973 m_updateScrollbarY
= FALSE
;
979 void wxListBox::DoEnsureVisible(int n
)
981 if ( !m_showScrollbarY
)
983 // nothing to do - everything is shown anyhow
988 GetViewStart(0, &first
);
991 // we need to scroll upwards, so make the current item appear on top
992 // of the shown range
997 int last
= first
+ GetClientSize().y
/ GetLineHeight() - 1;
1000 // scroll down: the current item appears at the bottom of the
1002 Scroll(0, n
- (last
- first
));
1007 void wxListBox::ChangeCurrent(int diff
)
1009 int current
= m_current
== -1 ? 0 : m_current
;
1013 int last
= GetCount() - 1;
1016 else if ( current
> last
)
1019 SetCurrentItem(current
);
1022 void wxListBox::ExtendSelection(int itemTo
)
1024 // if we don't have the explicit values for selection start/end, make them
1026 if ( m_selAnchor
== -1 )
1027 m_selAnchor
= m_current
;
1032 // swap the start/end of selection range if necessary
1033 int itemFrom
= m_selAnchor
;
1034 if ( itemFrom
> itemTo
)
1036 int itemTmp
= itemFrom
;
1041 // the selection should now include all items in the range between the
1042 // anchor and the specified item and only them
1045 for ( n
= 0; n
< itemFrom
; n
++ )
1050 for ( ; n
<= itemTo
; n
++ )
1055 int count
= GetCount();
1056 for ( ; n
< count
; n
++ )
1062 void wxListBox::DoSelect(int item
, bool sel
)
1066 // go to this item first
1067 SetCurrentItem(item
);
1070 // the current item is the one we want to change: either it was just
1071 // changed above to be the same as item or item == -1 in which we case we
1072 // are supposed to use the current one anyhow
1073 if ( m_current
!= -1 )
1076 SetSelection(m_current
, sel
);
1080 void wxListBox::SelectAndNotify(int item
)
1084 SendEvent(wxEVT_COMMAND_LISTBOX_SELECTED
);
1087 void wxListBox::Activate(int item
)
1090 SetCurrentItem(item
);
1094 if ( !(GetWindowStyle() & wxLB_MULTIPLE
) )
1103 SendEvent(wxEVT_COMMAND_LISTBOX_DOUBLECLICKED
);
1107 // ----------------------------------------------------------------------------
1109 // ----------------------------------------------------------------------------
1112 The numArg here is the listbox item index while the strArg is used
1113 differently for the different actions:
1115 a) for wxACTION_LISTBOX_FIND it has the natural meaning: this is the string
1118 b) for wxACTION_LISTBOX_SELECT and wxACTION_LISTBOX_EXTENDSEL it is used
1119 to decide if the listbox should send the notification event (it is empty)
1120 or not (it is not): this allows us to reuse the same action for when the
1121 user is dragging the mouse when it has been released although in the
1122 first case no notification is sent while in the second it is sent.
1124 bool wxListBox::PerformAction(const wxControlAction
& action
,
1126 const wxString
& strArg
)
1128 int item
= (int)numArg
;
1130 if ( action
== wxACTION_LISTBOX_SETFOCUS
)
1132 SetCurrentItem(item
);
1134 else if ( action
== wxACTION_LISTBOX_ACTIVATE
)
1138 else if ( action
== wxACTION_LISTBOX_TOGGLE
)
1143 if ( IsSelected(item
) )
1146 SelectAndNotify(item
);
1148 else if ( action
== wxACTION_LISTBOX_SELECT
)
1152 if ( strArg
.empty() )
1153 SelectAndNotify(item
);
1157 else if ( action
== wxACTION_LISTBOX_SELECTADD
)
1159 else if ( action
== wxACTION_LISTBOX_UNSELECT
)
1161 else if ( action
== wxACTION_LISTBOX_MOVEDOWN
)
1163 else if ( action
== wxACTION_LISTBOX_MOVEUP
)
1165 else if ( action
== wxACTION_LISTBOX_PAGEDOWN
)
1166 ChangeCurrent(GetItemsPerPage());
1167 else if ( action
== wxACTION_LISTBOX_PAGEUP
)
1168 ChangeCurrent(-GetItemsPerPage());
1169 else if ( action
== wxACTION_LISTBOX_START
)
1171 else if ( action
== wxACTION_LISTBOX_END
)
1172 SetCurrentItem(GetCount() - 1);
1173 else if ( action
== wxACTION_LISTBOX_UNSELECTALL
)
1175 else if ( action
== wxACTION_LISTBOX_EXTENDSEL
)
1176 ExtendSelection(item
);
1177 else if ( action
== wxACTION_LISTBOX_FIND
)
1178 FindNextItem(strArg
);
1179 else if ( action
== wxACTION_LISTBOX_ANCHOR
)
1180 AnchorSelection(item
== -1 ? m_current
: item
);
1181 else if ( action
== wxACTION_LISTBOX_SELECTALL
||
1182 action
== wxACTION_LISTBOX_SELTOGGLE
)
1183 wxFAIL_MSG(_T("unimplemented yet"));
1185 return wxControl::PerformAction(action
, numArg
, strArg
);
1190 // ============================================================================
1191 // implementation of wxStdListboxInputHandler
1192 // ============================================================================
1194 wxStdListboxInputHandler::wxStdListboxInputHandler(wxInputHandler
*handler
,
1195 bool toggleOnPressAlways
)
1196 : wxStdInputHandler(handler
)
1199 m_toggleOnPressAlways
= toggleOnPressAlways
;
1200 m_actionMouse
= wxACTION_NONE
;
1201 m_trackMouseOutside
= TRUE
;
1204 int wxStdListboxInputHandler::HitTest(const wxListBox
*lbox
,
1205 const wxMouseEvent
& event
)
1207 int item
= HitTestUnsafe(lbox
, event
);
1209 return FixItemIndex(lbox
, item
);
1212 int wxStdListboxInputHandler::HitTestUnsafe(const wxListBox
*lbox
,
1213 const wxMouseEvent
& event
)
1215 wxPoint pt
= event
.GetPosition();
1216 pt
-= lbox
->GetClientAreaOrigin();
1218 lbox
->CalcUnscrolledPosition(0, pt
.y
, NULL
, &y
);
1219 return y
/ lbox
->GetLineHeight();
1222 int wxStdListboxInputHandler::FixItemIndex(const wxListBox
*lbox
,
1227 // mouse is above the first item
1230 else if ( item
>= lbox
->GetCount() )
1232 // mouse is below the last item
1233 item
= lbox
->GetCount() - 1;
1239 bool wxStdListboxInputHandler::IsValidIndex(const wxListBox
*lbox
, int item
)
1241 return item
>= 0 && item
< lbox
->GetCount();
1245 wxStdListboxInputHandler::SetupCapture(wxListBox
*lbox
,
1246 const wxMouseEvent
& event
,
1249 // we currently only allow selecting with the left mouse button, if we
1250 // do need to allow using other buttons too we might use the code
1253 m_btnCapture
= event
.LeftDown()
1262 wxControlAction action
;
1263 if ( lbox
->HasMultipleSelection() )
1265 if ( lbox
->GetWindowStyle() & wxLB_MULTIPLE
)
1267 if ( m_toggleOnPressAlways
)
1269 // toggle the item right now
1270 action
= wxACTION_LISTBOX_TOGGLE
;
1274 m_actionMouse
= wxACTION_LISTBOX_SETFOCUS
;
1276 else // wxLB_EXTENDED listbox
1278 // simple click in an extended sel listbox clears the old
1279 // selection and adds the clicked item to it then, ctrl-click
1280 // toggles an item to it and shift-click adds a range between
1281 // the old selection anchor and the clicked item
1282 if ( event
.ControlDown() )
1284 lbox
->PerformAction(wxACTION_LISTBOX_ANCHOR
, item
);
1286 action
= wxACTION_LISTBOX_TOGGLE
;
1288 else if ( event
.ShiftDown() )
1290 action
= wxACTION_LISTBOX_EXTENDSEL
;
1292 else // simple click
1294 lbox
->PerformAction(wxACTION_LISTBOX_ANCHOR
, item
);
1296 action
= wxACTION_LISTBOX_SELECT
;
1299 m_actionMouse
= wxACTION_LISTBOX_EXTENDSEL
;
1302 else // single selection
1305 action
= wxACTION_LISTBOX_SELECT
;
1308 // by default we always do track it
1309 m_trackMouseOutside
= TRUE
;
1314 bool wxStdListboxInputHandler::HandleKey(wxInputConsumer
*consumer
,
1315 const wxKeyEvent
& event
,
1318 // we're only interested in the key press events
1319 if ( pressed
&& !event
.AltDown() )
1321 bool isMoveCmd
= TRUE
;
1322 int style
= consumer
->GetInputWindow()->GetWindowStyle();
1324 wxControlAction action
;
1327 int keycode
= event
.GetKeyCode();
1332 action
= wxACTION_LISTBOX_MOVEUP
;
1336 action
= wxACTION_LISTBOX_MOVEDOWN
;
1342 action
= wxACTION_LISTBOX_PAGEUP
;
1348 action
= wxACTION_LISTBOX_PAGEDOWN
;
1352 action
= wxACTION_LISTBOX_START
;
1356 action
= wxACTION_LISTBOX_END
;
1361 if ( style
& wxLB_MULTIPLE
)
1363 action
= wxACTION_LISTBOX_TOGGLE
;
1369 action
= wxACTION_LISTBOX_ACTIVATE
;
1374 if ( (keycode
< 255) && wxIsalnum(keycode
) )
1376 action
= wxACTION_LISTBOX_FIND
;
1377 strArg
= (wxChar
)keycode
;
1383 consumer
->PerformAction(action
, -1, strArg
);
1387 if ( style
& wxLB_SINGLE
)
1389 // the current item is always the one selected
1390 consumer
->PerformAction(wxACTION_LISTBOX_SELECT
);
1392 else if ( style
& wxLB_EXTENDED
)
1394 if ( event
.ShiftDown() )
1395 consumer
->PerformAction(wxACTION_LISTBOX_EXTENDSEL
);
1398 // select the item and make it the new selection anchor
1399 consumer
->PerformAction(wxACTION_LISTBOX_SELECT
);
1400 consumer
->PerformAction(wxACTION_LISTBOX_ANCHOR
);
1403 //else: nothing to do for multiple selection listboxes
1410 return wxStdInputHandler::HandleKey(consumer
, event
, pressed
);
1413 bool wxStdListboxInputHandler::HandleMouse(wxInputConsumer
*consumer
,
1414 const wxMouseEvent
& event
)
1416 wxListBox
*lbox
= wxStaticCast(consumer
->GetInputWindow(), wxListBox
);
1417 int item
= HitTest(lbox
, event
);
1418 wxControlAction action
;
1420 // when the left mouse button is pressed, capture the mouse and track the
1421 // item under mouse (if the mouse leaves the window, we will still be
1422 // getting the mouse move messages generated by wxScrollWindow)
1423 if ( event
.LeftDown() )
1425 // capture the mouse to track the selected item
1426 lbox
->CaptureMouse();
1428 action
= SetupCapture(lbox
, event
, item
);
1430 else if ( m_btnCapture
&& event
.ButtonUp(m_btnCapture
) )
1432 // when the left mouse button is released, release the mouse too
1433 wxWindow
*winCapture
= wxWindow::GetCapture();
1436 winCapture
->ReleaseMouse();
1439 action
= m_actionMouse
;
1441 //else: the mouse wasn't presed over the listbox, only released here
1443 else if ( event
.LeftDClick() )
1445 action
= wxACTION_LISTBOX_ACTIVATE
;
1450 lbox
->PerformAction(action
, item
);
1455 return wxStdInputHandler::HandleMouse(consumer
, event
);
1458 bool wxStdListboxInputHandler::HandleMouseMove(wxInputConsumer
*consumer
,
1459 const wxMouseEvent
& event
)
1461 wxWindow
*winCapture
= wxWindow::GetCapture();
1462 if ( winCapture
&& (event
.GetEventObject() == winCapture
) )
1464 wxListBox
*lbox
= wxStaticCast(consumer
->GetInputWindow(), wxListBox
);
1466 if ( !m_btnCapture
|| !m_trackMouseOutside
)
1468 // someone captured the mouse for us (we always set m_btnCapture
1469 // when we do it ourselves): in this case we only react to
1470 // the mouse messages when they happen inside the listbox
1471 if ( lbox
->HitTest(event
.GetPosition()) != wxHT_WINDOW_INSIDE
)
1475 int item
= HitTest(lbox
, event
);
1476 if ( !m_btnCapture
)
1478 // now that we have the mouse inside the listbox, do capture it
1479 // normally - but ensure that we will still ignore the outside
1481 SetupCapture(lbox
, event
, item
);
1483 m_trackMouseOutside
= FALSE
;
1486 if ( IsValidIndex(lbox
, item
) )
1488 // pass something into strArg to tell the listbox that it shouldn't
1489 // send the notification message: see PerformAction() above
1490 lbox
->PerformAction(m_actionMouse
, item
, _T("no"));
1492 // else: don't pass invalid index to the listbox
1494 else // we don't have capture any more
1498 // if we lost capture unexpectedly (someone else took the capture
1499 // from us), return to a consistent state
1504 return wxStdInputHandler::HandleMouseMove(consumer
, event
);
1507 #endif // wxUSE_LISTBOX