1
/////////////////////////////////////////////////////////////////////////////
2
// Name: univ/listbox.cpp
3
// Purpose: wxListBox implementation
4
// Author: Vadim Zeitlin
7
// RCS-ID: $Id: listbox.cpp,v 1.31 2005/02/13 17:08:27 VZ Exp $
8
// Copyright: (c) 2000 SciTech Software, Inc. (www.scitechsoft.com)
9
// Licence: wxWindows licence
10
/////////////////////////////////////////////////////////////////////////////
12
// ============================================================================
14
// ============================================================================
16
// ----------------------------------------------------------------------------
18
// ----------------------------------------------------------------------------
20
#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
21
#pragma implementation "univlistbox.h"
24
#include "wx/wxprec.h"
35
#include "wx/dcclient.h"
36
#include "wx/listbox.h"
37
#include "wx/validate.h"
40
#include "wx/univ/renderer.h"
41
#include "wx/univ/inphand.h"
42
#include "wx/univ/theme.h"
44
// ============================================================================
45
// implementation of wxListBox
46
// ============================================================================
48
IMPLEMENT_DYNAMIC_CLASS(wxListBox, wxControl)
50
BEGIN_EVENT_TABLE(wxListBox, wxListBoxBase)
51
EVT_SIZE(wxListBox::OnSize)
54
// ----------------------------------------------------------------------------
56
// ----------------------------------------------------------------------------
58
void wxListBox::Init()
60
// will be calculated later when needed
68
// no items hence no current item
71
m_currentChanged = false;
73
// no need to update anything initially
76
// no scrollbars to show nor update
80
m_showScrollbarY = false;
83
wxListBox::wxListBox(wxWindow *parent,
87
const wxArrayString& choices,
89
const wxValidator& validator,
94
Create(parent, id, pos, size, choices, style, validator, name);
97
bool wxListBox::Create(wxWindow *parent,
101
const wxArrayString& choices,
103
const wxValidator& validator,
104
const wxString &name)
106
wxCArrayString chs(choices);
108
return Create(parent, id, pos, size, chs.GetCount(), chs.GetStrings(),
109
style, validator, name);
112
bool wxListBox::Create(wxWindow *parent,
117
const wxString choices[],
119
const wxValidator& validator,
120
const wxString &name)
122
// for compatibility accept both the new and old styles - they mean the
124
if ( style & wxLB_ALWAYS_SB )
125
style |= wxALWAYS_SHOW_SB;
127
// if we don't have neither multiple nor extended flag, we must have the
128
// single selection listbox
129
if ( !(style & (wxLB_MULTIPLE | wxLB_EXTENDED)) )
130
style |= wxLB_SINGLE;
132
#if wxUSE_TWO_WINDOWS
133
style |= wxVSCROLL|wxHSCROLL;
134
if ((style & wxBORDER_MASK) == 0)
135
style |= wxBORDER_SUNKEN;
138
if ( !wxControl::Create(parent, id, pos, size, style,
144
m_strings = new wxArrayString;
150
CreateInputHandler(wxINP_HANDLER_LISTBOX);
155
wxListBox::~wxListBox()
157
// call this just to free the client data -- and avoid leaking memory
165
// ----------------------------------------------------------------------------
166
// adding/inserting strings
167
// ----------------------------------------------------------------------------
169
int wxCMPFUNC_CONV wxListBoxSortNoCase(wxString* s1, wxString* s2)
171
return s1->CmpNoCase(*s2);
174
int wxListBox::DoAppendOnly(const wxString& item)
180
m_strings->Add(item);
181
m_strings->Sort(wxListBoxSortNoCase);
182
index = m_strings->Index(item);
186
index = m_strings->GetCount();
187
m_strings->Add(item);
193
int wxListBox::DoAppend(const wxString& item)
195
size_t index = DoAppendOnly( item );
197
m_itemsClientData.Insert(NULL, index);
199
m_updateScrollbarY = true;
201
if ( HasHorzScrollbar() )
203
// has the max width increased?
205
GetTextExtent(item, &width, NULL);
206
if ( width > m_maxWidth )
209
m_maxWidthItem = index;
210
m_updateScrollbarX = true;
214
RefreshFromItemToEnd(index);
219
void wxListBox::DoInsertItems(const wxArrayString& items, int pos)
221
// the position of the item being added to a sorted listbox can't be
223
wxCHECK_RET( !IsSorted(), _T("can't insert items into sorted listbox") );
225
size_t count = items.GetCount();
226
for ( size_t n = 0; n < count; n++ )
228
m_strings->Insert(items[n], pos + n);
229
m_itemsClientData.Insert(NULL, pos + n);
232
// the number of items has changed so we might have to show the scrollbar
233
m_updateScrollbarY = true;
235
// the max width also might have changed - just recalculate it instead of
236
// keeping track of it here, this is probably more efficient for a typical
238
RefreshHorzScrollbar();
240
// note that we have to refresh all the items after the ones we inserted,
241
// not just these items
242
RefreshFromItemToEnd(pos);
245
void wxListBox::DoSetItems(const wxArrayString& items, void **clientData)
249
size_t count = items.GetCount();
253
m_strings->Alloc(count);
255
m_itemsClientData.Alloc(count);
256
for ( size_t n = 0; n < count; n++ )
258
size_t index = DoAppendOnly(items[n]);
260
m_itemsClientData.Insert(clientData ? clientData[n] : NULL, index);
263
m_updateScrollbarY = true;
268
void wxListBox::SetString(int n, const wxString& s)
270
wxCHECK_RET( !IsSorted(), _T("can't set string in sorted listbox") );
274
if ( HasHorzScrollbar() )
276
// we need to update m_maxWidth as changing the string may cause the
277
// horz scrollbar [dis]appear
280
GetTextExtent(s, &width, NULL);
282
// it might have increased if the new string is long
283
if ( width > m_maxWidth )
287
m_updateScrollbarX = true;
289
// or also decreased if the old string was the longest one
290
else if ( n == m_maxWidthItem )
292
RefreshHorzScrollbar();
299
// ----------------------------------------------------------------------------
301
// ----------------------------------------------------------------------------
303
void wxListBox::DoClear()
307
if ( HasClientObjectData() )
309
size_t count = m_itemsClientData.GetCount();
310
for ( size_t n = 0; n < count; n++ )
312
delete (wxClientData *) m_itemsClientData[n];
316
m_itemsClientData.Clear();
317
m_selections.Clear();
322
void wxListBox::Clear()
326
m_updateScrollbarY = true;
328
RefreshHorzScrollbar();
333
void wxListBox::Delete(int n)
335
wxCHECK_RET( n >= 0 && n < GetCount(),
336
_T("invalid index in wxListBox::Delete") );
338
// do it before removing the index as otherwise the last item will not be
339
// refreshed (as GetCount() will be decremented)
340
RefreshFromItemToEnd(n);
342
m_strings->RemoveAt(n);
344
if ( HasClientObjectData() )
346
delete (wxClientData *)m_itemsClientData[n];
349
m_itemsClientData.RemoveAt(n);
351
// when the item disappears we must not keep using its index
352
if ( n == m_current )
356
else if ( n < m_current )
360
//else: current item may stay
362
// update the selections array: the indices of all seletected items after
363
// the one being deleted must change and the item itselfm ust be removed
364
int index = wxNOT_FOUND;
365
size_t count = m_selections.GetCount();
366
for ( size_t item = 0; item < count; item++ )
368
if ( m_selections[item] == n )
370
// remember to delete it later
373
else if ( m_selections[item] > n )
375
// to account for the index shift
376
m_selections[item]--;
378
//else: nothing changed for this one
381
if ( index != wxNOT_FOUND )
383
m_selections.RemoveAt(index);
386
// the number of items has changed, hence the scrollbar may disappear
387
m_updateScrollbarY = true;
389
// finally, if the longest item was deleted the scrollbar may disappear
390
if ( n == m_maxWidthItem )
392
RefreshHorzScrollbar();
396
// ----------------------------------------------------------------------------
397
// client data handling
398
// ----------------------------------------------------------------------------
400
void wxListBox::DoSetItemClientData(int n, void* clientData)
402
m_itemsClientData[n] = clientData;
405
void *wxListBox::DoGetItemClientData(int n) const
407
return m_itemsClientData[n];
410
void wxListBox::DoSetItemClientObject(int n, wxClientData* clientData)
412
m_itemsClientData[n] = clientData;
415
wxClientData* wxListBox::DoGetItemClientObject(int n) const
417
return (wxClientData *)m_itemsClientData[n];
420
// ----------------------------------------------------------------------------
422
// ----------------------------------------------------------------------------
424
void wxListBox::DoSetSelection(int n, bool select)
428
if ( m_selections.Index(n) == wxNOT_FOUND )
430
if ( !HasMultipleSelection() )
432
// selecting an item in a single selection listbox deselects
441
//else: already selected
445
int index = m_selections.Index(n);
446
if ( index != wxNOT_FOUND )
448
m_selections.RemoveAt(index);
455
// sanity check: a single selection listbox can't have more than one item
457
wxASSERT_MSG( HasMultipleSelection() || (m_selections.GetCount() < 2),
458
_T("multiple selected items in single selection lbox?") );
462
// the newly selected item becomes the current one
467
int wxListBox::GetSelection() const
469
wxCHECK_MSG( !HasMultipleSelection(), -1,
470
_T("use wxListBox::GetSelections for ths listbox") );
472
return m_selections.IsEmpty() ? -1 : m_selections[0];
475
int wxCMPFUNC_CONV wxCompareInts(int *n, int *m)
480
int wxListBox::GetSelections(wxArrayInt& selections) const
482
// always return sorted array to the user
483
selections = m_selections;
484
size_t count = m_selections.GetCount();
486
// don't call sort on an empty array
489
selections.Sort(wxCompareInts);
495
// ----------------------------------------------------------------------------
496
// refresh logic: we use delayed refreshing which allows to avoid multiple
497
// refreshes (and hence flicker) in case when several listbox items are
498
// added/deleted/changed subsequently
499
// ----------------------------------------------------------------------------
501
void wxListBox::RefreshFromItemToEnd(int from)
503
RefreshItems(from, GetCount() - from);
506
void wxListBox::RefreshItems(int from, int count)
508
switch ( m_updateCount )
512
m_updateCount = count;
516
// we refresh everything anyhow
520
// add these items to the others which we have to refresh
521
if ( m_updateFrom < from )
523
count += from - m_updateFrom;
524
if ( m_updateCount < count )
525
m_updateCount = count;
527
else // m_updateFrom >= from
529
int updateLast = wxMax(m_updateFrom + m_updateCount,
532
m_updateCount = updateLast - m_updateFrom;
537
void wxListBox::RefreshItem(int n)
539
switch ( m_updateCount )
542
// refresh this item only
548
// we refresh everything anyhow
552
// add this item to the others which we have to refresh
553
if ( m_updateFrom < n )
555
if ( m_updateCount < n - m_updateFrom + 1 )
556
m_updateCount = n - m_updateFrom + 1;
558
else // n <= m_updateFrom
560
m_updateCount += m_updateFrom - n;
566
void wxListBox::RefreshAll()
571
void wxListBox::RefreshHorzScrollbar()
573
m_maxWidth = 0; // recalculate it
574
m_updateScrollbarX = true;
577
void wxListBox::UpdateScrollbars()
579
wxSize size = GetClientSize();
581
// is our height enough to show all items?
582
int nLines = GetCount();
583
wxCoord lineHeight = GetLineHeight();
584
bool showScrollbarY = nLines*lineHeight > size.y;
586
// check the width too if required
587
wxCoord charWidth, maxWidth;
589
if ( HasHorzScrollbar() )
591
charWidth = GetCharWidth();
592
maxWidth = GetMaxWidth();
593
showScrollbarX = maxWidth > size.x;
595
else // never show it
597
charWidth = maxWidth = 0;
598
showScrollbarX = false;
601
// what should be the scrollbar range now?
602
int scrollRangeX = showScrollbarX
603
? (maxWidth + charWidth - 1) / charWidth + 2 // FIXME
605
int scrollRangeY = showScrollbarY
607
(size.y % lineHeight + lineHeight - 1) / lineHeight
610
// reset scrollbars if something changed: either the visibility status
611
// or the range of a scrollbar which is shown
612
if ( (showScrollbarY != m_showScrollbarY) ||
613
(showScrollbarX != m_showScrollbarX) ||
614
(showScrollbarY && (scrollRangeY != m_scrollRangeY)) ||
615
(showScrollbarX && (scrollRangeX != m_scrollRangeX)) )
618
GetViewStart(&x, &y);
619
SetScrollbars(charWidth, lineHeight,
620
scrollRangeX, scrollRangeY,
623
m_showScrollbarX = showScrollbarX;
624
m_showScrollbarY = showScrollbarY;
626
m_scrollRangeX = scrollRangeX;
627
m_scrollRangeY = scrollRangeY;
631
void wxListBox::UpdateItems()
633
// only refresh the items which must be refreshed
634
if ( m_updateCount == -1 )
637
wxLogTrace(_T("listbox"), _T("Refreshing all"));
643
wxSize size = GetClientSize();
646
rect.height = size.y;
647
rect.y += m_updateFrom*GetLineHeight();
648
rect.height = m_updateCount*GetLineHeight();
650
// we don't need to calculate x position as we always refresh the
652
CalcScrolledPosition(0, rect.y, NULL, &rect.y);
654
wxLogTrace(_T("listbox"), _T("Refreshing items %d..%d (%d-%d)"),
655
m_updateFrom, m_updateFrom + m_updateCount - 1,
656
rect.GetTop(), rect.GetBottom());
658
Refresh(true, &rect);
662
void wxListBox::OnInternalIdle()
664
if ( m_updateScrollbarY || m_updateScrollbarX )
669
m_updateScrollbarY = false;
672
if ( m_currentChanged )
674
DoEnsureVisible(m_current);
676
m_currentChanged = false;
685
wxListBoxBase::OnInternalIdle();
688
// ----------------------------------------------------------------------------
690
// ----------------------------------------------------------------------------
692
wxBorder wxListBox::GetDefaultBorder() const
694
return wxBORDER_SUNKEN;
697
void wxListBox::DoDraw(wxControlRenderer *renderer)
699
// adjust the DC to account for scrolling
700
wxDC& dc = renderer->GetDC();
702
dc.SetFont(GetFont());
704
// get the update rect
705
wxRect rectUpdate = GetUpdateClientRect();
708
CalcUnscrolledPosition(0, rectUpdate.GetTop(), NULL, &yTop);
709
CalcUnscrolledPosition(0, rectUpdate.GetBottom(), NULL, &yBottom);
711
// get the items which must be redrawn
712
wxCoord lineHeight = GetLineHeight();
713
size_t itemFirst = yTop / lineHeight,
714
itemLast = (yBottom + lineHeight - 1) / lineHeight,
715
itemMax = m_strings->GetCount();
717
if ( itemFirst >= itemMax )
720
if ( itemLast > itemMax )
724
wxLogTrace(_T("listbox"), _T("Repainting items %d..%d"),
725
itemFirst, itemLast);
727
DoDrawRange(renderer, itemFirst, itemLast);
730
void wxListBox::DoDrawRange(wxControlRenderer *renderer,
731
int itemFirst, int itemLast)
733
renderer->DrawItems(this, itemFirst, itemLast);
736
// ----------------------------------------------------------------------------
738
// ----------------------------------------------------------------------------
740
bool wxListBox::SetFont(const wxFont& font)
742
if ( !wxControl::SetFont(font) )
752
void wxListBox::CalcItemsPerPage()
754
m_lineHeight = GetRenderer()->GetListboxItemHeight(GetCharHeight());
755
m_itemsPerPage = GetClientSize().y / m_lineHeight;
758
int wxListBox::GetItemsPerPage() const
760
if ( !m_itemsPerPage )
762
wxConstCast(this, wxListBox)->CalcItemsPerPage();
765
return m_itemsPerPage;
768
wxCoord wxListBox::GetLineHeight() const
772
wxConstCast(this, wxListBox)->CalcItemsPerPage();
778
wxCoord wxListBox::GetMaxWidth() const
780
if ( m_maxWidth == 0 )
782
wxListBox *self = wxConstCast(this, wxListBox);
784
size_t count = m_strings->GetCount();
785
for ( size_t n = 0; n < count; n++ )
787
GetTextExtent(this->GetString(n), &width, NULL);
788
if ( width > m_maxWidth )
790
self->m_maxWidth = width;
791
self->m_maxWidthItem = n;
799
void wxListBox::OnSize(wxSizeEvent& event)
801
// recalculate the number of items per page
804
// the scrollbars might [dis]appear
806
m_updateScrollbarY = true;
811
void wxListBox::DoSetFirstItem(int n)
816
void wxListBox::DoSetSize(int x, int y,
817
int width, int height,
820
if ( GetWindowStyle() & wxLB_INT_HEIGHT )
822
// we must round up the height to an entire number of rows
824
// the client area must contain an int number of rows, so take borders
826
wxRect rectBorders = GetRenderer()->GetBorderDimensions(GetBorder());
827
wxCoord hBorders = rectBorders.y + rectBorders.height;
829
wxCoord hLine = GetLineHeight();
830
height = ((height - hBorders + hLine - 1) / hLine)*hLine + hBorders;
833
wxListBoxBase::DoSetSize(x, y, width, height, sizeFlags);
836
wxSize wxListBox::DoGetBestClientSize() const
841
size_t count = m_strings->GetCount();
842
for ( size_t n = 0; n < count; n++ )
845
GetTextExtent(this->GetString(n), &w, &h);
853
// if the listbox is empty, still give it some non zero (even if
854
// arbitrary) size - otherwise, leave small margin around the strings
858
width += 3*GetCharWidth();
861
height = GetCharHeight();
863
// we need the height of the entire listbox, not just of one line
864
height *= wxMax(count, 7);
866
return wxSize(width, height);
869
// ----------------------------------------------------------------------------
871
// ----------------------------------------------------------------------------
873
bool wxListBox::SendEvent(wxEventType type, int item)
875
wxCommandEvent event(type, m_windowId);
876
event.SetEventObject(this);
878
// use the current item by default
884
// client data and string parameters only make sense if we have an item
887
if ( HasClientObjectData() )
888
event.SetClientObject(GetClientObject(item));
889
else if ( HasClientUntypedData() )
890
event.SetClientData(GetClientData(item));
892
event.SetString(GetString(item));
897
return GetEventHandler()->ProcessEvent(event);
900
void wxListBox::SetCurrentItem(int n)
902
if ( n != m_current )
904
if ( m_current != -1 )
905
RefreshItem(m_current);
909
if ( m_current != -1 )
911
m_currentChanged = true;
913
RefreshItem(m_current);
916
//else: nothing to do
919
bool wxListBox::FindItem(const wxString& prefix, bool strictlyAfter)
921
int count = GetCount();
924
// empty listbox, we can't find anything in it
928
// start either from the current item or from the next one if strictlyAfter
933
// the following line will set first correctly to 0 if there is no
934
// selection (m_current == -1)
935
first = m_current == count - 1 ? 0 : m_current + 1;
937
else // start with the current
939
first = m_current == -1 ? 0 : m_current;
942
int last = first == 0 ? count - 1 : first - 1;
944
// if this is not true we'd never exit from the loop below!
945
wxASSERT_MSG( first < count && last < count, _T("logic error") );
947
// precompute it outside the loop
948
size_t len = prefix.length();
950
// loop over all items in the listbox
951
for ( int item = first; item != last; item < count - 1 ? item++ : item = 0 )
953
if ( wxStrnicmp(this->GetString(item).c_str(), prefix, len) == 0 )
955
SetCurrentItem(item);
957
if ( !(GetWindowStyle() & wxLB_MULTIPLE) )
960
SelectAndNotify(item);
962
if ( GetWindowStyle() & wxLB_EXTENDED )
963
AnchorSelection(item);
974
void wxListBox::EnsureVisible(int n)
976
if ( m_updateScrollbarY )
981
m_updateScrollbarY = false;
987
void wxListBox::DoEnsureVisible(int n)
989
if ( !m_showScrollbarY )
991
// nothing to do - everything is shown anyhow
996
GetViewStart(0, &first);
999
// we need to scroll upwards, so make the current item appear on top
1000
// of the shown range
1005
int last = first + GetClientSize().y / GetLineHeight() - 1;
1008
// scroll down: the current item appears at the bottom of the
1010
Scroll(0, n - (last - first));
1015
void wxListBox::ChangeCurrent(int diff)
1017
int current = m_current == -1 ? 0 : m_current;
1021
int last = GetCount() - 1;
1024
else if ( current > last )
1027
SetCurrentItem(current);
1030
void wxListBox::ExtendSelection(int itemTo)
1032
// if we don't have the explicit values for selection start/end, make them
1034
if ( m_selAnchor == -1 )
1035
m_selAnchor = m_current;
1040
// swap the start/end of selection range if necessary
1041
int itemFrom = m_selAnchor;
1042
if ( itemFrom > itemTo )
1044
int itemTmp = itemFrom;
1049
// the selection should now include all items in the range between the
1050
// anchor and the specified item and only them
1053
for ( n = 0; n < itemFrom; n++ )
1058
for ( ; n <= itemTo; n++ )
1063
int count = GetCount();
1064
for ( ; n < count; n++ )
1070
void wxListBox::DoSelect(int item, bool sel)
1074
// go to this item first
1075
SetCurrentItem(item);
1078
// the current item is the one we want to change: either it was just
1079
// changed above to be the same as item or item == -1 in which we case we
1080
// are supposed to use the current one anyhow
1081
if ( m_current != -1 )
1084
SetSelection(m_current, sel);
1088
void wxListBox::SelectAndNotify(int item)
1092
SendEvent(wxEVT_COMMAND_LISTBOX_SELECTED);
1095
void wxListBox::Activate(int item)
1098
SetCurrentItem(item);
1102
if ( !(GetWindowStyle() & wxLB_MULTIPLE) )
1111
SendEvent(wxEVT_COMMAND_LISTBOX_DOUBLECLICKED);
1115
// ----------------------------------------------------------------------------
1117
// ----------------------------------------------------------------------------
1120
The numArg here is the listbox item index while the strArg is used
1121
differently for the different actions:
1123
a) for wxACTION_LISTBOX_FIND it has the natural meaning: this is the string
1126
b) for wxACTION_LISTBOX_SELECT and wxACTION_LISTBOX_EXTENDSEL it is used
1127
to decide if the listbox should send the notification event (it is empty)
1128
or not (it is not): this allows us to reuse the same action for when the
1129
user is dragging the mouse when it has been released although in the
1130
first case no notification is sent while in the second it is sent.
1132
bool wxListBox::PerformAction(const wxControlAction& action,
1134
const wxString& strArg)
1136
int item = (int)numArg;
1138
if ( action == wxACTION_LISTBOX_SETFOCUS )
1140
SetCurrentItem(item);
1142
else if ( action == wxACTION_LISTBOX_ACTIVATE )
1146
else if ( action == wxACTION_LISTBOX_TOGGLE )
1151
if ( IsSelected(item) )
1154
SelectAndNotify(item);
1156
else if ( action == wxACTION_LISTBOX_SELECT )
1160
if ( strArg.empty() )
1161
SelectAndNotify(item);
1165
else if ( action == wxACTION_LISTBOX_SELECTADD )
1167
else if ( action == wxACTION_LISTBOX_UNSELECT )
1169
else if ( action == wxACTION_LISTBOX_MOVEDOWN )
1171
else if ( action == wxACTION_LISTBOX_MOVEUP )
1173
else if ( action == wxACTION_LISTBOX_PAGEDOWN )
1174
ChangeCurrent(GetItemsPerPage());
1175
else if ( action == wxACTION_LISTBOX_PAGEUP )
1176
ChangeCurrent(-GetItemsPerPage());
1177
else if ( action == wxACTION_LISTBOX_START )
1179
else if ( action == wxACTION_LISTBOX_END )
1180
SetCurrentItem(GetCount() - 1);
1181
else if ( action == wxACTION_LISTBOX_UNSELECTALL )
1183
else if ( action == wxACTION_LISTBOX_EXTENDSEL )
1184
ExtendSelection(item);
1185
else if ( action == wxACTION_LISTBOX_FIND )
1186
FindNextItem(strArg);
1187
else if ( action == wxACTION_LISTBOX_ANCHOR )
1188
AnchorSelection(item == -1 ? m_current : item);
1189
else if ( action == wxACTION_LISTBOX_SELECTALL ||
1190
action == wxACTION_LISTBOX_SELTOGGLE )
1191
wxFAIL_MSG(_T("unimplemented yet"));
1193
return wxControl::PerformAction(action, numArg, strArg);
1198
// ============================================================================
1199
// implementation of wxStdListboxInputHandler
1200
// ============================================================================
1202
wxStdListboxInputHandler::wxStdListboxInputHandler(wxInputHandler *handler,
1203
bool toggleOnPressAlways)
1204
: wxStdInputHandler(handler)
1207
m_toggleOnPressAlways = toggleOnPressAlways;
1208
m_actionMouse = wxACTION_NONE;
1209
m_trackMouseOutside = true;
1212
int wxStdListboxInputHandler::HitTest(const wxListBox *lbox,
1213
const wxMouseEvent& event)
1215
int item = HitTestUnsafe(lbox, event);
1217
return FixItemIndex(lbox, item);
1220
int wxStdListboxInputHandler::HitTestUnsafe(const wxListBox *lbox,
1221
const wxMouseEvent& event)
1223
wxPoint pt = event.GetPosition();
1224
pt -= lbox->GetClientAreaOrigin();
1226
lbox->CalcUnscrolledPosition(0, pt.y, NULL, &y);
1227
return y / lbox->GetLineHeight();
1230
int wxStdListboxInputHandler::FixItemIndex(const wxListBox *lbox,
1235
// mouse is above the first item
1238
else if ( item >= lbox->GetCount() )
1240
// mouse is below the last item
1241
item = lbox->GetCount() - 1;
1247
bool wxStdListboxInputHandler::IsValidIndex(const wxListBox *lbox, int item)
1249
return item >= 0 && item < lbox->GetCount();
1253
wxStdListboxInputHandler::SetupCapture(wxListBox *lbox,
1254
const wxMouseEvent& event,
1257
// we currently only allow selecting with the left mouse button, if we
1258
// do need to allow using other buttons too we might use the code
1261
m_btnCapture = event.LeftDown()
1270
wxControlAction action;
1271
if ( lbox->HasMultipleSelection() )
1273
if ( lbox->GetWindowStyle() & wxLB_MULTIPLE )
1275
if ( m_toggleOnPressAlways )
1277
// toggle the item right now
1278
action = wxACTION_LISTBOX_TOGGLE;
1282
m_actionMouse = wxACTION_LISTBOX_SETFOCUS;
1284
else // wxLB_EXTENDED listbox
1286
// simple click in an extended sel listbox clears the old
1287
// selection and adds the clicked item to it then, ctrl-click
1288
// toggles an item to it and shift-click adds a range between
1289
// the old selection anchor and the clicked item
1290
if ( event.ControlDown() )
1292
lbox->PerformAction(wxACTION_LISTBOX_ANCHOR, item);
1294
action = wxACTION_LISTBOX_TOGGLE;
1296
else if ( event.ShiftDown() )
1298
action = wxACTION_LISTBOX_EXTENDSEL;
1300
else // simple click
1302
lbox->PerformAction(wxACTION_LISTBOX_ANCHOR, item);
1304
action = wxACTION_LISTBOX_SELECT;
1307
m_actionMouse = wxACTION_LISTBOX_EXTENDSEL;
1310
else // single selection
1313
action = wxACTION_LISTBOX_SELECT;
1316
// by default we always do track it
1317
m_trackMouseOutside = true;
1322
bool wxStdListboxInputHandler::HandleKey(wxInputConsumer *consumer,
1323
const wxKeyEvent& event,
1326
// we're only interested in the key press events
1327
if ( pressed && !event.AltDown() )
1329
bool isMoveCmd = true;
1330
int style = consumer->GetInputWindow()->GetWindowStyle();
1332
wxControlAction action;
1335
int keycode = event.GetKeyCode();
1340
action = wxACTION_LISTBOX_MOVEUP;
1344
action = wxACTION_LISTBOX_MOVEDOWN;
1350
action = wxACTION_LISTBOX_PAGEUP;
1356
action = wxACTION_LISTBOX_PAGEDOWN;
1360
action = wxACTION_LISTBOX_START;
1364
action = wxACTION_LISTBOX_END;
1369
if ( style & wxLB_MULTIPLE )
1371
action = wxACTION_LISTBOX_TOGGLE;
1377
action = wxACTION_LISTBOX_ACTIVATE;
1382
if ( (keycode < 255) && wxIsalnum((wxChar)keycode) )
1384
action = wxACTION_LISTBOX_FIND;
1385
strArg = (wxChar)keycode;
1389
if ( !action.IsEmpty() )
1391
consumer->PerformAction(action, -1, strArg);
1395
if ( style & wxLB_SINGLE )
1397
// the current item is always the one selected
1398
consumer->PerformAction(wxACTION_LISTBOX_SELECT);
1400
else if ( style & wxLB_EXTENDED )
1402
if ( event.ShiftDown() )
1403
consumer->PerformAction(wxACTION_LISTBOX_EXTENDSEL);
1406
// select the item and make it the new selection anchor
1407
consumer->PerformAction(wxACTION_LISTBOX_SELECT);
1408
consumer->PerformAction(wxACTION_LISTBOX_ANCHOR);
1411
//else: nothing to do for multiple selection listboxes
1418
return wxStdInputHandler::HandleKey(consumer, event, pressed);
1421
bool wxStdListboxInputHandler::HandleMouse(wxInputConsumer *consumer,
1422
const wxMouseEvent& event)
1424
wxListBox *lbox = wxStaticCast(consumer->GetInputWindow(), wxListBox);
1425
int item = HitTest(lbox, event);
1426
wxControlAction action;
1428
// when the left mouse button is pressed, capture the mouse and track the
1429
// item under mouse (if the mouse leaves the window, we will still be
1430
// getting the mouse move messages generated by wxScrollWindow)
1431
if ( event.LeftDown() )
1433
// capture the mouse to track the selected item
1434
lbox->CaptureMouse();
1436
action = SetupCapture(lbox, event, item);
1438
else if ( m_btnCapture && event.ButtonUp(m_btnCapture) )
1440
// when the left mouse button is released, release the mouse too
1441
wxWindow *winCapture = wxWindow::GetCapture();
1444
winCapture->ReleaseMouse();
1447
action = m_actionMouse;
1449
//else: the mouse wasn't presed over the listbox, only released here
1451
else if ( event.LeftDClick() )
1453
action = wxACTION_LISTBOX_ACTIVATE;
1456
if ( !action.IsEmpty() )
1458
lbox->PerformAction(action, item);
1463
return wxStdInputHandler::HandleMouse(consumer, event);
1466
bool wxStdListboxInputHandler::HandleMouseMove(wxInputConsumer *consumer,
1467
const wxMouseEvent& event)
1469
wxWindow *winCapture = wxWindow::GetCapture();
1470
if ( winCapture && (event.GetEventObject() == winCapture) )
1472
wxListBox *lbox = wxStaticCast(consumer->GetInputWindow(), wxListBox);
1474
if ( !m_btnCapture || !m_trackMouseOutside )
1476
// someone captured the mouse for us (we always set m_btnCapture
1477
// when we do it ourselves): in this case we only react to
1478
// the mouse messages when they happen inside the listbox
1479
if ( lbox->HitTest(event.GetPosition()) != wxHT_WINDOW_INSIDE )
1483
int item = HitTest(lbox, event);
1484
if ( !m_btnCapture )
1486
// now that we have the mouse inside the listbox, do capture it
1487
// normally - but ensure that we will still ignore the outside
1489
SetupCapture(lbox, event, item);
1491
m_trackMouseOutside = false;
1494
if ( IsValidIndex(lbox, item) )
1496
// pass something into strArg to tell the listbox that it shouldn't
1497
// send the notification message: see PerformAction() above
1498
lbox->PerformAction(m_actionMouse, item, _T("no"));
1500
// else: don't pass invalid index to the listbox
1502
else // we don't have capture any more
1506
// if we lost capture unexpectedly (someone else took the capture
1507
// from us), return to a consistent state
1512
return wxStdInputHandler::HandleMouseMove(consumer, event);
1515
#endif // wxUSE_LISTBOX