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
)
53 EVT_IDLE(wxListBox::OnIdle
)
56 // ----------------------------------------------------------------------------
58 // ----------------------------------------------------------------------------
60 void wxListBox::Init()
62 // will be calculated later when needed
69 // no items hence no current item
72 m_currentChanged
= FALSE
;
74 // no need to update anything initially
77 // no scrollbars to show nor update
81 m_showScrollbarY
= FALSE
;
84 bool wxListBox::Create(wxWindow
*parent
,
89 const wxString choices
[],
91 const wxValidator
& validator
,
94 // for compatibility accept both the new and old styles - they mean the
96 if ( style
& wxLB_ALWAYS_SB
)
97 style
|= wxALWAYS_SHOW_SB
;
99 // if we don't have neither multiple nor extended flag, we must have the
100 // single selection listbox
101 if ( !(style
& (wxLB_MULTIPLE
| wxLB_EXTENDED
)) )
102 style
|= wxLB_SINGLE
;
104 #if wxUSE_TWO_WINDOWS
105 style
|= wxVSCROLL
|wxHSCROLL
;
106 if ((style
& wxBORDER_MASK
) == 0)
107 style
|= wxBORDER_SUNKEN
;
110 if ( !wxControl::Create(parent
, id
, pos
, size
, style
,
111 wxDefaultValidator
, name
) )
116 if ( style
& wxLB_SORT
)
117 m_strings
= wxArrayString(TRUE
/* auto sort */);
123 CreateInputHandler(wxINP_HANDLER_LISTBOX
);
128 wxListBox::~wxListBox()
130 // call this just to free the client data -- and avoid leaking memory
134 // ----------------------------------------------------------------------------
135 // adding/inserting strings
136 // ----------------------------------------------------------------------------
138 int wxListBox::DoAppend(const wxString
& item
)
140 size_t index
= m_strings
.Add(item
);
141 m_itemsClientData
.Insert(NULL
, index
);
143 m_updateScrollbarY
= TRUE
;
145 if ( HasHorzScrollbar() )
147 // has the max width increased?
149 GetTextExtent(item
, &width
, NULL
);
150 if ( width
> m_maxWidth
)
153 m_maxWidthItem
= index
;
154 m_updateScrollbarX
= TRUE
;
158 RefreshFromItemToEnd(index
);
163 void wxListBox::DoInsertItems(const wxArrayString
& items
, int pos
)
165 // the position of the item being added to a sorted listbox can't be
167 wxCHECK_RET( !IsSorted(), _T("can't insert items into sorted listbox") );
169 size_t count
= items
.GetCount();
170 for ( size_t n
= 0; n
< count
; n
++ )
172 m_strings
.Insert(items
[n
], pos
+ n
);
173 m_itemsClientData
.Insert(NULL
, pos
+ n
);
176 // the number of items has changed so we might have to show the scrollbar
177 m_updateScrollbarY
= TRUE
;
179 // the max width also might have changed - just recalculate it instead of
180 // keeping track of it here, this is probably more efficient for a typical
182 RefreshHorzScrollbar();
184 // note that we have to refresh all the items after the ones we inserted,
185 // not just these items
186 RefreshFromItemToEnd(pos
);
189 void wxListBox::DoSetItems(const wxArrayString
& items
, void **clientData
)
193 size_t count
= items
.GetCount();
197 m_strings
.Alloc(count
);
198 m_itemsClientData
.Alloc(count
);
199 for ( size_t n
= 0; n
< count
; n
++ )
201 size_t index
= m_strings
.Add(items
[n
]);
202 m_itemsClientData
.Insert(clientData
? clientData
[n
] : NULL
, index
);
205 m_updateScrollbarY
= TRUE
;
210 void wxListBox::SetString(int n
, const wxString
& s
)
212 if ( HasHorzScrollbar() )
214 // we need to update m_maxWidth as changing the string may cause the
215 // horz scrollbar [dis]appear
218 GetTextExtent(s
, &width
, NULL
);
220 // it might have increased if the new string is long
221 if ( width
> m_maxWidth
)
225 m_updateScrollbarX
= TRUE
;
227 // or also decreased if the old string was the longest one
228 else if ( n
== m_maxWidthItem
)
230 RefreshHorzScrollbar();
233 else // no horz scrollbar
241 // ----------------------------------------------------------------------------
243 // ----------------------------------------------------------------------------
245 void wxListBox::DoClear()
249 if ( HasClientObjectData() )
251 size_t count
= m_itemsClientData
.GetCount();
252 for ( size_t n
= 0; n
< count
; n
++ )
254 delete (wxClientData
*) m_itemsClientData
[n
];
258 m_itemsClientData
.Clear();
259 m_selections
.Clear();
264 void wxListBox::Clear()
268 m_updateScrollbarY
= TRUE
;
270 RefreshHorzScrollbar();
275 void wxListBox::Delete(int n
)
277 wxCHECK_RET( n
>= 0 && n
< GetCount(),
278 _T("invalid index in wxListBox::Delete") );
280 // do it before removing the index as otherwise the last item will not be
281 // refreshed (as GetCount() will be decremented)
282 RefreshFromItemToEnd(n
);
284 m_strings
.RemoveAt(n
);
286 if ( HasClientObjectData() )
288 delete (wxClientData
*)m_itemsClientData
[n
];
291 m_itemsClientData
.RemoveAt(n
);
293 // when the item disappears we must not keep using its index
294 if ( n
== m_current
)
298 else if ( n
< m_current
)
302 //else: current item may stay
304 // update the selections array: the indices of all seletected items after
305 // the one being deleted must change and the item itselfm ust be removed
306 int index
= wxNOT_FOUND
;
307 size_t count
= m_selections
.GetCount();
308 for ( size_t item
= 0; item
< count
; item
++ )
310 if ( m_selections
[item
] == n
)
312 // remember to delete it later
315 else if ( m_selections
[item
] > n
)
317 // to account for the index shift
318 m_selections
[item
]--;
320 //else: nothing changed for this one
323 if ( index
!= wxNOT_FOUND
)
325 m_selections
.RemoveAt(index
);
328 // the number of items has changed, hence the scrollbar may disappear
329 m_updateScrollbarY
= TRUE
;
331 // finally, if the longest item was deleted the scrollbar may disappear
332 if ( n
== m_maxWidthItem
)
334 RefreshHorzScrollbar();
338 // ----------------------------------------------------------------------------
339 // client data handling
340 // ----------------------------------------------------------------------------
342 void wxListBox::DoSetItemClientData(int n
, void* clientData
)
344 m_itemsClientData
[n
] = clientData
;
347 void *wxListBox::DoGetItemClientData(int n
) const
349 return m_itemsClientData
[n
];
352 void wxListBox::DoSetItemClientObject(int n
, wxClientData
* clientData
)
354 m_itemsClientData
[n
] = clientData
;
357 wxClientData
* wxListBox::DoGetItemClientObject(int n
) const
359 return (wxClientData
*)m_itemsClientData
[n
];
362 // ----------------------------------------------------------------------------
364 // ----------------------------------------------------------------------------
366 void wxListBox::SetSelection(int n
, bool select
)
370 if ( m_selections
.Index(n
) == wxNOT_FOUND
)
372 if ( !HasMultipleSelection() )
374 // selecting an item in a single selection listbox deselects
383 //else: already selected
387 int index
= m_selections
.Index(n
);
388 if ( index
!= wxNOT_FOUND
)
390 m_selections
.RemoveAt(index
);
397 // sanity check: a single selection listbox can't have more than one item
399 wxASSERT_MSG( HasMultipleSelection() || (m_selections
.GetCount() < 2),
400 _T("multiple selected items in single selection lbox?") );
404 // the newly selected item becomes the current one
409 int wxListBox::GetSelection() const
411 wxCHECK_MSG( !HasMultipleSelection(), -1,
412 _T("use wxListBox::GetSelections for ths listbox") );
414 return m_selections
.IsEmpty() ? -1 : m_selections
[0];
417 int wxCMPFUNC_CONV
wxCompareInts(int *n
, int *m
)
422 int wxListBox::GetSelections(wxArrayInt
& selections
) const
424 // always return sorted array to the user
425 selections
= m_selections
;
426 size_t count
= m_selections
.GetCount();
428 // don't call sort on an empty array
431 selections
.Sort(wxCompareInts
);
437 // ----------------------------------------------------------------------------
438 // refresh logic: we use delayed refreshing which allows to avoid multiple
439 // refreshes (and hence flicker) in case when several listbox items are
440 // added/deleted/changed subsequently
441 // ----------------------------------------------------------------------------
443 void wxListBox::RefreshFromItemToEnd(int from
)
445 RefreshItems(from
, GetCount() - from
);
448 void wxListBox::RefreshItems(int from
, int count
)
450 switch ( m_updateCount
)
454 m_updateCount
= count
;
458 // we refresh everything anyhow
462 // add these items to the others which we have to refresh
463 if ( m_updateFrom
< from
)
465 count
+= from
- m_updateFrom
;
466 if ( m_updateCount
< count
)
467 m_updateCount
= count
;
469 else // m_updateFrom >= from
471 int updateLast
= wxMax(m_updateFrom
+ m_updateCount
,
474 m_updateCount
= updateLast
- m_updateFrom
;
479 void wxListBox::RefreshItem(int n
)
481 switch ( m_updateCount
)
484 // refresh this item only
490 // we refresh everything anyhow
494 // add this item to the others which we have to refresh
495 if ( m_updateFrom
< n
)
497 if ( m_updateCount
< n
- m_updateFrom
+ 1 )
498 m_updateCount
= n
- m_updateFrom
+ 1;
500 else // n <= m_updateFrom
502 m_updateCount
+= m_updateFrom
- n
;
508 void wxListBox::RefreshAll()
513 void wxListBox::RefreshHorzScrollbar()
515 m_maxWidth
= 0; // recalculate it
516 m_updateScrollbarX
= TRUE
;
519 void wxListBox::UpdateScrollbars()
521 wxSize size
= GetClientSize();
523 // is our height enough to show all items?
524 int nLines
= GetCount();
525 wxCoord lineHeight
= GetLineHeight();
526 bool showScrollbarY
= nLines
*lineHeight
> size
.y
;
528 // check the width too if required
529 wxCoord charWidth
, maxWidth
;
531 if ( HasHorzScrollbar() )
533 charWidth
= GetCharWidth();
534 maxWidth
= GetMaxWidth();
535 showScrollbarX
= maxWidth
> size
.x
;
537 else // never show it
539 charWidth
= maxWidth
= 0;
540 showScrollbarX
= FALSE
;
543 // what should be the scrollbar range now?
544 int scrollRangeX
= showScrollbarX
545 ? (maxWidth
+ charWidth
- 1) / charWidth
+ 2 // FIXME
547 int scrollRangeY
= showScrollbarY
549 (size
.y
% lineHeight
+ lineHeight
- 1) / lineHeight
552 // reset scrollbars if something changed: either the visibility status
553 // or the range of a scrollbar which is shown
554 if ( (showScrollbarY
!= m_showScrollbarY
) ||
555 (showScrollbarX
!= m_showScrollbarX
) ||
556 (showScrollbarY
&& (scrollRangeY
!= m_scrollRangeY
)) ||
557 (showScrollbarX
&& (scrollRangeX
!= m_scrollRangeX
)) )
560 GetViewStart(&x
, &y
);
561 SetScrollbars(charWidth
, lineHeight
,
562 scrollRangeX
, scrollRangeY
,
565 m_showScrollbarX
= showScrollbarX
;
566 m_showScrollbarY
= showScrollbarY
;
568 m_scrollRangeX
= scrollRangeX
;
569 m_scrollRangeY
= scrollRangeY
;
573 void wxListBox::UpdateItems()
575 // only refresh the items which must be refreshed
576 if ( m_updateCount
== -1 )
579 wxLogTrace(_T("listbox"), _T("Refreshing all"));
585 wxSize size
= GetClientSize();
588 rect
.height
= size
.y
;
589 rect
.y
+= m_updateFrom
*GetLineHeight();
590 rect
.height
= m_updateCount
*GetLineHeight();
592 // we don't need to calculate x position as we always refresh the
594 CalcScrolledPosition(0, rect
.y
, NULL
, &rect
.y
);
596 wxLogTrace(_T("listbox"), _T("Refreshing items %d..%d (%d-%d)"),
597 m_updateFrom
, m_updateFrom
+ m_updateCount
- 1,
598 rect
.GetTop(), rect
.GetBottom());
600 Refresh(TRUE
, &rect
);
604 void wxListBox::OnIdle(wxIdleEvent
& event
)
606 if ( m_updateScrollbarY
|| m_updateScrollbarX
)
611 m_updateScrollbarY
= FALSE
;
614 if ( m_currentChanged
)
616 DoEnsureVisible(m_current
);
618 m_currentChanged
= FALSE
;
631 // ----------------------------------------------------------------------------
633 // ----------------------------------------------------------------------------
635 wxBorder
wxListBox::GetDefaultBorder() const
637 return wxBORDER_SUNKEN
;
640 void wxListBox::DoDraw(wxControlRenderer
*renderer
)
642 // adjust the DC to account for scrolling
643 wxDC
& dc
= renderer
->GetDC();
645 dc
.SetFont(GetFont());
647 // get the update rect
648 wxRect rectUpdate
= GetUpdateClientRect();
651 CalcUnscrolledPosition(0, rectUpdate
.GetTop(), NULL
, &yTop
);
652 CalcUnscrolledPosition(0, rectUpdate
.GetBottom(), NULL
, &yBottom
);
654 // get the items which must be redrawn
655 wxCoord lineHeight
= GetLineHeight();
656 size_t itemFirst
= yTop
/ lineHeight
,
657 itemLast
= (yBottom
+ lineHeight
- 1) / lineHeight
,
658 itemMax
= m_strings
.GetCount();
660 if ( itemFirst
>= itemMax
)
663 if ( itemLast
> itemMax
)
667 wxLogTrace(_T("listbox"), _T("Repainting items %d..%d"),
668 itemFirst
, itemLast
);
670 DoDrawRange(renderer
, itemFirst
, itemLast
);
673 void wxListBox::DoDrawRange(wxControlRenderer
*renderer
,
674 int itemFirst
, int itemLast
)
676 renderer
->DrawItems(this, itemFirst
, itemLast
);
679 // ----------------------------------------------------------------------------
681 // ----------------------------------------------------------------------------
683 bool wxListBox::SetFont(const wxFont
& font
)
685 if ( !wxControl::SetFont(font
) )
695 void wxListBox::CalcItemsPerPage()
697 m_lineHeight
= GetRenderer()->GetListboxItemHeight(GetCharHeight());
698 m_itemsPerPage
= GetClientSize().y
/ m_lineHeight
;
701 int wxListBox::GetItemsPerPage() const
703 if ( !m_itemsPerPage
)
705 wxConstCast(this, wxListBox
)->CalcItemsPerPage();
708 return m_itemsPerPage
;
711 wxCoord
wxListBox::GetLineHeight() const
715 wxConstCast(this, wxListBox
)->CalcItemsPerPage();
721 wxCoord
wxListBox::GetMaxWidth() const
723 if ( m_maxWidth
== 0 )
725 wxListBox
*self
= wxConstCast(this, wxListBox
);
727 size_t count
= m_strings
.GetCount();
728 for ( size_t n
= 0; n
< count
; n
++ )
730 GetTextExtent(m_strings
[n
], &width
, NULL
);
731 if ( width
> m_maxWidth
)
733 self
->m_maxWidth
= width
;
734 self
->m_maxWidthItem
= n
;
742 void wxListBox::OnSize(wxSizeEvent
& event
)
744 // recalculate the number of items per page
747 // the scrollbars might [dis]appear
749 m_updateScrollbarY
= TRUE
;
754 void wxListBox::DoSetFirstItem(int n
)
759 void wxListBox::DoSetSize(int x
, int y
,
760 int width
, int height
,
763 if ( GetWindowStyle() & wxLB_INT_HEIGHT
)
765 // we must round up the height to an entire number of rows
767 // the client area must contain an int number of rows, so take borders
769 wxRect rectBorders
= GetRenderer()->GetBorderDimensions(GetBorder());
770 wxCoord hBorders
= rectBorders
.y
+ rectBorders
.height
;
772 wxCoord hLine
= GetLineHeight();
773 height
= ((height
- hBorders
+ hLine
- 1) / hLine
)*hLine
+ hBorders
;
776 wxListBoxBase::DoSetSize(x
, y
, width
, height
);
779 wxSize
wxListBox::DoGetBestClientSize() const
784 size_t count
= m_strings
.GetCount();
785 for ( size_t n
= 0; n
< count
; n
++ )
788 GetTextExtent(m_strings
[n
], &w
, &h
);
796 // if the listbox is empty, still give it some non zero (even if
797 // arbitrary) size - otherwise, leave small margin around the strings
801 width
+= 3*GetCharWidth();
804 height
= GetCharHeight();
806 // we need the height of the entire listbox, not just of one line
807 height
*= wxMax(count
, 7);
809 return wxSize(width
, height
);
812 // ----------------------------------------------------------------------------
814 // ----------------------------------------------------------------------------
816 bool wxListBox::SendEvent(wxEventType type
, int item
)
818 wxCommandEvent
event(type
, m_windowId
);
819 event
.SetEventObject(this);
821 // use the current item by default
827 // client data and string parameters only make sense if we have an item
830 if ( HasClientObjectData() )
831 event
.SetClientObject(GetClientObject(item
));
832 else if ( HasClientUntypedData() )
833 event
.SetClientData(GetClientData(item
));
835 event
.SetString(GetString(item
));
838 event
.m_commandInt
= item
;
840 return GetEventHandler()->ProcessEvent(event
);
843 void wxListBox::SetCurrentItem(int n
)
845 if ( n
!= m_current
)
847 if ( m_current
!= -1 )
848 RefreshItem(m_current
);
852 if ( m_current
!= -1 )
854 m_currentChanged
= TRUE
;
856 RefreshItem(m_current
);
859 //else: nothing to do
862 bool wxListBox::FindItem(const wxString
& prefix
, bool strictlyAfter
)
864 int count
= GetCount();
867 // empty listbox, we can't find anything in it
871 // start either from the current item or from the next one if strictlyAfter
876 // the following line will set first correctly to 0 if there is no
877 // selection (m_current == -1)
878 first
= m_current
== count
- 1 ? 0 : m_current
+ 1;
880 else // start with the current
882 first
= m_current
== -1 ? 0 : m_current
;
885 int last
= first
== 0 ? count
- 1 : first
- 1;
887 // if this is not true we'd never exit from the loop below!
888 wxASSERT_MSG( first
< count
&& last
< count
, _T("logic error") );
890 // precompute it outside the loop
891 size_t len
= prefix
.length();
893 // loop over all items in the listbox
894 for ( int item
= first
; item
!= last
; item
< count
- 1 ? item
++ : item
= 0 )
896 if ( wxStrnicmp(m_strings
[item
], prefix
, len
) == 0 )
898 SetCurrentItem(item
);
900 if ( !(GetWindowStyle() & wxLB_MULTIPLE
) )
903 SelectAndNotify(item
);
905 if ( GetWindowStyle() & wxLB_EXTENDED
)
906 AnchorSelection(item
);
917 void wxListBox::EnsureVisible(int n
)
919 if ( m_updateScrollbarY
)
924 m_updateScrollbarY
= FALSE
;
930 void wxListBox::DoEnsureVisible(int n
)
932 if ( !m_showScrollbarY
)
934 // nothing to do - everything is shown anyhow
939 GetViewStart(0, &first
);
942 // we need to scroll upwards, so make the current item appear on top
943 // of the shown range
948 int last
= first
+ GetClientSize().y
/ GetLineHeight() - 1;
951 // scroll down: the current item appears at the bottom of the
953 Scroll(0, n
- (last
- first
));
958 void wxListBox::ChangeCurrent(int diff
)
960 int current
= m_current
== -1 ? 0 : m_current
;
964 int last
= GetCount() - 1;
967 else if ( current
> last
)
970 SetCurrentItem(current
);
973 void wxListBox::ExtendSelection(int itemTo
)
975 // if we don't have the explicit values for selection start/end, make them
977 if ( m_selAnchor
== -1 )
978 m_selAnchor
= m_current
;
983 // swap the start/end of selection range if necessary
984 int itemFrom
= m_selAnchor
;
985 if ( itemFrom
> itemTo
)
987 int itemTmp
= itemFrom
;
992 // the selection should now include all items in the range between the
993 // anchor and the specified item and only them
996 for ( n
= 0; n
< itemFrom
; n
++ )
1001 for ( ; n
<= itemTo
; n
++ )
1006 int count
= GetCount();
1007 for ( ; n
< count
; n
++ )
1013 void wxListBox::DoSelect(int item
, bool sel
)
1017 // go to this item first
1018 SetCurrentItem(item
);
1021 // the current item is the one we want to change: either it was just
1022 // changed above to be the same as item or item == -1 in which we case we
1023 // are supposed to use the current one anyhow
1024 if ( m_current
!= -1 )
1027 SetSelection(m_current
, sel
);
1031 void wxListBox::SelectAndNotify(int item
)
1035 SendEvent(wxEVT_COMMAND_LISTBOX_SELECTED
);
1038 void wxListBox::Activate(int item
)
1041 SetCurrentItem(item
);
1045 if ( !(GetWindowStyle() & wxLB_MULTIPLE
) )
1054 SendEvent(wxEVT_COMMAND_LISTBOX_DOUBLECLICKED
);
1058 // ----------------------------------------------------------------------------
1060 // ----------------------------------------------------------------------------
1063 The numArg here is the listbox item index while the strArg is used
1064 differently for the different actions:
1066 a) for wxACTION_LISTBOX_FIND it has the natural meaning: this is the string
1069 b) for wxACTION_LISTBOX_SELECT and wxACTION_LISTBOX_EXTENDSEL it is used
1070 to decide if the listbox should send the notification event (it is empty)
1071 or not (it is not): this allows us to reuse the same action for when the
1072 user is dragging the mouse when it has been released although in the
1073 first case no notification is sent while in the second it is sent.
1075 bool wxListBox::PerformAction(const wxControlAction
& action
,
1077 const wxString
& strArg
)
1079 int item
= (int)numArg
;
1081 if ( action
== wxACTION_LISTBOX_SETFOCUS
)
1083 SetCurrentItem(item
);
1085 else if ( action
== wxACTION_LISTBOX_ACTIVATE
)
1089 else if ( action
== wxACTION_LISTBOX_TOGGLE
)
1094 if ( IsSelected(item
) )
1097 SelectAndNotify(item
);
1099 else if ( action
== wxACTION_LISTBOX_SELECT
)
1103 if ( strArg
.empty() )
1104 SelectAndNotify(item
);
1108 else if ( action
== wxACTION_LISTBOX_SELECTADD
)
1110 else if ( action
== wxACTION_LISTBOX_UNSELECT
)
1112 else if ( action
== wxACTION_LISTBOX_MOVEDOWN
)
1114 else if ( action
== wxACTION_LISTBOX_MOVEUP
)
1116 else if ( action
== wxACTION_LISTBOX_PAGEDOWN
)
1117 ChangeCurrent(GetItemsPerPage());
1118 else if ( action
== wxACTION_LISTBOX_PAGEUP
)
1119 ChangeCurrent(-GetItemsPerPage());
1120 else if ( action
== wxACTION_LISTBOX_START
)
1122 else if ( action
== wxACTION_LISTBOX_END
)
1123 SetCurrentItem(GetCount() - 1);
1124 else if ( action
== wxACTION_LISTBOX_UNSELECTALL
)
1126 else if ( action
== wxACTION_LISTBOX_EXTENDSEL
)
1127 ExtendSelection(item
);
1128 else if ( action
== wxACTION_LISTBOX_FIND
)
1129 FindNextItem(strArg
);
1130 else if ( action
== wxACTION_LISTBOX_ANCHOR
)
1131 AnchorSelection(item
== -1 ? m_current
: item
);
1132 else if ( action
== wxACTION_LISTBOX_SELECTALL
||
1133 action
== wxACTION_LISTBOX_SELTOGGLE
)
1134 wxFAIL_MSG(_T("unimplemented yet"));
1136 return wxControl::PerformAction(action
, numArg
, strArg
);
1141 // ============================================================================
1142 // implementation of wxStdListboxInputHandler
1143 // ============================================================================
1145 wxStdListboxInputHandler::wxStdListboxInputHandler(wxInputHandler
*handler
,
1146 bool toggleOnPressAlways
)
1147 : wxStdInputHandler(handler
)
1150 m_toggleOnPressAlways
= toggleOnPressAlways
;
1151 m_actionMouse
= wxACTION_NONE
;
1152 m_trackMouseOutside
= TRUE
;
1155 int wxStdListboxInputHandler::HitTest(const wxListBox
*lbox
,
1156 const wxMouseEvent
& event
)
1158 int item
= HitTestUnsafe(lbox
, event
);
1160 return FixItemIndex(lbox
, item
);
1163 int wxStdListboxInputHandler::HitTestUnsafe(const wxListBox
*lbox
,
1164 const wxMouseEvent
& event
)
1166 wxPoint pt
= event
.GetPosition();
1167 pt
-= lbox
->GetClientAreaOrigin();
1169 lbox
->CalcUnscrolledPosition(0, pt
.y
, NULL
, &y
);
1170 return y
/ lbox
->GetLineHeight();
1173 int wxStdListboxInputHandler::FixItemIndex(const wxListBox
*lbox
,
1178 // mouse is above the first item
1181 else if ( item
>= lbox
->GetCount() )
1183 // mouse is below the last item
1184 item
= lbox
->GetCount() - 1;
1190 bool wxStdListboxInputHandler::IsValidIndex(const wxListBox
*lbox
, int item
)
1192 return item
>= 0 && item
< lbox
->GetCount();
1196 wxStdListboxInputHandler::SetupCapture(wxListBox
*lbox
,
1197 const wxMouseEvent
& event
,
1200 // we currently only allow selecting with the left mouse button, if we
1201 // do need to allow using other buttons too we might use the code
1204 m_btnCapture
= event
.LeftDown()
1213 wxControlAction action
;
1214 if ( lbox
->HasMultipleSelection() )
1216 if ( lbox
->GetWindowStyle() & wxLB_MULTIPLE
)
1218 if ( m_toggleOnPressAlways
)
1220 // toggle the item right now
1221 action
= wxACTION_LISTBOX_TOGGLE
;
1225 m_actionMouse
= wxACTION_LISTBOX_SETFOCUS
;
1227 else // wxLB_EXTENDED listbox
1229 // simple click in an extended sel listbox clears the old
1230 // selection and adds the clicked item to it then, ctrl-click
1231 // toggles an item to it and shift-click adds a range between
1232 // the old selection anchor and the clicked item
1233 if ( event
.ControlDown() )
1235 lbox
->PerformAction(wxACTION_LISTBOX_ANCHOR
, item
);
1237 action
= wxACTION_LISTBOX_TOGGLE
;
1239 else if ( event
.ShiftDown() )
1241 action
= wxACTION_LISTBOX_EXTENDSEL
;
1243 else // simple click
1245 lbox
->PerformAction(wxACTION_LISTBOX_ANCHOR
, item
);
1247 action
= wxACTION_LISTBOX_SELECT
;
1250 m_actionMouse
= wxACTION_LISTBOX_EXTENDSEL
;
1253 else // single selection
1256 action
= wxACTION_LISTBOX_SELECT
;
1259 // by default we always do track it
1260 m_trackMouseOutside
= TRUE
;
1265 bool wxStdListboxInputHandler::HandleKey(wxInputConsumer
*consumer
,
1266 const wxKeyEvent
& event
,
1269 // we're only interested in the key press events
1270 if ( pressed
&& !event
.AltDown() )
1272 bool isMoveCmd
= TRUE
;
1273 int style
= consumer
->GetInputWindow()->GetWindowStyle();
1275 wxControlAction action
;
1278 int keycode
= event
.GetKeyCode();
1283 action
= wxACTION_LISTBOX_MOVEUP
;
1287 action
= wxACTION_LISTBOX_MOVEDOWN
;
1293 action
= wxACTION_LISTBOX_PAGEUP
;
1299 action
= wxACTION_LISTBOX_PAGEDOWN
;
1303 action
= wxACTION_LISTBOX_START
;
1307 action
= wxACTION_LISTBOX_END
;
1312 if ( style
& wxLB_MULTIPLE
)
1314 action
= wxACTION_LISTBOX_TOGGLE
;
1320 action
= wxACTION_LISTBOX_ACTIVATE
;
1325 if ( (keycode
< 255) && wxIsalnum(keycode
) )
1327 action
= wxACTION_LISTBOX_FIND
;
1328 strArg
= (wxChar
)keycode
;
1334 consumer
->PerformAction(action
, -1, strArg
);
1338 if ( style
& wxLB_SINGLE
)
1340 // the current item is always the one selected
1341 consumer
->PerformAction(wxACTION_LISTBOX_SELECT
);
1343 else if ( style
& wxLB_EXTENDED
)
1345 if ( event
.ShiftDown() )
1346 consumer
->PerformAction(wxACTION_LISTBOX_EXTENDSEL
);
1349 // select the item and make it the new selection anchor
1350 consumer
->PerformAction(wxACTION_LISTBOX_SELECT
);
1351 consumer
->PerformAction(wxACTION_LISTBOX_ANCHOR
);
1354 //else: nothing to do for multiple selection listboxes
1361 return wxStdInputHandler::HandleKey(consumer
, event
, pressed
);
1364 bool wxStdListboxInputHandler::HandleMouse(wxInputConsumer
*consumer
,
1365 const wxMouseEvent
& event
)
1367 wxListBox
*lbox
= wxStaticCast(consumer
->GetInputWindow(), wxListBox
);
1368 int item
= HitTest(lbox
, event
);
1369 wxControlAction action
;
1371 // when the left mouse button is pressed, capture the mouse and track the
1372 // item under mouse (if the mouse leaves the window, we will still be
1373 // getting the mouse move messages generated by wxScrollWindow)
1374 if ( event
.LeftDown() )
1376 // capture the mouse to track the selected item
1377 lbox
->CaptureMouse();
1379 action
= SetupCapture(lbox
, event
, item
);
1381 else if ( m_btnCapture
&& event
.ButtonUp(m_btnCapture
) )
1383 // when the left mouse button is released, release the mouse too
1384 wxWindow
*winCapture
= wxWindow::GetCapture();
1387 winCapture
->ReleaseMouse();
1390 action
= m_actionMouse
;
1392 //else: the mouse wasn't presed over the listbox, only released here
1394 else if ( event
.LeftDClick() )
1396 action
= wxACTION_LISTBOX_ACTIVATE
;
1401 lbox
->PerformAction(action
, item
);
1406 return wxStdInputHandler::HandleMouse(consumer
, event
);
1409 bool wxStdListboxInputHandler::HandleMouseMove(wxInputConsumer
*consumer
,
1410 const wxMouseEvent
& event
)
1412 wxWindow
*winCapture
= wxWindow::GetCapture();
1413 if ( winCapture
&& (event
.GetEventObject() == winCapture
) )
1415 wxListBox
*lbox
= wxStaticCast(consumer
->GetInputWindow(), wxListBox
);
1417 if ( !m_btnCapture
|| !m_trackMouseOutside
)
1419 // someone captured the mouse for us (we always set m_btnCapture
1420 // when we do it ourselves): in this case we only react to
1421 // the mouse messages when they happen inside the listbox
1422 if ( lbox
->HitTest(event
.GetPosition()) != wxHT_WINDOW_INSIDE
)
1426 int item
= HitTest(lbox
, event
);
1427 if ( !m_btnCapture
)
1429 // now that we have the mouse inside the listbox, do capture it
1430 // normally - but ensure that we will still ignore the outside
1432 SetupCapture(lbox
, event
, item
);
1434 m_trackMouseOutside
= FALSE
;
1437 if ( IsValidIndex(lbox
, item
) )
1439 // pass something into strArg to tell the listbox that it shouldn't
1440 // send the notification message: see PerformAction() above
1441 lbox
->PerformAction(m_actionMouse
, item
, _T("no"));
1443 // else: don't pass invalid index to the listbox
1445 else // we don't have capture any more
1449 // if we lost capture unexpectedly (someone else took the capture
1450 // from us), return to a consistent state
1455 return wxStdInputHandler::HandleMouseMove(consumer
, event
);
1458 #endif // wxUSE_LISTBOX