1 /////////////////////////////////////////////////////////////////////////////
2 // Name: univ/listbox.cpp
3 // Purpose: wxListBox implementation
4 // Author: Vadim Zeitlin
8 // Copyright: (c) 2000 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
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 ( !wxControl::Create(parent
, id
, pos
, size
, style
, wxDefaultValidator
, name
) )
109 if ( style
& wxLB_SORT
)
110 m_strings
= wxArrayString(TRUE
/* auto sort */);
116 CreateInputHandler(wxINP_HANDLER_LISTBOX
);
121 wxListBox::~wxListBox()
125 // ----------------------------------------------------------------------------
126 // adding/inserting strings
127 // ----------------------------------------------------------------------------
129 int wxListBox::DoAppend(const wxString
& item
)
131 size_t index
= m_strings
.Add(item
);
132 m_itemsClientData
.Insert(NULL
, index
);
134 m_updateScrollbarY
= TRUE
;
136 if ( HasHorzScrollbar() )
138 // has the max width increased?
140 GetTextExtent(item
, &width
, NULL
);
141 if ( width
> m_maxWidth
)
144 m_maxWidthItem
= index
;
145 m_updateScrollbarX
= TRUE
;
149 RefreshFromItemToEnd(index
);
154 void wxListBox::DoInsertItems(const wxArrayString
& items
, int pos
)
156 // the position of the item being added to a sorted listbox can't be
158 wxCHECK_RET( !IsSorted(), _T("can't insert items into sorted listbox") );
160 size_t count
= items
.GetCount();
161 for ( size_t n
= 0; n
< count
; n
++ )
163 m_strings
.Insert(items
[n
], pos
+ n
);
164 m_itemsClientData
.Insert(NULL
, pos
+ n
);
167 // the number of items has changed so we might have to show the scrollbar
168 m_updateScrollbarY
= TRUE
;
170 // the max width also might have changed - just recalculate it instead of
171 // keeping track of it here, this is probably more efficient for a typical
173 RefreshHorzScrollbar();
175 // note that we have to refresh all the items after the ones we inserted,
176 // not just these items
177 RefreshFromItemToEnd(pos
);
180 void wxListBox::DoSetItems(const wxArrayString
& items
, void **clientData
)
184 size_t count
= items
.GetCount();
188 m_strings
.Alloc(count
);
189 m_itemsClientData
.Alloc(count
);
190 for ( size_t n
= 0; n
< count
; n
++ )
192 size_t index
= m_strings
.Add(items
[n
]);
193 m_itemsClientData
.Insert(clientData
? clientData
[n
] : NULL
, index
);
196 m_updateScrollbarY
= TRUE
;
201 void wxListBox::SetString(int n
, const wxString
& s
)
203 if ( HasHorzScrollbar() )
205 // we need to update m_maxWidth as changing the string may cause the
206 // horz scrollbar [dis]appear
209 GetTextExtent(s
, &width
, NULL
);
211 // it might have increased if the new string is long
212 if ( width
> m_maxWidth
)
216 m_updateScrollbarX
= TRUE
;
218 // or also decreased if the old string was the longest one
219 else if ( n
== m_maxWidthItem
)
221 RefreshHorzScrollbar();
224 else // no horz scrollbar
232 // ----------------------------------------------------------------------------
234 // ----------------------------------------------------------------------------
236 void wxListBox::DoClear()
240 if ( HasClientObjectData() )
242 size_t count
= m_itemsClientData
.GetCount();
243 for ( size_t n
= 0; n
< count
; n
++ )
245 delete m_itemsClientData
[n
];
249 m_itemsClientData
.Clear();
250 m_selections
.Clear();
255 void wxListBox::Clear()
259 m_updateScrollbarY
= TRUE
;
261 RefreshHorzScrollbar();
266 void wxListBox::Delete(int n
)
268 wxCHECK_RET( n
< GetCount(), _T("invalid index in wxListBox::Delete") );
270 // do it before removing the index as otherwise the last item will not be
271 // refreshed (as GetCount() will be decremented)
272 RefreshFromItemToEnd(n
);
274 m_strings
.RemoveAt(n
);
276 if ( HasClientObjectData() )
278 delete m_itemsClientData
[n
];
281 m_itemsClientData
.RemoveAt(n
);
283 // when the item disappears we must not keep using its index
284 if ( n
== m_current
)
288 else if ( n
< m_current
)
292 //else: current item may stay
294 // update the selections array: the indices of all seletected items after
295 // the one being deleted must change and the item itselfm ust be removed
296 int index
= wxNOT_FOUND
;
297 size_t count
= m_selections
.GetCount();
298 for ( size_t item
= 0; item
< count
; item
++ )
300 if ( m_selections
[item
] == n
)
302 // remember to delete it later
305 else if ( m_selections
[item
] > n
)
307 // to account for the index shift
308 m_selections
[item
]--;
310 //else: nothing changed for this one
313 if ( index
!= wxNOT_FOUND
)
315 m_selections
.RemoveAt(index
);
318 // the number of items has changed, hence the scrollbar may disappear
319 m_updateScrollbarY
= TRUE
;
321 // finally, if the longest item was deleted the scrollbar may disappear
322 if ( n
== m_maxWidthItem
)
324 RefreshHorzScrollbar();
328 // ----------------------------------------------------------------------------
329 // client data handling
330 // ----------------------------------------------------------------------------
332 void wxListBox::DoSetItemClientData(int n
, void* clientData
)
334 m_itemsClientData
[n
] = clientData
;
337 void *wxListBox::DoGetItemClientData(int n
) const
339 return m_itemsClientData
[n
];
342 void wxListBox::DoSetItemClientObject(int n
, wxClientData
* clientData
)
344 m_itemsClientData
[n
] = clientData
;
347 wxClientData
* wxListBox::DoGetItemClientObject(int n
) const
349 return (wxClientData
*)m_itemsClientData
[n
];
352 // ----------------------------------------------------------------------------
354 // ----------------------------------------------------------------------------
356 void wxListBox::SetSelection(int n
, bool select
)
360 if ( m_selections
.Index(n
) == wxNOT_FOUND
)
362 if ( !HasMultipleSelection() )
364 // selecting an item in a single selection listbox deselects
373 //else: already selected
377 int index
= m_selections
.Index(n
);
378 if ( index
!= wxNOT_FOUND
)
380 m_selections
.RemoveAt(index
);
387 // sanity check: a single selection listbox can't have more than one item
389 wxASSERT_MSG( HasMultipleSelection() || (m_selections
.GetCount() < 2),
390 _T("multiple selected items in single selection lbox?") );
394 // the newly selected item becomes the current one
399 int wxListBox::GetSelection() const
401 wxCHECK_MSG( !HasMultipleSelection(), -1,
402 _T("use wxListBox::GetSelections for ths listbox") );
404 return m_selections
.IsEmpty() ? -1 : m_selections
[0];
407 int wxCMPFUNC_CONV
wxCompareInts(int *n
, int *m
)
412 int wxListBox::GetSelections(wxArrayInt
& selections
) const
414 // always return sorted array to the user
415 selections
= m_selections
;
416 size_t count
= m_selections
.GetCount();
418 // don't call sort on an empty array
421 selections
.Sort(wxCompareInts
);
427 // ----------------------------------------------------------------------------
428 // refresh logic: we use delayed refreshing which allows to avoid multiple
429 // refreshes (and hence flicker) in case when several listbox items are
430 // added/deleted/changed subsequently
431 // ----------------------------------------------------------------------------
433 void wxListBox::RefreshFromItemToEnd(int from
)
435 RefreshItems(from
, GetCount() - from
);
438 void wxListBox::RefreshItems(int from
, int count
)
440 switch ( m_updateCount
)
444 m_updateCount
= count
;
448 // we refresh everything anyhow
452 // add these items to the others which we have to refresh
453 if ( m_updateFrom
< from
)
455 count
+= from
- m_updateFrom
;
456 if ( m_updateCount
< count
)
457 m_updateCount
= count
;
459 else // m_updateFrom >= from
461 int updateLast
= wxMax(m_updateFrom
+ m_updateCount
,
464 m_updateCount
= updateLast
- m_updateFrom
;
469 void wxListBox::RefreshItem(int n
)
471 switch ( m_updateCount
)
474 // refresh this item only
480 // we refresh everything anyhow
484 // add this item to the others which we have to refresh
485 if ( m_updateFrom
< n
)
487 if ( m_updateCount
< n
- m_updateFrom
+ 1 )
488 m_updateCount
= n
- m_updateFrom
+ 1;
490 else // n <= m_updateFrom
492 m_updateCount
+= m_updateFrom
- n
;
498 void wxListBox::RefreshAll()
503 void wxListBox::RefreshHorzScrollbar()
505 m_maxWidth
= 0; // recalculate it
506 m_updateScrollbarX
= TRUE
;
509 void wxListBox::UpdateScrollbars()
511 wxSize size
= GetClientSize();
513 // is our height enough to show all items?
514 int nLines
= GetCount();
515 wxCoord lineHeight
= GetLineHeight();
516 bool showScrollbarY
= nLines
*lineHeight
> size
.y
;
518 // check the width too if required
519 wxCoord charWidth
, maxWidth
;
521 if ( HasHorzScrollbar() )
523 charWidth
= GetCharWidth();
524 maxWidth
= GetMaxWidth();
525 showScrollbarX
= maxWidth
> size
.x
;
527 else // never show it
529 charWidth
= maxWidth
= 0;
530 showScrollbarX
= FALSE
;
533 // what should be the scrollbar range now?
534 int scrollRangeX
= showScrollbarX
535 ? (maxWidth
+ charWidth
- 1) / charWidth
+ 2 // FIXME
537 int scrollRangeY
= showScrollbarY
539 (size
.y
% lineHeight
+ lineHeight
- 1) / lineHeight
542 // reset scrollbars if something changed: either the visibility status
543 // or the range of a scrollbar which is shown
544 if ( (showScrollbarY
!= m_showScrollbarY
) ||
545 (showScrollbarX
!= m_showScrollbarX
) ||
546 (showScrollbarY
&& (scrollRangeY
!= m_scrollRangeY
)) ||
547 (showScrollbarX
&& (scrollRangeX
!= m_scrollRangeX
)) )
550 GetViewStart(&x
, &y
);
551 SetScrollbars(charWidth
, lineHeight
,
552 scrollRangeX
, scrollRangeY
,
555 m_showScrollbarX
= showScrollbarX
;
556 m_showScrollbarY
= showScrollbarY
;
558 m_scrollRangeX
= scrollRangeX
;
559 m_scrollRangeY
= scrollRangeY
;
563 void wxListBox::UpdateItems()
565 // only refresh the items which must be refreshed
566 if ( m_updateCount
== -1 )
569 wxLogTrace(_T("listbox"), _T("Refreshing all"));
575 wxSize size
= GetClientSize();
578 rect
.height
= size
.y
;
579 rect
.y
+= m_updateFrom
*GetLineHeight();
580 rect
.height
= m_updateCount
*GetLineHeight();
582 // we don't need to calculate x position as we always refresh the
584 CalcScrolledPosition(0, rect
.y
, NULL
, &rect
.y
);
586 wxLogTrace(_T("listbox"), _T("Refreshing items %d..%d (%d-%d)"),
587 m_updateFrom
, m_updateFrom
+ m_updateCount
- 1,
588 rect
.GetTop(), rect
.GetBottom());
590 Refresh(TRUE
, &rect
);
594 void wxListBox::OnIdle(wxIdleEvent
& event
)
596 if ( m_updateScrollbarY
|| m_updateScrollbarX
)
601 m_updateScrollbarY
= FALSE
;
604 if ( m_currentChanged
)
606 DoEnsureVisible(m_current
);
608 m_currentChanged
= FALSE
;
621 // ----------------------------------------------------------------------------
623 // ----------------------------------------------------------------------------
625 wxBorder
wxListBox::GetDefaultBorder() const
627 return wxBORDER_SUNKEN
;
630 void wxListBox::DoDraw(wxControlRenderer
*renderer
)
632 // adjust the DC to account for scrolling
633 wxDC
& dc
= renderer
->GetDC();
635 dc
.SetFont(GetFont());
637 // get the update rect
638 wxRect rectUpdate
= GetUpdateClientRect();
641 CalcUnscrolledPosition(0, rectUpdate
.GetTop(), NULL
, &yTop
);
642 CalcUnscrolledPosition(0, rectUpdate
.GetBottom(), NULL
, &yBottom
);
644 // get the items which must be redrawn
645 wxCoord lineHeight
= GetLineHeight();
646 size_t itemFirst
= yTop
/ lineHeight
,
647 itemLast
= (yBottom
+ lineHeight
- 1) / lineHeight
,
648 itemMax
= m_strings
.GetCount();
650 if ( itemFirst
>= itemMax
)
653 if ( itemLast
> itemMax
)
657 wxLogTrace(_T("listbox"), _T("Repainting items %d..%d"),
658 itemFirst
, itemLast
);
660 DoDrawRange(renderer
, itemFirst
, itemLast
);
663 void wxListBox::DoDrawRange(wxControlRenderer
*renderer
,
664 int itemFirst
, int itemLast
)
666 renderer
->DrawItems(this, itemFirst
, itemLast
);
669 // ----------------------------------------------------------------------------
671 // ----------------------------------------------------------------------------
673 bool wxListBox::SetFont(const wxFont
& font
)
675 if ( !wxControl::SetFont(font
) )
685 void wxListBox::CalcItemsPerPage()
687 m_lineHeight
= GetRenderer()->GetListboxItemHeight(GetCharHeight());
688 m_itemsPerPage
= GetClientSize().y
/ m_lineHeight
;
691 int wxListBox::GetItemsPerPage() const
693 if ( !m_itemsPerPage
)
695 wxConstCast(this, wxListBox
)->CalcItemsPerPage();
698 return m_itemsPerPage
;
701 wxCoord
wxListBox::GetLineHeight() const
705 wxConstCast(this, wxListBox
)->CalcItemsPerPage();
711 wxCoord
wxListBox::GetMaxWidth() const
713 if ( m_maxWidth
== 0 )
715 wxListBox
*self
= wxConstCast(this, wxListBox
);
717 size_t count
= m_strings
.GetCount();
718 for ( size_t n
= 0; n
< count
; n
++ )
720 GetTextExtent(m_strings
[n
], &width
, NULL
);
721 if ( width
> m_maxWidth
)
723 self
->m_maxWidth
= width
;
724 self
->m_maxWidthItem
= n
;
732 void wxListBox::OnSize(wxSizeEvent
& event
)
734 // recalculate the number of items per page
737 // the scrollbars might [dis]appear
739 m_updateScrollbarY
= TRUE
;
744 void wxListBox::DoSetFirstItem(int n
)
749 void wxListBox::DoSetSize(int x
, int y
,
750 int width
, int height
,
753 if ( GetWindowStyle() & wxLB_INT_HEIGHT
)
755 // we must round up the height to an entire number of rows
757 // the client area must contain an int number of rows, so take borders
759 wxRect rectBorders
= GetRenderer()->GetBorderDimensions(GetBorder());
760 wxCoord hBorders
= rectBorders
.y
+ rectBorders
.height
;
762 wxCoord hLine
= GetLineHeight();
763 height
= ((height
- hBorders
+ hLine
- 1) / hLine
)*hLine
+ hBorders
;
766 wxListBoxBase::DoSetSize(x
, y
, width
, height
);
769 wxSize
wxListBox::DoGetBestClientSize() const
774 size_t count
= m_strings
.GetCount();
775 for ( size_t n
= 0; n
< count
; n
++ )
778 GetTextExtent(m_strings
[n
], &w
, &h
);
786 // if the listbox is empty, still give it some non zero (even if
787 // arbitrary) size - otherwise, leave small margin around the strings
791 width
+= 3*GetCharWidth();
794 height
= GetCharHeight();
796 // we need the height of the entire listbox, not just of one line
797 height
*= wxMax(count
, 7);
799 return wxSize(width
, height
);
802 // ----------------------------------------------------------------------------
804 // ----------------------------------------------------------------------------
806 bool wxListBox::SendEvent(wxEventType type
, int item
)
808 // don't generate select events while the mouse is captured, we will only
809 // send them once it is released
810 if ( (type
== wxEVT_COMMAND_LISTBOX_SELECTED
) && (GetCapture() == this) )
813 wxCommandEvent
event(type
, m_windowId
);
814 event
.SetEventObject(this);
816 // use the current item by default
822 // client data and string parameters only make sense if we have an item
825 if ( HasClientObjectData() )
826 event
.SetClientObject(GetClientObject(item
));
827 else if ( HasClientUntypedData() )
828 event
.SetClientData(GetClientData(item
));
830 event
.SetString(GetString(item
));
833 event
.m_commandInt
= item
;
835 return GetEventHandler()->ProcessEvent(event
);
838 void wxListBox::SetCurrentItem(int n
)
840 if ( n
!= m_current
)
842 if ( m_current
!= -1 )
843 RefreshItem(m_current
);
847 if ( m_current
!= -1 )
849 m_currentChanged
= TRUE
;
851 RefreshItem(m_current
);
854 //else: nothing to do
857 bool wxListBox::FindItem(const wxString
& prefix
, bool strictlyAfter
)
859 int count
= GetCount();
862 // empty listbox, we can't find anything in it
866 // start either from the current item or from the next one if strictlyAfter
871 // the following line will set first correctly to 0 if there is no
872 // selection (m_current == -1)
873 first
= m_current
== count
- 1 ? 0 : m_current
+ 1;
875 else // start with the current
877 first
= m_current
== -1 ? 0 : m_current
;
880 int last
= first
== 0 ? count
- 1 : first
- 1;
882 // if this is not true we'd never exit from the loop below!
883 wxASSERT_MSG( first
< count
&& last
< count
, _T("logic error") );
885 // precompute it outside the loop
886 size_t len
= prefix
.length();
888 // loop over all items in the listbox
889 for ( int item
= first
; item
!= last
; item
< count
- 1 ? item
++ : item
= 0 )
891 if ( wxStrnicmp(m_strings
[item
], prefix
, len
) == 0 )
893 SetCurrentItem(item
);
895 if ( !(GetWindowStyle() & wxLB_MULTIPLE
) )
898 SelectAndNotify(item
);
900 if ( GetWindowStyle() & wxLB_EXTENDED
)
901 AnchorSelection(item
);
912 void wxListBox::EnsureVisible(int n
)
914 if ( m_updateScrollbarY
)
919 m_updateScrollbarY
= FALSE
;
925 void wxListBox::DoEnsureVisible(int n
)
927 if ( !m_showScrollbarY
)
929 // nothing to do - everything is shown anyhow
934 GetViewStart(0, &first
);
937 // we need to scroll upwards, so make the current item appear on top
938 // of the shown range
943 int last
= first
+ GetClientSize().y
/ GetLineHeight() - 1;
946 // scroll down: the current item appears at the bottom of the
948 Scroll(0, n
- (last
- first
));
953 void wxListBox::ChangeCurrent(int diff
)
955 int current
= m_current
== -1 ? 0 : m_current
;
959 int last
= GetCount() - 1;
962 else if ( current
> last
)
965 SetCurrentItem(current
);
968 void wxListBox::ExtendSelection(int itemTo
)
970 // if we don't have the explicit values for selection start/end, make them
972 if ( m_selAnchor
== -1 )
973 m_selAnchor
= m_current
;
978 // swap the start/end of selection range if necessary
979 int itemFrom
= m_selAnchor
;
980 if ( itemFrom
> itemTo
)
982 int itemTmp
= itemFrom
;
987 // the selection should now include all items in the range between the
988 // anchor and the specified item and only them
991 for ( n
= 0; n
< itemFrom
; n
++ )
996 for ( ; n
<= itemTo
; n
++ )
1001 int count
= GetCount();
1002 for ( ; n
< count
; n
++ )
1008 void wxListBox::Select(bool sel
, int item
)
1012 // go to this item first
1013 SetCurrentItem(item
);
1016 // the current item is the one we want to change: either it was just
1017 // changed above to be the same as item or item == -1 in which we case we
1018 // are supposed to use the current one anyhow
1019 if ( m_current
!= -1 )
1022 SetSelection(m_current
, sel
);
1026 void wxListBox::SelectAndNotify(int item
)
1030 SendEvent(wxEVT_COMMAND_LISTBOX_SELECTED
);
1033 void wxListBox::Activate(int item
)
1036 SetCurrentItem(item
);
1040 if ( !(GetWindowStyle() & wxLB_MULTIPLE
) )
1049 SendEvent(wxEVT_COMMAND_LISTBOX_DOUBLECLICKED
);
1053 // ----------------------------------------------------------------------------
1055 // ----------------------------------------------------------------------------
1057 bool wxListBox::PerformAction(const wxControlAction
& action
,
1059 const wxString
& strArg
)
1061 int item
= (int)numArg
;
1063 if ( action
== wxACTION_LISTBOX_SETFOCUS
)
1064 SetCurrentItem(item
);
1065 else if ( action
== wxACTION_LISTBOX_ACTIVATE
)
1067 else if ( action
== wxACTION_LISTBOX_TOGGLE
)
1072 if ( IsSelected(item
) )
1075 SelectAndNotify(item
);
1077 else if ( action
== wxACTION_LISTBOX_SELECT
)
1080 SelectAndNotify(item
);
1082 else if ( action
== wxACTION_LISTBOX_SELECTADD
)
1084 else if ( action
== wxACTION_LISTBOX_UNSELECT
)
1085 Select(FALSE
, item
);
1086 else if ( action
== wxACTION_LISTBOX_MOVEDOWN
)
1088 else if ( action
== wxACTION_LISTBOX_MOVEUP
)
1090 else if ( action
== wxACTION_LISTBOX_PAGEDOWN
)
1091 ChangeCurrent(GetItemsPerPage());
1092 else if ( action
== wxACTION_LISTBOX_PAGEUP
)
1093 ChangeCurrent(-GetItemsPerPage());
1094 else if ( action
== wxACTION_LISTBOX_START
)
1096 else if ( action
== wxACTION_LISTBOX_END
)
1097 SetCurrentItem(GetCount() - 1);
1098 else if ( action
== wxACTION_LISTBOX_UNSELECTALL
)
1100 else if ( action
== wxACTION_LISTBOX_EXTENDSEL
)
1101 ExtendSelection(item
);
1102 else if ( action
== wxACTION_LISTBOX_FIND
)
1103 FindNextItem(strArg
);
1104 else if ( action
== wxACTION_LISTBOX_ANCHOR
)
1105 AnchorSelection(item
== -1 ? m_current
: item
);
1106 else if ( action
== wxACTION_LISTBOX_SELECTALL
||
1107 action
== wxACTION_LISTBOX_SELTOGGLE
)
1108 wxFAIL_MSG(_T("unimplemented yet"));
1110 return wxControl::PerformAction(action
, numArg
, strArg
);
1115 // ============================================================================
1116 // implementation of wxStdListboxInputHandler
1117 // ============================================================================
1119 wxStdListboxInputHandler::wxStdListboxInputHandler(wxInputHandler
*handler
,
1120 bool toggleOnPressAlways
)
1121 : wxStdInputHandler(handler
)
1124 m_toggleOnPressAlways
= toggleOnPressAlways
;
1125 m_actionMouse
= wxACTION_NONE
;
1126 m_trackMouseOutside
= TRUE
;
1129 int wxStdListboxInputHandler::HitTest(const wxListBox
*lbox
,
1130 const wxMouseEvent
& event
)
1132 int item
= HitTestUnsafe(lbox
, event
);
1134 return FixItemIndex(lbox
, item
);
1137 int wxStdListboxInputHandler::HitTestUnsafe(const wxListBox
*lbox
,
1138 const wxMouseEvent
& event
)
1140 wxPoint pt
= event
.GetPosition();
1141 pt
-= lbox
->GetClientAreaOrigin();
1143 lbox
->CalcUnscrolledPosition(0, pt
.y
, NULL
, &y
);
1144 return y
/ lbox
->GetLineHeight();
1147 int wxStdListboxInputHandler::FixItemIndex(const wxListBox
*lbox
,
1152 // mouse is above the first item
1155 else if ( item
>= lbox
->GetCount() )
1157 // mouse is below the last item
1158 item
= lbox
->GetCount() - 1;
1164 bool wxStdListboxInputHandler::IsValidIndex(const wxListBox
*lbox
, int item
)
1166 return item
>= 0 && item
< lbox
->GetCount();
1170 wxStdListboxInputHandler::SetupCapture(wxListBox
*lbox
,
1171 const wxMouseEvent
& event
,
1174 // we currently only allow selecting with the left mouse button, if we
1175 // do need to allow using other buttons too we might use the code
1178 m_btnCapture
= event
.LeftDown()
1187 wxControlAction action
;
1188 if ( lbox
->HasMultipleSelection() )
1190 if ( lbox
->GetWindowStyle() & wxLB_MULTIPLE
)
1192 if ( m_toggleOnPressAlways
)
1194 // toggle the item right now
1195 action
= wxACTION_LISTBOX_TOGGLE
;
1199 m_actionMouse
= wxACTION_LISTBOX_SETFOCUS
;
1201 else // wxLB_EXTENDED listbox
1203 // simple click in an extended sel listbox clears the old
1204 // selection and adds the clicked item to it then, ctrl-click
1205 // toggles an item to it and shift-click adds a range between
1206 // the old selection anchor and the clicked item
1207 if ( event
.ControlDown() )
1209 lbox
->PerformAction(wxACTION_LISTBOX_ANCHOR
, item
);
1211 action
= wxACTION_LISTBOX_TOGGLE
;
1213 else if ( event
.ShiftDown() )
1215 action
= wxACTION_LISTBOX_EXTENDSEL
;
1217 else // simple click
1219 lbox
->PerformAction(wxACTION_LISTBOX_ANCHOR
, item
);
1221 action
= wxACTION_LISTBOX_SELECT
;
1224 m_actionMouse
= wxACTION_LISTBOX_EXTENDSEL
;
1227 else // single selection
1230 action
= wxACTION_LISTBOX_SELECT
;
1233 // by default we always do track it
1234 m_trackMouseOutside
= TRUE
;
1239 bool wxStdListboxInputHandler::HandleKey(wxControl
*control
,
1240 const wxKeyEvent
& event
,
1243 // we're only interested in the key press events
1244 if ( pressed
&& !event
.AltDown() )
1246 bool isMoveCmd
= TRUE
;
1247 int style
= control
->GetWindowStyle();
1249 wxControlAction action
;
1252 int keycode
= event
.GetKeyCode();
1256 case WXK_UP
: action
= wxACTION_LISTBOX_MOVEUP
; break;
1257 case WXK_DOWN
: action
= wxACTION_LISTBOX_MOVEDOWN
; break;
1258 case WXK_PRIOR
: action
= wxACTION_LISTBOX_PAGEUP
; break;
1259 case WXK_NEXT
: action
= wxACTION_LISTBOX_PAGEDOWN
; break;
1260 case WXK_HOME
: action
= wxACTION_LISTBOX_START
; break;
1261 case WXK_END
: action
= wxACTION_LISTBOX_END
; break;
1265 if ( style
& wxLB_MULTIPLE
)
1267 action
= wxACTION_LISTBOX_TOGGLE
;
1273 action
= wxACTION_LISTBOX_ACTIVATE
;
1278 if ( (keycode
< 255) && wxIsalnum(keycode
) )
1280 action
= wxACTION_LISTBOX_FIND
;
1281 strArg
= (wxChar
)keycode
;
1287 control
->PerformAction(action
, -1, strArg
);
1291 if ( style
& wxLB_SINGLE
)
1293 // the current item is always the one selected
1294 control
->PerformAction(wxACTION_LISTBOX_SELECT
);
1296 else if ( style
& wxLB_EXTENDED
)
1298 if ( event
.ShiftDown() )
1299 control
->PerformAction(wxACTION_LISTBOX_EXTENDSEL
);
1302 // select the item and make it the new selection anchor
1303 control
->PerformAction(wxACTION_LISTBOX_SELECT
);
1304 control
->PerformAction(wxACTION_LISTBOX_ANCHOR
);
1307 //else: nothing to do for multiple selection listboxes
1314 return wxStdInputHandler::HandleKey(control
, event
, pressed
);
1317 bool wxStdListboxInputHandler::HandleMouse(wxControl
*control
,
1318 const wxMouseEvent
& event
)
1320 wxListBox
*lbox
= wxStaticCast(control
, wxListBox
);
1321 int item
= HitTest(lbox
, event
);
1322 wxControlAction action
;
1324 // when the left mouse button is pressed, capture the mouse and track the
1325 // item under mouse (if the mouse leaves the window, we will still be
1326 // getting the mouse move messages generated by wxScrollWindow)
1327 if ( event
.LeftDown() )
1329 // capture the mouse to track the selected item
1330 lbox
->CaptureMouse();
1332 action
= SetupCapture(lbox
, event
, item
);
1334 else if ( m_btnCapture
&& event
.ButtonUp(m_btnCapture
) )
1336 // when the left mouse button is released, release the mouse too
1337 wxWindow
*winCapture
= wxWindow::GetCapture();
1340 winCapture
->ReleaseMouse();
1343 // generate the last event to triiger sending the selection event
1344 action
= m_actionMouse
;
1346 //else: the mouse wasn't presed over the listbox, only released here
1348 else if ( event
.LeftDClick() )
1350 action
= wxACTION_LISTBOX_ACTIVATE
;
1355 lbox
->PerformAction(action
, item
);
1360 return wxStdInputHandler::HandleMouse(control
, event
);
1363 bool wxStdListboxInputHandler::HandleMouseMove(wxControl
*control
,
1364 const wxMouseEvent
& event
)
1366 wxWindow
*winCapture
= wxWindow::GetCapture();
1367 if ( winCapture
&& (event
.GetEventObject() == winCapture
) )
1369 wxListBox
*lbox
= wxStaticCast(control
, wxListBox
);
1371 if ( !m_btnCapture
|| !m_trackMouseOutside
)
1373 // someone captured the mouse for us (we always set m_btnCapture
1374 // when we do it ourselves): in this case we only react to
1375 // the mouse messages when they happen inside the listbox
1376 if ( lbox
->HitTest(event
.GetPosition()) != wxHT_WINDOW_INSIDE
)
1380 int item
= HitTest(lbox
, event
);
1381 if ( !m_btnCapture
)
1383 // now that we have the mouse inside the listbox, do capture it
1384 // normally - but ensure that we will still ignore the outside
1386 SetupCapture(lbox
, event
, item
);
1388 m_trackMouseOutside
= FALSE
;
1391 if ( IsValidIndex(lbox
, item
) )
1393 lbox
->PerformAction(m_actionMouse
, item
);
1395 // else: don't pass invalid index to the listbox
1397 else // we don't have capture any more
1401 // if we lost capture unexpectedly (someone else took the capture
1402 // from us), return to a consistent state
1407 return wxStdInputHandler::HandleMouseMove(control
, event
);
1410 #endif // wxUSE_LISTBOX