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 #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
,
90 Create(parent
, id
, pos
, size
, choices
, style
, validator
, name
);
93 bool wxListBox::Create(wxWindow
*parent
,
97 const wxArrayString
& choices
,
99 const wxValidator
& validator
,
100 const wxString
&name
)
102 wxCArrayString
chs(choices
);
104 return Create(parent
, id
, pos
, size
, chs
.GetCount(), chs
.GetStrings(),
105 style
, validator
, name
);
108 bool wxListBox::Create(wxWindow
*parent
,
113 const wxString choices
[],
115 const wxValidator
& validator
,
116 const wxString
&name
)
118 // for compatibility accept both the new and old styles - they mean the
120 if ( style
& wxLB_ALWAYS_SB
)
121 style
|= wxALWAYS_SHOW_SB
;
123 // if we don't have neither multiple nor extended flag, we must have the
124 // single selection listbox
125 if ( !(style
& (wxLB_MULTIPLE
| wxLB_EXTENDED
)) )
126 style
|= wxLB_SINGLE
;
128 #if wxUSE_TWO_WINDOWS
129 style
|= wxVSCROLL
|wxHSCROLL
;
130 if ((style
& wxBORDER_MASK
) == 0)
131 style
|= wxBORDER_SUNKEN
;
134 if ( !wxControl::Create(parent
, id
, pos
, size
, style
,
140 m_strings
= new wxArrayString
;
146 CreateInputHandler(wxINP_HANDLER_LISTBOX
);
151 wxListBox::~wxListBox()
153 // call this just to free the client data -- and avoid leaking memory
161 // ----------------------------------------------------------------------------
162 // adding/inserting strings
163 // ----------------------------------------------------------------------------
165 int wxCMPFUNC_CONV
wxListBoxSortNoCase(wxString
* s1
, wxString
* s2
)
167 return s1
->CmpNoCase(*s2
);
170 int wxListBox::DoAppendOnly(const wxString
& item
)
176 m_strings
->Add(item
);
177 m_strings
->Sort(wxListBoxSortNoCase
);
178 index
= m_strings
->Index(item
);
182 index
= m_strings
->GetCount();
183 m_strings
->Add(item
);
189 int wxListBox::DoAppend(const wxString
& item
)
191 size_t index
= DoAppendOnly( item
);
193 m_itemsClientData
.Insert(NULL
, index
);
195 m_updateScrollbarY
= true;
197 if ( HasHorzScrollbar() )
199 // has the max width increased?
201 GetTextExtent(item
, &width
, NULL
);
202 if ( width
> m_maxWidth
)
205 m_maxWidthItem
= index
;
206 m_updateScrollbarX
= true;
210 RefreshFromItemToEnd(index
);
215 void wxListBox::DoInsertItems(const wxArrayString
& items
, int pos
)
217 // the position of the item being added to a sorted listbox can't be
219 wxCHECK_RET( !IsSorted(), _T("can't insert items into sorted listbox") );
221 size_t count
= items
.GetCount();
222 for ( size_t n
= 0; n
< count
; n
++ )
224 m_strings
->Insert(items
[n
], pos
+ n
);
225 m_itemsClientData
.Insert(NULL
, pos
+ n
);
228 // the number of items has changed so we might have to show the scrollbar
229 m_updateScrollbarY
= true;
231 // the max width also might have changed - just recalculate it instead of
232 // keeping track of it here, this is probably more efficient for a typical
234 RefreshHorzScrollbar();
236 // note that we have to refresh all the items after the ones we inserted,
237 // not just these items
238 RefreshFromItemToEnd(pos
);
241 void wxListBox::DoSetItems(const wxArrayString
& items
, void **clientData
)
245 size_t count
= items
.GetCount();
249 m_strings
->Alloc(count
);
251 m_itemsClientData
.Alloc(count
);
252 for ( size_t n
= 0; n
< count
; n
++ )
254 size_t index
= DoAppendOnly(items
[n
]);
256 m_itemsClientData
.Insert(clientData
? clientData
[n
] : NULL
, index
);
259 m_updateScrollbarY
= true;
264 void wxListBox::SetString(int n
, const wxString
& s
)
266 wxCHECK_RET( !IsSorted(), _T("can't set string in sorted listbox") );
270 if ( HasHorzScrollbar() )
272 // we need to update m_maxWidth as changing the string may cause the
273 // horz scrollbar [dis]appear
276 GetTextExtent(s
, &width
, NULL
);
278 // it might have increased if the new string is long
279 if ( width
> m_maxWidth
)
283 m_updateScrollbarX
= true;
285 // or also decreased if the old string was the longest one
286 else if ( n
== m_maxWidthItem
)
288 RefreshHorzScrollbar();
295 // ----------------------------------------------------------------------------
297 // ----------------------------------------------------------------------------
299 void wxListBox::DoClear()
303 if ( HasClientObjectData() )
305 size_t count
= m_itemsClientData
.GetCount();
306 for ( size_t n
= 0; n
< count
; n
++ )
308 delete (wxClientData
*) m_itemsClientData
[n
];
312 m_itemsClientData
.Clear();
313 m_selections
.Clear();
318 void wxListBox::Clear()
322 m_updateScrollbarY
= true;
324 RefreshHorzScrollbar();
329 void wxListBox::Delete(int n
)
331 wxCHECK_RET( n
>= 0 && n
< GetCount(),
332 _T("invalid index in wxListBox::Delete") );
334 // do it before removing the index as otherwise the last item will not be
335 // refreshed (as GetCount() will be decremented)
336 RefreshFromItemToEnd(n
);
338 m_strings
->RemoveAt(n
);
340 if ( HasClientObjectData() )
342 delete (wxClientData
*)m_itemsClientData
[n
];
345 m_itemsClientData
.RemoveAt(n
);
347 // when the item disappears we must not keep using its index
348 if ( n
== m_current
)
352 else if ( n
< m_current
)
356 //else: current item may stay
358 // update the selections array: the indices of all seletected items after
359 // the one being deleted must change and the item itselfm ust be removed
360 int index
= wxNOT_FOUND
;
361 size_t count
= m_selections
.GetCount();
362 for ( size_t item
= 0; item
< count
; item
++ )
364 if ( m_selections
[item
] == n
)
366 // remember to delete it later
369 else if ( m_selections
[item
] > n
)
371 // to account for the index shift
372 m_selections
[item
]--;
374 //else: nothing changed for this one
377 if ( index
!= wxNOT_FOUND
)
379 m_selections
.RemoveAt(index
);
382 // the number of items has changed, hence the scrollbar may disappear
383 m_updateScrollbarY
= true;
385 // finally, if the longest item was deleted the scrollbar may disappear
386 if ( n
== m_maxWidthItem
)
388 RefreshHorzScrollbar();
392 // ----------------------------------------------------------------------------
393 // client data handling
394 // ----------------------------------------------------------------------------
396 void wxListBox::DoSetItemClientData(int n
, void* clientData
)
398 m_itemsClientData
[n
] = clientData
;
401 void *wxListBox::DoGetItemClientData(int n
) const
403 return m_itemsClientData
[n
];
406 void wxListBox::DoSetItemClientObject(int n
, wxClientData
* clientData
)
408 m_itemsClientData
[n
] = clientData
;
411 wxClientData
* wxListBox::DoGetItemClientObject(int n
) const
413 return (wxClientData
*)m_itemsClientData
[n
];
416 // ----------------------------------------------------------------------------
418 // ----------------------------------------------------------------------------
420 void wxListBox::DoSetSelection(int n
, bool select
)
424 if ( m_selections
.Index(n
) == wxNOT_FOUND
)
426 if ( !HasMultipleSelection() )
428 // selecting an item in a single selection listbox deselects
437 //else: already selected
441 int index
= m_selections
.Index(n
);
442 if ( index
!= wxNOT_FOUND
)
444 m_selections
.RemoveAt(index
);
451 // sanity check: a single selection listbox can't have more than one item
453 wxASSERT_MSG( HasMultipleSelection() || (m_selections
.GetCount() < 2),
454 _T("multiple selected items in single selection lbox?") );
458 // the newly selected item becomes the current one
463 int wxListBox::GetSelection() const
465 wxCHECK_MSG( !HasMultipleSelection(), -1,
466 _T("use wxListBox::GetSelections for ths listbox") );
468 return m_selections
.IsEmpty() ? -1 : m_selections
[0];
471 int wxCMPFUNC_CONV
wxCompareInts(int *n
, int *m
)
476 int wxListBox::GetSelections(wxArrayInt
& selections
) const
478 // always return sorted array to the user
479 selections
= m_selections
;
480 size_t count
= m_selections
.GetCount();
482 // don't call sort on an empty array
485 selections
.Sort(wxCompareInts
);
491 // ----------------------------------------------------------------------------
492 // refresh logic: we use delayed refreshing which allows to avoid multiple
493 // refreshes (and hence flicker) in case when several listbox items are
494 // added/deleted/changed subsequently
495 // ----------------------------------------------------------------------------
497 void wxListBox::RefreshFromItemToEnd(int from
)
499 RefreshItems(from
, GetCount() - from
);
502 void wxListBox::RefreshItems(int from
, int count
)
504 switch ( m_updateCount
)
508 m_updateCount
= count
;
512 // we refresh everything anyhow
516 // add these items to the others which we have to refresh
517 if ( m_updateFrom
< from
)
519 count
+= from
- m_updateFrom
;
520 if ( m_updateCount
< count
)
521 m_updateCount
= count
;
523 else // m_updateFrom >= from
525 int updateLast
= wxMax(m_updateFrom
+ m_updateCount
,
528 m_updateCount
= updateLast
- m_updateFrom
;
533 void wxListBox::RefreshItem(int n
)
535 switch ( m_updateCount
)
538 // refresh this item only
544 // we refresh everything anyhow
548 // add this item to the others which we have to refresh
549 if ( m_updateFrom
< n
)
551 if ( m_updateCount
< n
- m_updateFrom
+ 1 )
552 m_updateCount
= n
- m_updateFrom
+ 1;
554 else // n <= m_updateFrom
556 m_updateCount
+= m_updateFrom
- n
;
562 void wxListBox::RefreshAll()
567 void wxListBox::RefreshHorzScrollbar()
569 m_maxWidth
= 0; // recalculate it
570 m_updateScrollbarX
= true;
573 void wxListBox::UpdateScrollbars()
575 wxSize size
= GetClientSize();
577 // is our height enough to show all items?
578 int nLines
= GetCount();
579 wxCoord lineHeight
= GetLineHeight();
580 bool showScrollbarY
= nLines
*lineHeight
> size
.y
;
582 // check the width too if required
583 wxCoord charWidth
, maxWidth
;
585 if ( HasHorzScrollbar() )
587 charWidth
= GetCharWidth();
588 maxWidth
= GetMaxWidth();
589 showScrollbarX
= maxWidth
> size
.x
;
591 else // never show it
593 charWidth
= maxWidth
= 0;
594 showScrollbarX
= false;
597 // what should be the scrollbar range now?
598 int scrollRangeX
= showScrollbarX
599 ? (maxWidth
+ charWidth
- 1) / charWidth
+ 2 // FIXME
601 int scrollRangeY
= showScrollbarY
603 (size
.y
% lineHeight
+ lineHeight
- 1) / lineHeight
606 // reset scrollbars if something changed: either the visibility status
607 // or the range of a scrollbar which is shown
608 if ( (showScrollbarY
!= m_showScrollbarY
) ||
609 (showScrollbarX
!= m_showScrollbarX
) ||
610 (showScrollbarY
&& (scrollRangeY
!= m_scrollRangeY
)) ||
611 (showScrollbarX
&& (scrollRangeX
!= m_scrollRangeX
)) )
614 GetViewStart(&x
, &y
);
615 SetScrollbars(charWidth
, lineHeight
,
616 scrollRangeX
, scrollRangeY
,
619 m_showScrollbarX
= showScrollbarX
;
620 m_showScrollbarY
= showScrollbarY
;
622 m_scrollRangeX
= scrollRangeX
;
623 m_scrollRangeY
= scrollRangeY
;
627 void wxListBox::UpdateItems()
629 // only refresh the items which must be refreshed
630 if ( m_updateCount
== -1 )
633 wxLogTrace(_T("listbox"), _T("Refreshing all"));
639 wxSize size
= GetClientSize();
642 rect
.height
= size
.y
;
643 rect
.y
+= m_updateFrom
*GetLineHeight();
644 rect
.height
= m_updateCount
*GetLineHeight();
646 // we don't need to calculate x position as we always refresh the
648 CalcScrolledPosition(0, rect
.y
, NULL
, &rect
.y
);
650 wxLogTrace(_T("listbox"), _T("Refreshing items %d..%d (%d-%d)"),
651 m_updateFrom
, m_updateFrom
+ m_updateCount
- 1,
652 rect
.GetTop(), rect
.GetBottom());
654 Refresh(true, &rect
);
658 void wxListBox::OnInternalIdle()
660 if ( m_updateScrollbarY
|| m_updateScrollbarX
)
665 m_updateScrollbarY
= false;
668 if ( m_currentChanged
)
670 DoEnsureVisible(m_current
);
672 m_currentChanged
= false;
681 wxListBoxBase::OnInternalIdle();
684 // ----------------------------------------------------------------------------
686 // ----------------------------------------------------------------------------
688 wxBorder
wxListBox::GetDefaultBorder() const
690 return wxBORDER_SUNKEN
;
693 void wxListBox::DoDraw(wxControlRenderer
*renderer
)
695 // adjust the DC to account for scrolling
696 wxDC
& dc
= renderer
->GetDC();
698 dc
.SetFont(GetFont());
700 // get the update rect
701 wxRect rectUpdate
= GetUpdateClientRect();
704 CalcUnscrolledPosition(0, rectUpdate
.GetTop(), NULL
, &yTop
);
705 CalcUnscrolledPosition(0, rectUpdate
.GetBottom(), NULL
, &yBottom
);
707 // get the items which must be redrawn
708 wxCoord lineHeight
= GetLineHeight();
709 size_t itemFirst
= yTop
/ lineHeight
,
710 itemLast
= (yBottom
+ lineHeight
- 1) / lineHeight
,
711 itemMax
= m_strings
->GetCount();
713 if ( itemFirst
>= itemMax
)
716 if ( itemLast
> itemMax
)
720 wxLogTrace(_T("listbox"), _T("Repainting items %d..%d"),
721 itemFirst
, itemLast
);
723 DoDrawRange(renderer
, itemFirst
, itemLast
);
726 void wxListBox::DoDrawRange(wxControlRenderer
*renderer
,
727 int itemFirst
, int itemLast
)
729 renderer
->DrawItems(this, itemFirst
, itemLast
);
732 // ----------------------------------------------------------------------------
734 // ----------------------------------------------------------------------------
736 bool wxListBox::SetFont(const wxFont
& font
)
738 if ( !wxControl::SetFont(font
) )
748 void wxListBox::CalcItemsPerPage()
750 m_lineHeight
= GetRenderer()->GetListboxItemHeight(GetCharHeight());
751 m_itemsPerPage
= GetClientSize().y
/ m_lineHeight
;
754 int wxListBox::GetItemsPerPage() const
756 if ( !m_itemsPerPage
)
758 wxConstCast(this, wxListBox
)->CalcItemsPerPage();
761 return m_itemsPerPage
;
764 wxCoord
wxListBox::GetLineHeight() const
768 wxConstCast(this, wxListBox
)->CalcItemsPerPage();
774 wxCoord
wxListBox::GetMaxWidth() const
776 if ( m_maxWidth
== 0 )
778 wxListBox
*self
= wxConstCast(this, wxListBox
);
780 size_t count
= m_strings
->GetCount();
781 for ( size_t n
= 0; n
< count
; n
++ )
783 GetTextExtent(this->GetString(n
), &width
, NULL
);
784 if ( width
> m_maxWidth
)
786 self
->m_maxWidth
= width
;
787 self
->m_maxWidthItem
= n
;
795 void wxListBox::OnSize(wxSizeEvent
& event
)
797 // recalculate the number of items per page
800 // the scrollbars might [dis]appear
802 m_updateScrollbarY
= true;
807 void wxListBox::DoSetFirstItem(int n
)
812 void wxListBox::DoSetSize(int x
, int y
,
813 int width
, int height
,
816 if ( GetWindowStyle() & wxLB_INT_HEIGHT
)
818 // we must round up the height to an entire number of rows
820 // the client area must contain an int number of rows, so take borders
822 wxRect rectBorders
= GetRenderer()->GetBorderDimensions(GetBorder());
823 wxCoord hBorders
= rectBorders
.y
+ rectBorders
.height
;
825 wxCoord hLine
= GetLineHeight();
826 height
= ((height
- hBorders
+ hLine
- 1) / hLine
)*hLine
+ hBorders
;
829 wxListBoxBase::DoSetSize(x
, y
, width
, height
, sizeFlags
);
832 wxSize
wxListBox::DoGetBestClientSize() const
837 size_t count
= m_strings
->GetCount();
838 for ( size_t n
= 0; n
< count
; n
++ )
841 GetTextExtent(this->GetString(n
), &w
, &h
);
849 // if the listbox is empty, still give it some non zero (even if
850 // arbitrary) size - otherwise, leave small margin around the strings
854 width
+= 3*GetCharWidth();
857 height
= GetCharHeight();
859 // we need the height of the entire listbox, not just of one line
860 height
*= wxMax(count
, 7);
862 return wxSize(width
, height
);
865 // ----------------------------------------------------------------------------
867 // ----------------------------------------------------------------------------
869 bool wxListBox::SendEvent(wxEventType type
, int item
)
871 wxCommandEvent
event(type
, m_windowId
);
872 event
.SetEventObject(this);
874 // use the current item by default
880 // client data and string parameters only make sense if we have an item
883 if ( HasClientObjectData() )
884 event
.SetClientObject(GetClientObject(item
));
885 else if ( HasClientUntypedData() )
886 event
.SetClientData(GetClientData(item
));
888 event
.SetString(GetString(item
));
893 return GetEventHandler()->ProcessEvent(event
);
896 void wxListBox::SetCurrentItem(int n
)
898 if ( n
!= m_current
)
900 if ( m_current
!= -1 )
901 RefreshItem(m_current
);
905 if ( m_current
!= -1 )
907 m_currentChanged
= true;
909 RefreshItem(m_current
);
912 //else: nothing to do
915 bool wxListBox::FindItem(const wxString
& prefix
, bool strictlyAfter
)
917 int count
= GetCount();
920 // empty listbox, we can't find anything in it
924 // start either from the current item or from the next one if strictlyAfter
929 // the following line will set first correctly to 0 if there is no
930 // selection (m_current == -1)
931 first
= m_current
== count
- 1 ? 0 : m_current
+ 1;
933 else // start with the current
935 first
= m_current
== -1 ? 0 : m_current
;
938 int last
= first
== 0 ? count
- 1 : first
- 1;
940 // if this is not true we'd never exit from the loop below!
941 wxASSERT_MSG( first
< count
&& last
< count
, _T("logic error") );
943 // precompute it outside the loop
944 size_t len
= prefix
.length();
946 // loop over all items in the listbox
947 for ( int item
= first
; item
!= last
; item
< count
- 1 ? item
++ : item
= 0 )
949 if ( wxStrnicmp(this->GetString(item
).c_str(), prefix
, len
) == 0 )
951 SetCurrentItem(item
);
953 if ( !(GetWindowStyle() & wxLB_MULTIPLE
) )
956 SelectAndNotify(item
);
958 if ( GetWindowStyle() & wxLB_EXTENDED
)
959 AnchorSelection(item
);
970 void wxListBox::EnsureVisible(int n
)
972 if ( m_updateScrollbarY
)
977 m_updateScrollbarY
= false;
983 void wxListBox::DoEnsureVisible(int n
)
985 if ( !m_showScrollbarY
)
987 // nothing to do - everything is shown anyhow
992 GetViewStart(0, &first
);
995 // we need to scroll upwards, so make the current item appear on top
996 // of the shown range
1001 int last
= first
+ GetClientSize().y
/ GetLineHeight() - 1;
1004 // scroll down: the current item appears at the bottom of the
1006 Scroll(0, n
- (last
- first
));
1011 void wxListBox::ChangeCurrent(int diff
)
1013 int current
= m_current
== -1 ? 0 : m_current
;
1017 int last
= GetCount() - 1;
1020 else if ( current
> last
)
1023 SetCurrentItem(current
);
1026 void wxListBox::ExtendSelection(int itemTo
)
1028 // if we don't have the explicit values for selection start/end, make them
1030 if ( m_selAnchor
== -1 )
1031 m_selAnchor
= m_current
;
1036 // swap the start/end of selection range if necessary
1037 int itemFrom
= m_selAnchor
;
1038 if ( itemFrom
> itemTo
)
1040 int itemTmp
= itemFrom
;
1045 // the selection should now include all items in the range between the
1046 // anchor and the specified item and only them
1049 for ( n
= 0; n
< itemFrom
; n
++ )
1054 for ( ; n
<= itemTo
; n
++ )
1059 int count
= GetCount();
1060 for ( ; n
< count
; n
++ )
1066 void wxListBox::DoSelect(int item
, bool sel
)
1070 // go to this item first
1071 SetCurrentItem(item
);
1074 // the current item is the one we want to change: either it was just
1075 // changed above to be the same as item or item == -1 in which we case we
1076 // are supposed to use the current one anyhow
1077 if ( m_current
!= -1 )
1080 SetSelection(m_current
, sel
);
1084 void wxListBox::SelectAndNotify(int item
)
1088 SendEvent(wxEVT_COMMAND_LISTBOX_SELECTED
);
1091 void wxListBox::Activate(int item
)
1094 SetCurrentItem(item
);
1098 if ( !(GetWindowStyle() & wxLB_MULTIPLE
) )
1107 SendEvent(wxEVT_COMMAND_LISTBOX_DOUBLECLICKED
);
1111 // ----------------------------------------------------------------------------
1113 // ----------------------------------------------------------------------------
1116 The numArg here is the listbox item index while the strArg is used
1117 differently for the different actions:
1119 a) for wxACTION_LISTBOX_FIND it has the natural meaning: this is the string
1122 b) for wxACTION_LISTBOX_SELECT and wxACTION_LISTBOX_EXTENDSEL it is used
1123 to decide if the listbox should send the notification event (it is empty)
1124 or not (it is not): this allows us to reuse the same action for when the
1125 user is dragging the mouse when it has been released although in the
1126 first case no notification is sent while in the second it is sent.
1128 bool wxListBox::PerformAction(const wxControlAction
& action
,
1130 const wxString
& strArg
)
1132 int item
= (int)numArg
;
1134 if ( action
== wxACTION_LISTBOX_SETFOCUS
)
1136 SetCurrentItem(item
);
1138 else if ( action
== wxACTION_LISTBOX_ACTIVATE
)
1142 else if ( action
== wxACTION_LISTBOX_TOGGLE
)
1147 if ( IsSelected(item
) )
1150 SelectAndNotify(item
);
1152 else if ( action
== wxACTION_LISTBOX_SELECT
)
1156 if ( strArg
.empty() )
1157 SelectAndNotify(item
);
1161 else if ( action
== wxACTION_LISTBOX_SELECTADD
)
1163 else if ( action
== wxACTION_LISTBOX_UNSELECT
)
1165 else if ( action
== wxACTION_LISTBOX_MOVEDOWN
)
1167 else if ( action
== wxACTION_LISTBOX_MOVEUP
)
1169 else if ( action
== wxACTION_LISTBOX_PAGEDOWN
)
1170 ChangeCurrent(GetItemsPerPage());
1171 else if ( action
== wxACTION_LISTBOX_PAGEUP
)
1172 ChangeCurrent(-GetItemsPerPage());
1173 else if ( action
== wxACTION_LISTBOX_START
)
1175 else if ( action
== wxACTION_LISTBOX_END
)
1176 SetCurrentItem(GetCount() - 1);
1177 else if ( action
== wxACTION_LISTBOX_UNSELECTALL
)
1179 else if ( action
== wxACTION_LISTBOX_EXTENDSEL
)
1180 ExtendSelection(item
);
1181 else if ( action
== wxACTION_LISTBOX_FIND
)
1182 FindNextItem(strArg
);
1183 else if ( action
== wxACTION_LISTBOX_ANCHOR
)
1184 AnchorSelection(item
== -1 ? m_current
: item
);
1185 else if ( action
== wxACTION_LISTBOX_SELECTALL
||
1186 action
== wxACTION_LISTBOX_SELTOGGLE
)
1187 wxFAIL_MSG(_T("unimplemented yet"));
1189 return wxControl::PerformAction(action
, numArg
, strArg
);
1194 // ============================================================================
1195 // implementation of wxStdListboxInputHandler
1196 // ============================================================================
1198 wxStdListboxInputHandler::wxStdListboxInputHandler(wxInputHandler
*handler
,
1199 bool toggleOnPressAlways
)
1200 : wxStdInputHandler(handler
)
1203 m_toggleOnPressAlways
= toggleOnPressAlways
;
1204 m_actionMouse
= wxACTION_NONE
;
1205 m_trackMouseOutside
= true;
1208 int wxStdListboxInputHandler::HitTest(const wxListBox
*lbox
,
1209 const wxMouseEvent
& event
)
1211 int item
= HitTestUnsafe(lbox
, event
);
1213 return FixItemIndex(lbox
, item
);
1216 int wxStdListboxInputHandler::HitTestUnsafe(const wxListBox
*lbox
,
1217 const wxMouseEvent
& event
)
1219 wxPoint pt
= event
.GetPosition();
1220 pt
-= lbox
->GetClientAreaOrigin();
1222 lbox
->CalcUnscrolledPosition(0, pt
.y
, NULL
, &y
);
1223 return y
/ lbox
->GetLineHeight();
1226 int wxStdListboxInputHandler::FixItemIndex(const wxListBox
*lbox
,
1231 // mouse is above the first item
1234 else if ( item
>= lbox
->GetCount() )
1236 // mouse is below the last item
1237 item
= lbox
->GetCount() - 1;
1243 bool wxStdListboxInputHandler::IsValidIndex(const wxListBox
*lbox
, int item
)
1245 return item
>= 0 && item
< lbox
->GetCount();
1249 wxStdListboxInputHandler::SetupCapture(wxListBox
*lbox
,
1250 const wxMouseEvent
& event
,
1253 // we currently only allow selecting with the left mouse button, if we
1254 // do need to allow using other buttons too we might use the code
1257 m_btnCapture
= event
.LeftDown()
1266 wxControlAction action
;
1267 if ( lbox
->HasMultipleSelection() )
1269 if ( lbox
->GetWindowStyle() & wxLB_MULTIPLE
)
1271 if ( m_toggleOnPressAlways
)
1273 // toggle the item right now
1274 action
= wxACTION_LISTBOX_TOGGLE
;
1278 m_actionMouse
= wxACTION_LISTBOX_SETFOCUS
;
1280 else // wxLB_EXTENDED listbox
1282 // simple click in an extended sel listbox clears the old
1283 // selection and adds the clicked item to it then, ctrl-click
1284 // toggles an item to it and shift-click adds a range between
1285 // the old selection anchor and the clicked item
1286 if ( event
.ControlDown() )
1288 lbox
->PerformAction(wxACTION_LISTBOX_ANCHOR
, item
);
1290 action
= wxACTION_LISTBOX_TOGGLE
;
1292 else if ( event
.ShiftDown() )
1294 action
= wxACTION_LISTBOX_EXTENDSEL
;
1296 else // simple click
1298 lbox
->PerformAction(wxACTION_LISTBOX_ANCHOR
, item
);
1300 action
= wxACTION_LISTBOX_SELECT
;
1303 m_actionMouse
= wxACTION_LISTBOX_EXTENDSEL
;
1306 else // single selection
1309 action
= wxACTION_LISTBOX_SELECT
;
1312 // by default we always do track it
1313 m_trackMouseOutside
= true;
1318 bool wxStdListboxInputHandler::HandleKey(wxInputConsumer
*consumer
,
1319 const wxKeyEvent
& event
,
1322 // we're only interested in the key press events
1323 if ( pressed
&& !event
.AltDown() )
1325 bool isMoveCmd
= true;
1326 int style
= consumer
->GetInputWindow()->GetWindowStyle();
1328 wxControlAction action
;
1331 int keycode
= event
.GetKeyCode();
1336 action
= wxACTION_LISTBOX_MOVEUP
;
1340 action
= wxACTION_LISTBOX_MOVEDOWN
;
1346 action
= wxACTION_LISTBOX_PAGEUP
;
1352 action
= wxACTION_LISTBOX_PAGEDOWN
;
1356 action
= wxACTION_LISTBOX_START
;
1360 action
= wxACTION_LISTBOX_END
;
1365 if ( style
& wxLB_MULTIPLE
)
1367 action
= wxACTION_LISTBOX_TOGGLE
;
1373 action
= wxACTION_LISTBOX_ACTIVATE
;
1378 if ( (keycode
< 255) && wxIsalnum((wxChar
)keycode
) )
1380 action
= wxACTION_LISTBOX_FIND
;
1381 strArg
= (wxChar
)keycode
;
1385 if ( !action
.IsEmpty() )
1387 consumer
->PerformAction(action
, -1, strArg
);
1391 if ( style
& wxLB_SINGLE
)
1393 // the current item is always the one selected
1394 consumer
->PerformAction(wxACTION_LISTBOX_SELECT
);
1396 else if ( style
& wxLB_EXTENDED
)
1398 if ( event
.ShiftDown() )
1399 consumer
->PerformAction(wxACTION_LISTBOX_EXTENDSEL
);
1402 // select the item and make it the new selection anchor
1403 consumer
->PerformAction(wxACTION_LISTBOX_SELECT
);
1404 consumer
->PerformAction(wxACTION_LISTBOX_ANCHOR
);
1407 //else: nothing to do for multiple selection listboxes
1414 return wxStdInputHandler::HandleKey(consumer
, event
, pressed
);
1417 bool wxStdListboxInputHandler::HandleMouse(wxInputConsumer
*consumer
,
1418 const wxMouseEvent
& event
)
1420 wxListBox
*lbox
= wxStaticCast(consumer
->GetInputWindow(), wxListBox
);
1421 int item
= HitTest(lbox
, event
);
1422 wxControlAction action
;
1424 // when the left mouse button is pressed, capture the mouse and track the
1425 // item under mouse (if the mouse leaves the window, we will still be
1426 // getting the mouse move messages generated by wxScrollWindow)
1427 if ( event
.LeftDown() )
1429 // capture the mouse to track the selected item
1430 lbox
->CaptureMouse();
1432 action
= SetupCapture(lbox
, event
, item
);
1434 else if ( m_btnCapture
&& event
.ButtonUp(m_btnCapture
) )
1436 // when the left mouse button is released, release the mouse too
1437 wxWindow
*winCapture
= wxWindow::GetCapture();
1440 winCapture
->ReleaseMouse();
1443 action
= m_actionMouse
;
1445 //else: the mouse wasn't presed over the listbox, only released here
1447 else if ( event
.LeftDClick() )
1449 action
= wxACTION_LISTBOX_ACTIVATE
;
1452 if ( !action
.IsEmpty() )
1454 lbox
->PerformAction(action
, item
);
1459 return wxStdInputHandler::HandleMouse(consumer
, event
);
1462 bool wxStdListboxInputHandler::HandleMouseMove(wxInputConsumer
*consumer
,
1463 const wxMouseEvent
& event
)
1465 wxWindow
*winCapture
= wxWindow::GetCapture();
1466 if ( winCapture
&& (event
.GetEventObject() == winCapture
) )
1468 wxListBox
*lbox
= wxStaticCast(consumer
->GetInputWindow(), wxListBox
);
1470 if ( !m_btnCapture
|| !m_trackMouseOutside
)
1472 // someone captured the mouse for us (we always set m_btnCapture
1473 // when we do it ourselves): in this case we only react to
1474 // the mouse messages when they happen inside the listbox
1475 if ( lbox
->HitTest(event
.GetPosition()) != wxHT_WINDOW_INSIDE
)
1479 int item
= HitTest(lbox
, event
);
1480 if ( !m_btnCapture
)
1482 // now that we have the mouse inside the listbox, do capture it
1483 // normally - but ensure that we will still ignore the outside
1485 SetupCapture(lbox
, event
, item
);
1487 m_trackMouseOutside
= false;
1490 if ( IsValidIndex(lbox
, item
) )
1492 // pass something into strArg to tell the listbox that it shouldn't
1493 // send the notification message: see PerformAction() above
1494 lbox
->PerformAction(m_actionMouse
, item
, _T("no"));
1496 // else: don't pass invalid index to the listbox
1498 else // we don't have capture any more
1502 // if we lost capture unexpectedly (someone else took the capture
1503 // from us), return to a consistent state
1508 return wxStdInputHandler::HandleMouseMove(consumer
, event
);
1511 #endif // wxUSE_LISTBOX