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 // ----------------------------------------------------------------------------
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
67 // no items hence no current item
70 m_currentChanged
= FALSE
;
72 // no need to update anything initially
75 // no scrollbars to show nor update
79 m_showScrollbarY
= FALSE
;
82 bool wxListBox::Create(wxWindow
*parent
,
87 const wxString choices
[],
89 const wxValidator
& validator
,
92 // for compatibility accept both the new and old styles - they mean the
94 if ( style
& wxLB_ALWAYS_SB
)
95 style
|= wxALWAYS_SHOW_SB
;
97 // if we don't have neither multiple nor extended flag, we must have the
98 // single selection listbox
99 if ( !(style
& (wxLB_MULTIPLE
| wxLB_EXTENDED
)) )
100 style
|= wxLB_SINGLE
;
102 #if wxUSE_TWO_WINDOWS
103 style
|= wxVSCROLL
|wxHSCROLL
;
104 if ((style
& wxBORDER_MASK
) == 0)
105 style
|= wxBORDER_SUNKEN
;
108 if ( !wxControl::Create(parent
, id
, pos
, size
, style
,
114 if ( style
& wxLB_SORT
)
115 m_strings
= wxArrayString(TRUE
/* auto sort */);
121 CreateInputHandler(wxINP_HANDLER_LISTBOX
);
126 wxListBox::~wxListBox()
128 // call this just to free the client data -- and avoid leaking memory
132 // ----------------------------------------------------------------------------
133 // adding/inserting strings
134 // ----------------------------------------------------------------------------
136 int wxListBox::DoAppend(const wxString
& item
)
138 size_t index
= m_strings
.Add(item
);
139 m_itemsClientData
.Insert(NULL
, index
);
141 m_updateScrollbarY
= TRUE
;
143 if ( HasHorzScrollbar() )
145 // has the max width increased?
147 GetTextExtent(item
, &width
, NULL
);
148 if ( width
> m_maxWidth
)
151 m_maxWidthItem
= index
;
152 m_updateScrollbarX
= TRUE
;
156 RefreshFromItemToEnd(index
);
161 void wxListBox::DoInsertItems(const wxArrayString
& items
, int pos
)
163 // the position of the item being added to a sorted listbox can't be
165 wxCHECK_RET( !IsSorted(), _T("can't insert items into sorted listbox") );
167 size_t count
= items
.GetCount();
168 for ( size_t n
= 0; n
< count
; n
++ )
170 m_strings
.Insert(items
[n
], pos
+ n
);
171 m_itemsClientData
.Insert(NULL
, pos
+ n
);
174 // the number of items has changed so we might have to show the scrollbar
175 m_updateScrollbarY
= TRUE
;
177 // the max width also might have changed - just recalculate it instead of
178 // keeping track of it here, this is probably more efficient for a typical
180 RefreshHorzScrollbar();
182 // note that we have to refresh all the items after the ones we inserted,
183 // not just these items
184 RefreshFromItemToEnd(pos
);
187 void wxListBox::DoSetItems(const wxArrayString
& items
, void **clientData
)
191 size_t count
= items
.GetCount();
195 m_strings
.Alloc(count
);
196 m_itemsClientData
.Alloc(count
);
197 for ( size_t n
= 0; n
< count
; n
++ )
199 size_t index
= m_strings
.Add(items
[n
]);
200 m_itemsClientData
.Insert(clientData
? clientData
[n
] : NULL
, index
);
203 m_updateScrollbarY
= TRUE
;
208 void wxListBox::SetString(int n
, const wxString
& s
)
210 if ( HasHorzScrollbar() )
212 // we need to update m_maxWidth as changing the string may cause the
213 // horz scrollbar [dis]appear
216 GetTextExtent(s
, &width
, NULL
);
218 // it might have increased if the new string is long
219 if ( width
> m_maxWidth
)
223 m_updateScrollbarX
= TRUE
;
225 // or also decreased if the old string was the longest one
226 else if ( n
== m_maxWidthItem
)
228 RefreshHorzScrollbar();
231 else // no horz scrollbar
239 // ----------------------------------------------------------------------------
241 // ----------------------------------------------------------------------------
243 void wxListBox::DoClear()
247 if ( HasClientObjectData() )
249 size_t count
= m_itemsClientData
.GetCount();
250 for ( size_t n
= 0; n
< count
; n
++ )
252 delete (wxClientData
*) m_itemsClientData
[n
];
256 m_itemsClientData
.Clear();
257 m_selections
.Clear();
262 void wxListBox::Clear()
266 m_updateScrollbarY
= TRUE
;
268 RefreshHorzScrollbar();
273 void wxListBox::Delete(int n
)
275 wxCHECK_RET( n
>= 0 && n
< GetCount(),
276 _T("invalid index in wxListBox::Delete") );
278 // do it before removing the index as otherwise the last item will not be
279 // refreshed (as GetCount() will be decremented)
280 RefreshFromItemToEnd(n
);
282 m_strings
.RemoveAt(n
);
284 if ( HasClientObjectData() )
286 delete (wxClientData
*)m_itemsClientData
[n
];
289 m_itemsClientData
.RemoveAt(n
);
291 // when the item disappears we must not keep using its index
292 if ( n
== m_current
)
296 else if ( n
< m_current
)
300 //else: current item may stay
302 // update the selections array: the indices of all seletected items after
303 // the one being deleted must change and the item itselfm ust be removed
304 int index
= wxNOT_FOUND
;
305 size_t count
= m_selections
.GetCount();
306 for ( size_t item
= 0; item
< count
; item
++ )
308 if ( m_selections
[item
] == n
)
310 // remember to delete it later
313 else if ( m_selections
[item
] > n
)
315 // to account for the index shift
316 m_selections
[item
]--;
318 //else: nothing changed for this one
321 if ( index
!= wxNOT_FOUND
)
323 m_selections
.RemoveAt(index
);
326 // the number of items has changed, hence the scrollbar may disappear
327 m_updateScrollbarY
= TRUE
;
329 // finally, if the longest item was deleted the scrollbar may disappear
330 if ( n
== m_maxWidthItem
)
332 RefreshHorzScrollbar();
336 // ----------------------------------------------------------------------------
337 // client data handling
338 // ----------------------------------------------------------------------------
340 void wxListBox::DoSetItemClientData(int n
, void* clientData
)
342 m_itemsClientData
[n
] = clientData
;
345 void *wxListBox::DoGetItemClientData(int n
) const
347 return m_itemsClientData
[n
];
350 void wxListBox::DoSetItemClientObject(int n
, wxClientData
* clientData
)
352 m_itemsClientData
[n
] = clientData
;
355 wxClientData
* wxListBox::DoGetItemClientObject(int n
) const
357 return (wxClientData
*)m_itemsClientData
[n
];
360 // ----------------------------------------------------------------------------
362 // ----------------------------------------------------------------------------
364 void wxListBox::SetSelection(int n
, bool select
)
368 if ( m_selections
.Index(n
) == wxNOT_FOUND
)
370 if ( !HasMultipleSelection() )
372 // selecting an item in a single selection listbox deselects
381 //else: already selected
385 int index
= m_selections
.Index(n
);
386 if ( index
!= wxNOT_FOUND
)
388 m_selections
.RemoveAt(index
);
395 // sanity check: a single selection listbox can't have more than one item
397 wxASSERT_MSG( HasMultipleSelection() || (m_selections
.GetCount() < 2),
398 _T("multiple selected items in single selection lbox?") );
402 // the newly selected item becomes the current one
407 int wxListBox::GetSelection() const
409 wxCHECK_MSG( !HasMultipleSelection(), -1,
410 _T("use wxListBox::GetSelections for ths listbox") );
412 return m_selections
.IsEmpty() ? -1 : m_selections
[0];
415 int wxCMPFUNC_CONV
wxCompareInts(int *n
, int *m
)
420 int wxListBox::GetSelections(wxArrayInt
& selections
) const
422 // always return sorted array to the user
423 selections
= m_selections
;
424 size_t count
= m_selections
.GetCount();
426 // don't call sort on an empty array
429 selections
.Sort(wxCompareInts
);
435 // ----------------------------------------------------------------------------
436 // refresh logic: we use delayed refreshing which allows to avoid multiple
437 // refreshes (and hence flicker) in case when several listbox items are
438 // added/deleted/changed subsequently
439 // ----------------------------------------------------------------------------
441 void wxListBox::RefreshFromItemToEnd(int from
)
443 RefreshItems(from
, GetCount() - from
);
446 void wxListBox::RefreshItems(int from
, int count
)
448 switch ( m_updateCount
)
452 m_updateCount
= count
;
456 // we refresh everything anyhow
460 // add these items to the others which we have to refresh
461 if ( m_updateFrom
< from
)
463 count
+= from
- m_updateFrom
;
464 if ( m_updateCount
< count
)
465 m_updateCount
= count
;
467 else // m_updateFrom >= from
469 int updateLast
= wxMax(m_updateFrom
+ m_updateCount
,
472 m_updateCount
= updateLast
- m_updateFrom
;
477 void wxListBox::RefreshItem(int n
)
479 switch ( m_updateCount
)
482 // refresh this item only
488 // we refresh everything anyhow
492 // add this item to the others which we have to refresh
493 if ( m_updateFrom
< n
)
495 if ( m_updateCount
< n
- m_updateFrom
+ 1 )
496 m_updateCount
= n
- m_updateFrom
+ 1;
498 else // n <= m_updateFrom
500 m_updateCount
+= m_updateFrom
- n
;
506 void wxListBox::RefreshAll()
511 void wxListBox::RefreshHorzScrollbar()
513 m_maxWidth
= 0; // recalculate it
514 m_updateScrollbarX
= TRUE
;
517 void wxListBox::UpdateScrollbars()
519 wxSize size
= GetClientSize();
521 // is our height enough to show all items?
522 int nLines
= GetCount();
523 wxCoord lineHeight
= GetLineHeight();
524 bool showScrollbarY
= nLines
*lineHeight
> size
.y
;
526 // check the width too if required
527 wxCoord charWidth
, maxWidth
;
529 if ( HasHorzScrollbar() )
531 charWidth
= GetCharWidth();
532 maxWidth
= GetMaxWidth();
533 showScrollbarX
= maxWidth
> size
.x
;
535 else // never show it
537 charWidth
= maxWidth
= 0;
538 showScrollbarX
= FALSE
;
541 // what should be the scrollbar range now?
542 int scrollRangeX
= showScrollbarX
543 ? (maxWidth
+ charWidth
- 1) / charWidth
+ 2 // FIXME
545 int scrollRangeY
= showScrollbarY
547 (size
.y
% lineHeight
+ lineHeight
- 1) / lineHeight
550 // reset scrollbars if something changed: either the visibility status
551 // or the range of a scrollbar which is shown
552 if ( (showScrollbarY
!= m_showScrollbarY
) ||
553 (showScrollbarX
!= m_showScrollbarX
) ||
554 (showScrollbarY
&& (scrollRangeY
!= m_scrollRangeY
)) ||
555 (showScrollbarX
&& (scrollRangeX
!= m_scrollRangeX
)) )
558 GetViewStart(&x
, &y
);
559 SetScrollbars(charWidth
, lineHeight
,
560 scrollRangeX
, scrollRangeY
,
563 m_showScrollbarX
= showScrollbarX
;
564 m_showScrollbarY
= showScrollbarY
;
566 m_scrollRangeX
= scrollRangeX
;
567 m_scrollRangeY
= scrollRangeY
;
571 void wxListBox::UpdateItems()
573 // only refresh the items which must be refreshed
574 if ( m_updateCount
== -1 )
577 wxLogTrace(_T("listbox"), _T("Refreshing all"));
583 wxSize size
= GetClientSize();
586 rect
.height
= size
.y
;
587 rect
.y
+= m_updateFrom
*GetLineHeight();
588 rect
.height
= m_updateCount
*GetLineHeight();
590 // we don't need to calculate x position as we always refresh the
592 CalcScrolledPosition(0, rect
.y
, NULL
, &rect
.y
);
594 wxLogTrace(_T("listbox"), _T("Refreshing items %d..%d (%d-%d)"),
595 m_updateFrom
, m_updateFrom
+ m_updateCount
- 1,
596 rect
.GetTop(), rect
.GetBottom());
598 Refresh(TRUE
, &rect
);
602 void wxListBox::OnInternalIdle()
604 if ( m_updateScrollbarY
|| m_updateScrollbarX
)
609 m_updateScrollbarY
= FALSE
;
612 if ( m_currentChanged
)
614 DoEnsureVisible(m_current
);
616 m_currentChanged
= FALSE
;
627 // ----------------------------------------------------------------------------
629 // ----------------------------------------------------------------------------
631 wxBorder
wxListBox::GetDefaultBorder() const
633 return wxBORDER_SUNKEN
;
636 void wxListBox::DoDraw(wxControlRenderer
*renderer
)
638 // adjust the DC to account for scrolling
639 wxDC
& dc
= renderer
->GetDC();
641 dc
.SetFont(GetFont());
643 // get the update rect
644 wxRect rectUpdate
= GetUpdateClientRect();
647 CalcUnscrolledPosition(0, rectUpdate
.GetTop(), NULL
, &yTop
);
648 CalcUnscrolledPosition(0, rectUpdate
.GetBottom(), NULL
, &yBottom
);
650 // get the items which must be redrawn
651 wxCoord lineHeight
= GetLineHeight();
652 size_t itemFirst
= yTop
/ lineHeight
,
653 itemLast
= (yBottom
+ lineHeight
- 1) / lineHeight
,
654 itemMax
= m_strings
.GetCount();
656 if ( itemFirst
>= itemMax
)
659 if ( itemLast
> itemMax
)
663 wxLogTrace(_T("listbox"), _T("Repainting items %d..%d"),
664 itemFirst
, itemLast
);
666 DoDrawRange(renderer
, itemFirst
, itemLast
);
669 void wxListBox::DoDrawRange(wxControlRenderer
*renderer
,
670 int itemFirst
, int itemLast
)
672 renderer
->DrawItems(this, itemFirst
, itemLast
);
675 // ----------------------------------------------------------------------------
677 // ----------------------------------------------------------------------------
679 bool wxListBox::SetFont(const wxFont
& font
)
681 if ( !wxControl::SetFont(font
) )
691 void wxListBox::CalcItemsPerPage()
693 m_lineHeight
= GetRenderer()->GetListboxItemHeight(GetCharHeight());
694 m_itemsPerPage
= GetClientSize().y
/ m_lineHeight
;
697 int wxListBox::GetItemsPerPage() const
699 if ( !m_itemsPerPage
)
701 wxConstCast(this, wxListBox
)->CalcItemsPerPage();
704 return m_itemsPerPage
;
707 wxCoord
wxListBox::GetLineHeight() const
711 wxConstCast(this, wxListBox
)->CalcItemsPerPage();
717 wxCoord
wxListBox::GetMaxWidth() const
719 if ( m_maxWidth
== 0 )
721 wxListBox
*self
= wxConstCast(this, wxListBox
);
723 size_t count
= m_strings
.GetCount();
724 for ( size_t n
= 0; n
< count
; n
++ )
726 GetTextExtent(m_strings
[n
], &width
, NULL
);
727 if ( width
> m_maxWidth
)
729 self
->m_maxWidth
= width
;
730 self
->m_maxWidthItem
= n
;
738 void wxListBox::OnSize(wxSizeEvent
& event
)
740 // recalculate the number of items per page
743 // the scrollbars might [dis]appear
745 m_updateScrollbarY
= TRUE
;
750 void wxListBox::DoSetFirstItem(int n
)
755 void wxListBox::DoSetSize(int x
, int y
,
756 int width
, int height
,
759 if ( GetWindowStyle() & wxLB_INT_HEIGHT
)
761 // we must round up the height to an entire number of rows
763 // the client area must contain an int number of rows, so take borders
765 wxRect rectBorders
= GetRenderer()->GetBorderDimensions(GetBorder());
766 wxCoord hBorders
= rectBorders
.y
+ rectBorders
.height
;
768 wxCoord hLine
= GetLineHeight();
769 height
= ((height
- hBorders
+ hLine
- 1) / hLine
)*hLine
+ hBorders
;
772 wxListBoxBase::DoSetSize(x
, y
, width
, height
, sizeFlags
);
775 wxSize
wxListBox::DoGetBestClientSize() const
780 size_t count
= m_strings
.GetCount();
781 for ( size_t n
= 0; n
< count
; n
++ )
784 GetTextExtent(m_strings
[n
], &w
, &h
);
792 // if the listbox is empty, still give it some non zero (even if
793 // arbitrary) size - otherwise, leave small margin around the strings
797 width
+= 3*GetCharWidth();
800 height
= GetCharHeight();
802 // we need the height of the entire listbox, not just of one line
803 height
*= wxMax(count
, 7);
805 return wxSize(width
, height
);
808 // ----------------------------------------------------------------------------
810 // ----------------------------------------------------------------------------
812 bool wxListBox::SendEvent(wxEventType type
, int item
)
814 wxCommandEvent
event(type
, m_windowId
);
815 event
.SetEventObject(this);
817 // use the current item by default
823 // client data and string parameters only make sense if we have an item
826 if ( HasClientObjectData() )
827 event
.SetClientObject(GetClientObject(item
));
828 else if ( HasClientUntypedData() )
829 event
.SetClientData(GetClientData(item
));
831 event
.SetString(GetString(item
));
834 event
.m_commandInt
= item
;
836 return GetEventHandler()->ProcessEvent(event
);
839 void wxListBox::SetCurrentItem(int n
)
841 if ( n
!= m_current
)
843 if ( m_current
!= -1 )
844 RefreshItem(m_current
);
848 if ( m_current
!= -1 )
850 m_currentChanged
= TRUE
;
852 RefreshItem(m_current
);
855 //else: nothing to do
858 bool wxListBox::FindItem(const wxString
& prefix
, bool strictlyAfter
)
860 int count
= GetCount();
863 // empty listbox, we can't find anything in it
867 // start either from the current item or from the next one if strictlyAfter
872 // the following line will set first correctly to 0 if there is no
873 // selection (m_current == -1)
874 first
= m_current
== count
- 1 ? 0 : m_current
+ 1;
876 else // start with the current
878 first
= m_current
== -1 ? 0 : m_current
;
881 int last
= first
== 0 ? count
- 1 : first
- 1;
883 // if this is not true we'd never exit from the loop below!
884 wxASSERT_MSG( first
< count
&& last
< count
, _T("logic error") );
886 // precompute it outside the loop
887 size_t len
= prefix
.length();
889 // loop over all items in the listbox
890 for ( int item
= first
; item
!= last
; item
< count
- 1 ? item
++ : item
= 0 )
892 if ( wxStrnicmp(m_strings
[item
], prefix
, len
) == 0 )
894 SetCurrentItem(item
);
896 if ( !(GetWindowStyle() & wxLB_MULTIPLE
) )
899 SelectAndNotify(item
);
901 if ( GetWindowStyle() & wxLB_EXTENDED
)
902 AnchorSelection(item
);
913 void wxListBox::EnsureVisible(int n
)
915 if ( m_updateScrollbarY
)
920 m_updateScrollbarY
= FALSE
;
926 void wxListBox::DoEnsureVisible(int n
)
928 if ( !m_showScrollbarY
)
930 // nothing to do - everything is shown anyhow
935 GetViewStart(0, &first
);
938 // we need to scroll upwards, so make the current item appear on top
939 // of the shown range
944 int last
= first
+ GetClientSize().y
/ GetLineHeight() - 1;
947 // scroll down: the current item appears at the bottom of the
949 Scroll(0, n
- (last
- first
));
954 void wxListBox::ChangeCurrent(int diff
)
956 int current
= m_current
== -1 ? 0 : m_current
;
960 int last
= GetCount() - 1;
963 else if ( current
> last
)
966 SetCurrentItem(current
);
969 void wxListBox::ExtendSelection(int itemTo
)
971 // if we don't have the explicit values for selection start/end, make them
973 if ( m_selAnchor
== -1 )
974 m_selAnchor
= m_current
;
979 // swap the start/end of selection range if necessary
980 int itemFrom
= m_selAnchor
;
981 if ( itemFrom
> itemTo
)
983 int itemTmp
= itemFrom
;
988 // the selection should now include all items in the range between the
989 // anchor and the specified item and only them
992 for ( n
= 0; n
< itemFrom
; n
++ )
997 for ( ; n
<= itemTo
; n
++ )
1002 int count
= GetCount();
1003 for ( ; n
< count
; n
++ )
1009 void wxListBox::DoSelect(int item
, bool sel
)
1013 // go to this item first
1014 SetCurrentItem(item
);
1017 // the current item is the one we want to change: either it was just
1018 // changed above to be the same as item or item == -1 in which we case we
1019 // are supposed to use the current one anyhow
1020 if ( m_current
!= -1 )
1023 SetSelection(m_current
, sel
);
1027 void wxListBox::SelectAndNotify(int item
)
1031 SendEvent(wxEVT_COMMAND_LISTBOX_SELECTED
);
1034 void wxListBox::Activate(int item
)
1037 SetCurrentItem(item
);
1041 if ( !(GetWindowStyle() & wxLB_MULTIPLE
) )
1050 SendEvent(wxEVT_COMMAND_LISTBOX_DOUBLECLICKED
);
1054 // ----------------------------------------------------------------------------
1056 // ----------------------------------------------------------------------------
1059 The numArg here is the listbox item index while the strArg is used
1060 differently for the different actions:
1062 a) for wxACTION_LISTBOX_FIND it has the natural meaning: this is the string
1065 b) for wxACTION_LISTBOX_SELECT and wxACTION_LISTBOX_EXTENDSEL it is used
1066 to decide if the listbox should send the notification event (it is empty)
1067 or not (it is not): this allows us to reuse the same action for when the
1068 user is dragging the mouse when it has been released although in the
1069 first case no notification is sent while in the second it is sent.
1071 bool wxListBox::PerformAction(const wxControlAction
& action
,
1073 const wxString
& strArg
)
1075 int item
= (int)numArg
;
1077 if ( action
== wxACTION_LISTBOX_SETFOCUS
)
1079 SetCurrentItem(item
);
1081 else if ( action
== wxACTION_LISTBOX_ACTIVATE
)
1085 else if ( action
== wxACTION_LISTBOX_TOGGLE
)
1090 if ( IsSelected(item
) )
1093 SelectAndNotify(item
);
1095 else if ( action
== wxACTION_LISTBOX_SELECT
)
1099 if ( strArg
.empty() )
1100 SelectAndNotify(item
);
1104 else if ( action
== wxACTION_LISTBOX_SELECTADD
)
1106 else if ( action
== wxACTION_LISTBOX_UNSELECT
)
1108 else if ( action
== wxACTION_LISTBOX_MOVEDOWN
)
1110 else if ( action
== wxACTION_LISTBOX_MOVEUP
)
1112 else if ( action
== wxACTION_LISTBOX_PAGEDOWN
)
1113 ChangeCurrent(GetItemsPerPage());
1114 else if ( action
== wxACTION_LISTBOX_PAGEUP
)
1115 ChangeCurrent(-GetItemsPerPage());
1116 else if ( action
== wxACTION_LISTBOX_START
)
1118 else if ( action
== wxACTION_LISTBOX_END
)
1119 SetCurrentItem(GetCount() - 1);
1120 else if ( action
== wxACTION_LISTBOX_UNSELECTALL
)
1122 else if ( action
== wxACTION_LISTBOX_EXTENDSEL
)
1123 ExtendSelection(item
);
1124 else if ( action
== wxACTION_LISTBOX_FIND
)
1125 FindNextItem(strArg
);
1126 else if ( action
== wxACTION_LISTBOX_ANCHOR
)
1127 AnchorSelection(item
== -1 ? m_current
: item
);
1128 else if ( action
== wxACTION_LISTBOX_SELECTALL
||
1129 action
== wxACTION_LISTBOX_SELTOGGLE
)
1130 wxFAIL_MSG(_T("unimplemented yet"));
1132 return wxControl::PerformAction(action
, numArg
, strArg
);
1137 // ============================================================================
1138 // implementation of wxStdListboxInputHandler
1139 // ============================================================================
1141 wxStdListboxInputHandler::wxStdListboxInputHandler(wxInputHandler
*handler
,
1142 bool toggleOnPressAlways
)
1143 : wxStdInputHandler(handler
)
1146 m_toggleOnPressAlways
= toggleOnPressAlways
;
1147 m_actionMouse
= wxACTION_NONE
;
1148 m_trackMouseOutside
= TRUE
;
1151 int wxStdListboxInputHandler::HitTest(const wxListBox
*lbox
,
1152 const wxMouseEvent
& event
)
1154 int item
= HitTestUnsafe(lbox
, event
);
1156 return FixItemIndex(lbox
, item
);
1159 int wxStdListboxInputHandler::HitTestUnsafe(const wxListBox
*lbox
,
1160 const wxMouseEvent
& event
)
1162 wxPoint pt
= event
.GetPosition();
1163 pt
-= lbox
->GetClientAreaOrigin();
1165 lbox
->CalcUnscrolledPosition(0, pt
.y
, NULL
, &y
);
1166 return y
/ lbox
->GetLineHeight();
1169 int wxStdListboxInputHandler::FixItemIndex(const wxListBox
*lbox
,
1174 // mouse is above the first item
1177 else if ( item
>= lbox
->GetCount() )
1179 // mouse is below the last item
1180 item
= lbox
->GetCount() - 1;
1186 bool wxStdListboxInputHandler::IsValidIndex(const wxListBox
*lbox
, int item
)
1188 return item
>= 0 && item
< lbox
->GetCount();
1192 wxStdListboxInputHandler::SetupCapture(wxListBox
*lbox
,
1193 const wxMouseEvent
& event
,
1196 // we currently only allow selecting with the left mouse button, if we
1197 // do need to allow using other buttons too we might use the code
1200 m_btnCapture
= event
.LeftDown()
1209 wxControlAction action
;
1210 if ( lbox
->HasMultipleSelection() )
1212 if ( lbox
->GetWindowStyle() & wxLB_MULTIPLE
)
1214 if ( m_toggleOnPressAlways
)
1216 // toggle the item right now
1217 action
= wxACTION_LISTBOX_TOGGLE
;
1221 m_actionMouse
= wxACTION_LISTBOX_SETFOCUS
;
1223 else // wxLB_EXTENDED listbox
1225 // simple click in an extended sel listbox clears the old
1226 // selection and adds the clicked item to it then, ctrl-click
1227 // toggles an item to it and shift-click adds a range between
1228 // the old selection anchor and the clicked item
1229 if ( event
.ControlDown() )
1231 lbox
->PerformAction(wxACTION_LISTBOX_ANCHOR
, item
);
1233 action
= wxACTION_LISTBOX_TOGGLE
;
1235 else if ( event
.ShiftDown() )
1237 action
= wxACTION_LISTBOX_EXTENDSEL
;
1239 else // simple click
1241 lbox
->PerformAction(wxACTION_LISTBOX_ANCHOR
, item
);
1243 action
= wxACTION_LISTBOX_SELECT
;
1246 m_actionMouse
= wxACTION_LISTBOX_EXTENDSEL
;
1249 else // single selection
1252 action
= wxACTION_LISTBOX_SELECT
;
1255 // by default we always do track it
1256 m_trackMouseOutside
= TRUE
;
1261 bool wxStdListboxInputHandler::HandleKey(wxInputConsumer
*consumer
,
1262 const wxKeyEvent
& event
,
1265 // we're only interested in the key press events
1266 if ( pressed
&& !event
.AltDown() )
1268 bool isMoveCmd
= TRUE
;
1269 int style
= consumer
->GetInputWindow()->GetWindowStyle();
1271 wxControlAction action
;
1274 int keycode
= event
.GetKeyCode();
1279 action
= wxACTION_LISTBOX_MOVEUP
;
1283 action
= wxACTION_LISTBOX_MOVEDOWN
;
1289 action
= wxACTION_LISTBOX_PAGEUP
;
1295 action
= wxACTION_LISTBOX_PAGEDOWN
;
1299 action
= wxACTION_LISTBOX_START
;
1303 action
= wxACTION_LISTBOX_END
;
1308 if ( style
& wxLB_MULTIPLE
)
1310 action
= wxACTION_LISTBOX_TOGGLE
;
1316 action
= wxACTION_LISTBOX_ACTIVATE
;
1321 if ( (keycode
< 255) && wxIsalnum(keycode
) )
1323 action
= wxACTION_LISTBOX_FIND
;
1324 strArg
= (wxChar
)keycode
;
1330 consumer
->PerformAction(action
, -1, strArg
);
1334 if ( style
& wxLB_SINGLE
)
1336 // the current item is always the one selected
1337 consumer
->PerformAction(wxACTION_LISTBOX_SELECT
);
1339 else if ( style
& wxLB_EXTENDED
)
1341 if ( event
.ShiftDown() )
1342 consumer
->PerformAction(wxACTION_LISTBOX_EXTENDSEL
);
1345 // select the item and make it the new selection anchor
1346 consumer
->PerformAction(wxACTION_LISTBOX_SELECT
);
1347 consumer
->PerformAction(wxACTION_LISTBOX_ANCHOR
);
1350 //else: nothing to do for multiple selection listboxes
1357 return wxStdInputHandler::HandleKey(consumer
, event
, pressed
);
1360 bool wxStdListboxInputHandler::HandleMouse(wxInputConsumer
*consumer
,
1361 const wxMouseEvent
& event
)
1363 wxListBox
*lbox
= wxStaticCast(consumer
->GetInputWindow(), wxListBox
);
1364 int item
= HitTest(lbox
, event
);
1365 wxControlAction action
;
1367 // when the left mouse button is pressed, capture the mouse and track the
1368 // item under mouse (if the mouse leaves the window, we will still be
1369 // getting the mouse move messages generated by wxScrollWindow)
1370 if ( event
.LeftDown() )
1372 // capture the mouse to track the selected item
1373 lbox
->CaptureMouse();
1375 action
= SetupCapture(lbox
, event
, item
);
1377 else if ( m_btnCapture
&& event
.ButtonUp(m_btnCapture
) )
1379 // when the left mouse button is released, release the mouse too
1380 wxWindow
*winCapture
= wxWindow::GetCapture();
1383 winCapture
->ReleaseMouse();
1386 action
= m_actionMouse
;
1388 //else: the mouse wasn't presed over the listbox, only released here
1390 else if ( event
.LeftDClick() )
1392 action
= wxACTION_LISTBOX_ACTIVATE
;
1397 lbox
->PerformAction(action
, item
);
1402 return wxStdInputHandler::HandleMouse(consumer
, event
);
1405 bool wxStdListboxInputHandler::HandleMouseMove(wxInputConsumer
*consumer
,
1406 const wxMouseEvent
& event
)
1408 wxWindow
*winCapture
= wxWindow::GetCapture();
1409 if ( winCapture
&& (event
.GetEventObject() == winCapture
) )
1411 wxListBox
*lbox
= wxStaticCast(consumer
->GetInputWindow(), wxListBox
);
1413 if ( !m_btnCapture
|| !m_trackMouseOutside
)
1415 // someone captured the mouse for us (we always set m_btnCapture
1416 // when we do it ourselves): in this case we only react to
1417 // the mouse messages when they happen inside the listbox
1418 if ( lbox
->HitTest(event
.GetPosition()) != wxHT_WINDOW_INSIDE
)
1422 int item
= HitTest(lbox
, event
);
1423 if ( !m_btnCapture
)
1425 // now that we have the mouse inside the listbox, do capture it
1426 // normally - but ensure that we will still ignore the outside
1428 SetupCapture(lbox
, event
, item
);
1430 m_trackMouseOutside
= FALSE
;
1433 if ( IsValidIndex(lbox
, item
) )
1435 // pass something into strArg to tell the listbox that it shouldn't
1436 // send the notification message: see PerformAction() above
1437 lbox
->PerformAction(m_actionMouse
, item
, _T("no"));
1439 // else: don't pass invalid index to the listbox
1441 else // we don't have capture any more
1445 // if we lost capture unexpectedly (someone else took the capture
1446 // from us), return to a consistent state
1451 return wxStdInputHandler::HandleMouseMove(consumer
, event
);
1454 #endif // wxUSE_LISTBOX