]> git.saurik.com Git - wxWidgets.git/blame - src/univ/listbox.cpp
Second part of #15224 fix: AddRows, AddColumns (dghart)
[wxWidgets.git] / src / univ / listbox.cpp
CommitLineData
1e6feb95 1/////////////////////////////////////////////////////////////////////////////
8228b893 2// Name: src/univ/listbox.cpp
1e6feb95
VZ
3// Purpose: wxListBox implementation
4// Author: Vadim Zeitlin
5// Modified by:
6// Created: 30.08.00
442b35b5 7// Copyright: (c) 2000 SciTech Software, Inc. (www.scitechsoft.com)
65571936 8// Licence: wxWindows licence
1e6feb95
VZ
9/////////////////////////////////////////////////////////////////////////////
10
11// ============================================================================
12// declarations
13// ============================================================================
14
15// ----------------------------------------------------------------------------
16// headers
17// ----------------------------------------------------------------------------
18
1e6feb95
VZ
19#include "wx/wxprec.h"
20
21#ifdef __BORLANDC__
22 #pragma hdrstop
23#endif
24
25#if wxUSE_LISTBOX
26
27#ifndef WX_PRECOMP
28 #include "wx/log.h"
29
30 #include "wx/dcclient.h"
31 #include "wx/listbox.h"
32 #include "wx/validate.h"
33#endif
34
35#include "wx/univ/renderer.h"
36#include "wx/univ/inphand.h"
37#include "wx/univ/theme.h"
38
9467bdb7
VZ
39// ----------------------------------------------------------------------------
40// wxStdListboxInputHandler: handles mouse and kbd in a single or multi
41// selection listbox
42// ----------------------------------------------------------------------------
43
44class WXDLLEXPORT wxStdListboxInputHandler : public wxStdInputHandler
45{
46public:
47 // if pressing the mouse button in a multiselection listbox should toggle
48 // the item under mouse immediately, then specify true as the second
49 // parameter (this is the standard behaviour, under GTK the item is toggled
50 // only when the mouse is released in the multi selection listbox)
51 wxStdListboxInputHandler(wxInputHandler *inphand,
52 bool toggleOnPressAlways = true);
53
54 // base class methods
55 virtual bool HandleKey(wxInputConsumer *consumer,
56 const wxKeyEvent& event,
57 bool pressed);
58 virtual bool HandleMouse(wxInputConsumer *consumer,
59 const wxMouseEvent& event);
60 virtual bool HandleMouseMove(wxInputConsumer *consumer,
61 const wxMouseEvent& event);
62
63protected:
64 // return the item under mouse, 0 if the mouse is above the listbox or
65 // GetCount() if it is below it
66 int HitTest(const wxListBox *listbox, const wxMouseEvent& event);
67
68 // parts of HitTest(): first finds the pseudo (because not in range) index
69 // of the item and the second one adjusts it if necessary - that is if the
70 // third one returns false
71 int HitTestUnsafe(const wxListBox *listbox, const wxMouseEvent& event);
72 int FixItemIndex(const wxListBox *listbox, int item);
73 bool IsValidIndex(const wxListBox *listbox, int item);
74
75 // init m_btnCapture and m_actionMouse
76 wxControlAction SetupCapture(wxListBox *lbox,
77 const wxMouseEvent& event,
78 int item);
79
80 wxRenderer *m_renderer;
81
82 // the button which initiated the mouse capture (currently 0 or 1)
83 int m_btnCapture;
84
85 // the action to perform when the mouse moves while we capture it
86 wxControlAction m_actionMouse;
87
88 // the ctor parameter toggleOnPressAlways (see comments near it)
89 bool m_toggleOnPressAlways;
90
91 // do we track the mouse outside the window when it is captured?
92 bool m_trackMouseOutside;
93};
94
1e6feb95
VZ
95// ============================================================================
96// implementation of wxListBox
97// ============================================================================
98
1e6feb95
VZ
99BEGIN_EVENT_TABLE(wxListBox, wxListBoxBase)
100 EVT_SIZE(wxListBox::OnSize)
1e6feb95
VZ
101END_EVENT_TABLE()
102
103// ----------------------------------------------------------------------------
104// construction
105// ----------------------------------------------------------------------------
106
107void wxListBox::Init()
108{
109 // will be calculated later when needed
110 m_lineHeight = 0;
111 m_itemsPerPage = 0;
112 m_maxWidth = 0;
113 m_scrollRangeY = 0;
114 m_maxWidthItem = -1;
6f34862c 115 m_strings.unsorted = NULL;
1e6feb95
VZ
116
117 // no items hence no current item
118 m_current = -1;
119 m_selAnchor = -1;
a290fa5a 120 m_currentChanged = false;
1e6feb95
VZ
121
122 // no need to update anything initially
123 m_updateCount = 0;
124
125 // no scrollbars to show nor update
126 m_updateScrollbarX =
127 m_showScrollbarX =
128 m_updateScrollbarY =
a290fa5a 129 m_showScrollbarY = false;
1e6feb95
VZ
130}
131
584ad2a3
MB
132wxListBox::wxListBox(wxWindow *parent,
133 wxWindowID id,
134 const wxPoint &pos,
135 const wxSize &size,
136 const wxArrayString& choices,
137 long style,
138 const wxValidator& validator,
139 const wxString &name)
afe13769 140 :wxScrollHelper(this)
584ad2a3
MB
141{
142 Init();
143
144 Create(parent, id, pos, size, choices, style, validator, name);
145}
146
147bool wxListBox::Create(wxWindow *parent,
148 wxWindowID id,
149 const wxPoint &pos,
150 const wxSize &size,
151 const wxArrayString& choices,
152 long style,
153 const wxValidator& validator,
154 const wxString &name)
155{
156 wxCArrayString chs(choices);
157
158 return Create(parent, id, pos, size, chs.GetCount(), chs.GetStrings(),
159 style, validator, name);
160}
161
1e6feb95
VZ
162bool wxListBox::Create(wxWindow *parent,
163 wxWindowID id,
164 const wxPoint &pos,
165 const wxSize &size,
166 int n,
167 const wxString choices[],
168 long style,
169 const wxValidator& validator,
170 const wxString &name)
171{
172 // for compatibility accept both the new and old styles - they mean the
173 // same thing for us
174 if ( style & wxLB_ALWAYS_SB )
175 style |= wxALWAYS_SHOW_SB;
176
177 // if we don't have neither multiple nor extended flag, we must have the
178 // single selection listbox
179 if ( !(style & (wxLB_MULTIPLE | wxLB_EXTENDED)) )
180 style |= wxLB_SINGLE;
181
372c2812
RR
182#if wxUSE_TWO_WINDOWS
183 style |= wxVSCROLL|wxHSCROLL;
184 if ((style & wxBORDER_MASK) == 0)
185 style |= wxBORDER_SUNKEN;
186#endif
187
32b13913 188 if ( !wxControl::Create(parent, id, pos, size, style,
61fef19b 189 validator, name) )
a290fa5a 190 return false;
1e6feb95 191
6f34862c
VZ
192 if ( IsSorted() )
193 m_strings.sorted = new wxSortedArrayString;
194 else
195 m_strings.unsorted = new wxArrayString;
1e6feb95
VZ
196
197 Set(n, choices);
198
170acdc9 199 SetInitialSize(size);
1e6feb95
VZ
200
201 CreateInputHandler(wxINP_HANDLER_LISTBOX);
202
a290fa5a 203 return true;
1e6feb95
VZ
204}
205
206wxListBox::~wxListBox()
207{
d00b880a
VZ
208 // call this just to free the client data -- and avoid leaking memory
209 DoClear();
18dbdd3c 210
6f34862c
VZ
211 if ( IsSorted() )
212 delete m_strings.sorted;
213 else
214 delete m_strings.unsorted;
18dbdd3c 215
6f34862c
VZ
216 m_strings.sorted = NULL;
217}
218
219// ----------------------------------------------------------------------------
220// accessing strings
221// ----------------------------------------------------------------------------
222
223unsigned int wxListBox::GetCount() const
224{
225 return IsSorted() ? m_strings.sorted->size()
226 : m_strings.unsorted->size();
227}
228
229wxString wxListBox::GetString(unsigned int n) const
230{
231 return IsSorted() ? m_strings.sorted->Item(n)
232 : m_strings.unsorted->Item(n);
233}
234
235int wxListBox::FindString(const wxString& s, bool bCase) const
236{
237 return IsSorted() ? m_strings.sorted->Index(s, bCase)
238 : m_strings.unsorted->Index(s, bCase);
1e6feb95
VZ
239}
240
241// ----------------------------------------------------------------------------
242// adding/inserting strings
243// ----------------------------------------------------------------------------
244
a236aa20
VZ
245int wxListBox::DoInsertItems(const wxArrayStringsAdapter& items,
246 unsigned int pos,
247 void **clientData,
248 wxClientDataType type)
e1d6e01c 249{
a236aa20 250 int idx = wxNOT_FOUND;
1e6feb95 251
a236aa20
VZ
252 const unsigned int numItems = items.GetCount();
253 for ( unsigned int i = 0; i < numItems; ++i )
1e6feb95 254 {
a236aa20 255 const wxString& item = items[i];
6f34862c
VZ
256 idx = IsSorted() ? m_strings.sorted->Add(item)
257 : (m_strings.unsorted->Insert(item, pos), pos++);
1e6feb95 258
a236aa20
VZ
259 m_itemsClientData.Insert(NULL, idx);
260 AssignNewItemClientData(idx, clientData, i, type);
1e6feb95 261
a236aa20
VZ
262 // call the wxCheckListBox hook
263 OnItemInserted(idx);
1e6feb95
VZ
264 }
265
266 // the number of items has changed so we might have to show the scrollbar
a290fa5a 267 m_updateScrollbarY = true;
1e6feb95
VZ
268
269 // the max width also might have changed - just recalculate it instead of
270 // keeping track of it here, this is probably more efficient for a typical
271 // use pattern
272 RefreshHorzScrollbar();
273
274 // note that we have to refresh all the items after the ones we inserted,
275 // not just these items
276 RefreshFromItemToEnd(pos);
1e6feb95 277
a236aa20 278 return idx;
1e6feb95
VZ
279}
280
aa61d352 281void wxListBox::SetString(unsigned int n, const wxString& s)
1e6feb95 282{
9a83f860 283 wxCHECK_RET( !IsSorted(), wxT("can't set string in sorted listbox") );
e1d6e01c 284
6f34862c
VZ
285 if ( IsSorted() )
286 (*m_strings.sorted)[n] = s;
287 else
288 (*m_strings.unsorted)[n] = s;
e1d6e01c 289
1e6feb95
VZ
290 if ( HasHorzScrollbar() )
291 {
292 // we need to update m_maxWidth as changing the string may cause the
293 // horz scrollbar [dis]appear
294 wxCoord width;
e1d6e01c 295
1e6feb95
VZ
296 GetTextExtent(s, &width, NULL);
297
298 // it might have increased if the new string is long
299 if ( width > m_maxWidth )
300 {
301 m_maxWidth = width;
302 m_maxWidthItem = n;
a290fa5a 303 m_updateScrollbarX = true;
1e6feb95
VZ
304 }
305 // or also decreased if the old string was the longest one
87815d3d 306 else if ( n == (unsigned int)m_maxWidthItem )
1e6feb95
VZ
307 {
308 RefreshHorzScrollbar();
309 }
310 }
1e6feb95
VZ
311
312 RefreshItem(n);
313}
314
315// ----------------------------------------------------------------------------
316// removing strings
317// ----------------------------------------------------------------------------
318
319void wxListBox::DoClear()
320{
6f34862c
VZ
321 if ( IsSorted() )
322 m_strings.sorted->Clear();
323 else
324 m_strings.unsorted->Clear();
1e6feb95 325
1e6feb95
VZ
326 m_itemsClientData.Clear();
327 m_selections.Clear();
328
329 m_current = -1;
1e6feb95 330
a290fa5a 331 m_updateScrollbarY = true;
1e6feb95
VZ
332
333 RefreshHorzScrollbar();
334
335 RefreshAll();
336}
337
a236aa20 338void wxListBox::DoDeleteOneItem(unsigned int n)
1e6feb95 339{
8228b893 340 wxCHECK_RET( IsValid(n),
9a83f860 341 wxT("invalid index in wxListBox::Delete") );
1e6feb95
VZ
342
343 // do it before removing the index as otherwise the last item will not be
344 // refreshed (as GetCount() will be decremented)
345 RefreshFromItemToEnd(n);
346
6f34862c
VZ
347 if ( IsSorted() )
348 m_strings.sorted->RemoveAt(n);
349 else
350 m_strings.unsorted->RemoveAt(n);
1e6feb95 351
1e6feb95
VZ
352 m_itemsClientData.RemoveAt(n);
353
354 // when the item disappears we must not keep using its index
aa61d352 355 if ( (int)n == m_current )
1e6feb95
VZ
356 {
357 m_current = -1;
358 }
87815d3d 359 else if ( (int)n < m_current )
1e6feb95
VZ
360 {
361 m_current--;
362 }
363 //else: current item may stay
364
365 // update the selections array: the indices of all seletected items after
366 // the one being deleted must change and the item itselfm ust be removed
367 int index = wxNOT_FOUND;
aa61d352
VZ
368 unsigned int count = m_selections.GetCount();
369 for ( unsigned int item = 0; item < count; item++ )
1e6feb95 370 {
87815d3d 371 if ( m_selections[item] == (int)n )
1e6feb95
VZ
372 {
373 // remember to delete it later
374 index = item;
375 }
87815d3d 376 else if ( m_selections[item] > (int)n )
1e6feb95
VZ
377 {
378 // to account for the index shift
379 m_selections[item]--;
380 }
381 //else: nothing changed for this one
382 }
383
384 if ( index != wxNOT_FOUND )
385 {
386 m_selections.RemoveAt(index);
387 }
388
389 // the number of items has changed, hence the scrollbar may disappear
a290fa5a 390 m_updateScrollbarY = true;
1e6feb95
VZ
391
392 // finally, if the longest item was deleted the scrollbar may disappear
87815d3d 393 if ( (int)n == m_maxWidthItem )
1e6feb95
VZ
394 {
395 RefreshHorzScrollbar();
396 }
397}
398
399// ----------------------------------------------------------------------------
400// client data handling
401// ----------------------------------------------------------------------------
402
aa61d352 403void wxListBox::DoSetItemClientData(unsigned int n, void* clientData)
1e6feb95
VZ
404{
405 m_itemsClientData[n] = clientData;
406}
407
aa61d352 408void *wxListBox::DoGetItemClientData(unsigned int n) const
1e6feb95
VZ
409{
410 return m_itemsClientData[n];
411}
412
1e6feb95
VZ
413// ----------------------------------------------------------------------------
414// selection
415// ----------------------------------------------------------------------------
416
c6179a84 417void wxListBox::DoSetSelection(int n, bool select)
1e6feb95
VZ
418{
419 if ( select )
420 {
b3894f30
VS
421 if ( n == wxNOT_FOUND )
422 {
423 if ( !HasMultipleSelection() )
424 {
425 // selecting wxNOT_FOUND is documented to deselect all items
426 DeselectAll();
427 return;
428 }
429 }
430 else if ( m_selections.Index(n) == wxNOT_FOUND )
1e6feb95
VZ
431 {
432 if ( !HasMultipleSelection() )
433 {
434 // selecting an item in a single selection listbox deselects
435 // all the others
436 DeselectAll();
437 }
438
439 m_selections.Add(n);
440
441 RefreshItem(n);
442 }
443 //else: already selected
444 }
445 else // unselect
446 {
447 int index = m_selections.Index(n);
448 if ( index != wxNOT_FOUND )
449 {
450 m_selections.RemoveAt(index);
451
452 RefreshItem(n);
453 }
454 //else: not selected
455 }
456
457 // sanity check: a single selection listbox can't have more than one item
458 // selected
459 wxASSERT_MSG( HasMultipleSelection() || (m_selections.GetCount() < 2),
9a83f860 460 wxT("multiple selected items in single selection lbox?") );
1e6feb95
VZ
461
462 if ( select )
463 {
464 // the newly selected item becomes the current one
465 SetCurrentItem(n);
466 }
467}
468
469int wxListBox::GetSelection() const
470{
e95c145c 471 wxCHECK_MSG( !HasMultipleSelection(), wxNOT_FOUND,
9a83f860 472 wxT("use wxListBox::GetSelections for ths listbox") );
1e6feb95 473
e95c145c 474 return m_selections.IsEmpty() ? wxNOT_FOUND : m_selections[0];
1e6feb95
VZ
475}
476
2ad4f89f 477static int wxCMPFUNC_CONV wxCompareInts(int *n, int *m)
1e6feb95
VZ
478{
479 return *n - *m;
480}
481
482int wxListBox::GetSelections(wxArrayInt& selections) const
483{
484 // always return sorted array to the user
485 selections = m_selections;
aa61d352 486 unsigned int count = m_selections.GetCount();
1e6feb95
VZ
487
488 // don't call sort on an empty array
489 if ( count )
490 {
491 selections.Sort(wxCompareInts);
492 }
493
494 return count;
495}
496
497// ----------------------------------------------------------------------------
498// refresh logic: we use delayed refreshing which allows to avoid multiple
499// refreshes (and hence flicker) in case when several listbox items are
500// added/deleted/changed subsequently
501// ----------------------------------------------------------------------------
502
503void wxListBox::RefreshFromItemToEnd(int from)
504{
505 RefreshItems(from, GetCount() - from);
506}
507
508void wxListBox::RefreshItems(int from, int count)
509{
510 switch ( m_updateCount )
511 {
512 case 0:
513 m_updateFrom = from;
514 m_updateCount = count;
515 break;
516
517 case -1:
518 // we refresh everything anyhow
519 break;
520
521 default:
522 // add these items to the others which we have to refresh
523 if ( m_updateFrom < from )
524 {
525 count += from - m_updateFrom;
526 if ( m_updateCount < count )
527 m_updateCount = count;
528 }
529 else // m_updateFrom >= from
530 {
531 int updateLast = wxMax(m_updateFrom + m_updateCount,
532 from + count);
533 m_updateFrom = from;
534 m_updateCount = updateLast - m_updateFrom;
535 }
536 }
537}
538
539void wxListBox::RefreshItem(int n)
540{
541 switch ( m_updateCount )
542 {
543 case 0:
544 // refresh this item only
545 m_updateFrom = n;
546 m_updateCount = 1;
547 break;
548
549 case -1:
550 // we refresh everything anyhow
551 break;
552
553 default:
554 // add this item to the others which we have to refresh
555 if ( m_updateFrom < n )
556 {
557 if ( m_updateCount < n - m_updateFrom + 1 )
558 m_updateCount = n - m_updateFrom + 1;
559 }
560 else // n <= m_updateFrom
561 {
562 m_updateCount += m_updateFrom - n;
563 m_updateFrom = n;
564 }
565 }
566}
567
568void wxListBox::RefreshAll()
569{
570 m_updateCount = -1;
571}
572
573void wxListBox::RefreshHorzScrollbar()
574{
575 m_maxWidth = 0; // recalculate it
a290fa5a 576 m_updateScrollbarX = true;
1e6feb95
VZ
577}
578
579void wxListBox::UpdateScrollbars()
580{
581 wxSize size = GetClientSize();
582
583 // is our height enough to show all items?
aa61d352 584 unsigned int nLines = GetCount();
1e6feb95 585 wxCoord lineHeight = GetLineHeight();
e95c145c 586 bool showScrollbarY = (int)nLines*lineHeight > size.y;
1e6feb95
VZ
587
588 // check the width too if required
589 wxCoord charWidth, maxWidth;
590 bool showScrollbarX;
591 if ( HasHorzScrollbar() )
592 {
593 charWidth = GetCharWidth();
594 maxWidth = GetMaxWidth();
595 showScrollbarX = maxWidth > size.x;
596 }
597 else // never show it
598 {
599 charWidth = maxWidth = 0;
a290fa5a 600 showScrollbarX = false;
1e6feb95
VZ
601 }
602
603 // what should be the scrollbar range now?
604 int scrollRangeX = showScrollbarX
605 ? (maxWidth + charWidth - 1) / charWidth + 2 // FIXME
606 : 0;
607 int scrollRangeY = showScrollbarY
608 ? nLines +
609 (size.y % lineHeight + lineHeight - 1) / lineHeight
610 : 0;
611
612 // reset scrollbars if something changed: either the visibility status
613 // or the range of a scrollbar which is shown
614 if ( (showScrollbarY != m_showScrollbarY) ||
615 (showScrollbarX != m_showScrollbarX) ||
616 (showScrollbarY && (scrollRangeY != m_scrollRangeY)) ||
617 (showScrollbarX && (scrollRangeX != m_scrollRangeX)) )
618 {
619 int x, y;
620 GetViewStart(&x, &y);
621 SetScrollbars(charWidth, lineHeight,
622 scrollRangeX, scrollRangeY,
623 x, y);
624
625 m_showScrollbarX = showScrollbarX;
626 m_showScrollbarY = showScrollbarY;
627
628 m_scrollRangeX = scrollRangeX;
629 m_scrollRangeY = scrollRangeY;
630 }
631}
632
633void wxListBox::UpdateItems()
634{
635 // only refresh the items which must be refreshed
636 if ( m_updateCount == -1 )
637 {
638 // refresh all
9a83f860 639 wxLogTrace(wxT("listbox"), wxT("Refreshing all"));
1e6feb95
VZ
640
641 Refresh();
642 }
643 else
644 {
645 wxSize size = GetClientSize();
646 wxRect rect;
647 rect.width = size.x;
648 rect.height = size.y;
649 rect.y += m_updateFrom*GetLineHeight();
650 rect.height = m_updateCount*GetLineHeight();
651
652 // we don't need to calculate x position as we always refresh the
653 // entire line(s)
654 CalcScrolledPosition(0, rect.y, NULL, &rect.y);
655
9a83f860 656 wxLogTrace(wxT("listbox"), wxT("Refreshing items %d..%d (%d-%d)"),
1e6feb95
VZ
657 m_updateFrom, m_updateFrom + m_updateCount - 1,
658 rect.GetTop(), rect.GetBottom());
659
a290fa5a 660 Refresh(true, &rect);
1e6feb95
VZ
661 }
662}
663
e39af974 664void wxListBox::OnInternalIdle()
1e6feb95
VZ
665{
666 if ( m_updateScrollbarY || m_updateScrollbarX )
667 {
668 UpdateScrollbars();
669
670 m_updateScrollbarX =
a290fa5a 671 m_updateScrollbarY = false;
1e6feb95
VZ
672 }
673
674 if ( m_currentChanged )
675 {
676 DoEnsureVisible(m_current);
677
a290fa5a 678 m_currentChanged = false;
1e6feb95
VZ
679 }
680
681 if ( m_updateCount )
682 {
683 UpdateItems();
684
685 m_updateCount = 0;
686 }
6f4767ac 687 wxListBoxBase::OnInternalIdle();
1e6feb95
VZ
688}
689
690// ----------------------------------------------------------------------------
691// drawing
692// ----------------------------------------------------------------------------
693
694wxBorder wxListBox::GetDefaultBorder() const
695{
696 return wxBORDER_SUNKEN;
697}
698
699void wxListBox::DoDraw(wxControlRenderer *renderer)
700{
701 // adjust the DC to account for scrolling
702 wxDC& dc = renderer->GetDC();
703 PrepareDC(dc);
704 dc.SetFont(GetFont());
705
706 // get the update rect
707 wxRect rectUpdate = GetUpdateClientRect();
708
709 int yTop, yBottom;
710 CalcUnscrolledPosition(0, rectUpdate.GetTop(), NULL, &yTop);
711 CalcUnscrolledPosition(0, rectUpdate.GetBottom(), NULL, &yBottom);
712
713 // get the items which must be redrawn
714 wxCoord lineHeight = GetLineHeight();
aa61d352
VZ
715 unsigned int itemFirst = yTop / lineHeight,
716 itemLast = (yBottom + lineHeight - 1) / lineHeight,
6f34862c 717 itemMax = GetCount();
1e6feb95
VZ
718
719 if ( itemFirst >= itemMax )
720 return;
721
722 if ( itemLast > itemMax )
723 itemLast = itemMax;
724
725 // do draw them
9a83f860 726 wxLogTrace(wxT("listbox"), wxT("Repainting items %d..%d"),
1e6feb95
VZ
727 itemFirst, itemLast);
728
729 DoDrawRange(renderer, itemFirst, itemLast);
730}
731
732void wxListBox::DoDrawRange(wxControlRenderer *renderer,
733 int itemFirst, int itemLast)
734{
735 renderer->DrawItems(this, itemFirst, itemLast);
736}
737
738// ----------------------------------------------------------------------------
739// size calculations
740// ----------------------------------------------------------------------------
741
742bool wxListBox::SetFont(const wxFont& font)
743{
744 if ( !wxControl::SetFont(font) )
a290fa5a 745 return false;
1e6feb95
VZ
746
747 CalcItemsPerPage();
748
749 RefreshAll();
750
a290fa5a 751 return true;
1e6feb95
VZ
752}
753
754void wxListBox::CalcItemsPerPage()
755{
756 m_lineHeight = GetRenderer()->GetListboxItemHeight(GetCharHeight());
757 m_itemsPerPage = GetClientSize().y / m_lineHeight;
758}
759
760int wxListBox::GetItemsPerPage() const
761{
762 if ( !m_itemsPerPage )
763 {
764 wxConstCast(this, wxListBox)->CalcItemsPerPage();
765 }
766
767 return m_itemsPerPage;
768}
769
770wxCoord wxListBox::GetLineHeight() const
771{
772 if ( !m_lineHeight )
773 {
774 wxConstCast(this, wxListBox)->CalcItemsPerPage();
775 }
776
777 return m_lineHeight;
778}
779
780wxCoord wxListBox::GetMaxWidth() const
781{
782 if ( m_maxWidth == 0 )
783 {
784 wxListBox *self = wxConstCast(this, wxListBox);
785 wxCoord width;
6f34862c 786 unsigned int count = GetCount();
aa61d352 787 for ( unsigned int n = 0; n < count; n++ )
1e6feb95 788 {
e1d6e01c 789 GetTextExtent(this->GetString(n), &width, NULL);
1e6feb95
VZ
790 if ( width > m_maxWidth )
791 {
792 self->m_maxWidth = width;
793 self->m_maxWidthItem = n;
794 }
795 }
796 }
797
798 return m_maxWidth;
799}
800
801void wxListBox::OnSize(wxSizeEvent& event)
802{
803 // recalculate the number of items per page
804 CalcItemsPerPage();
805
806 // the scrollbars might [dis]appear
807 m_updateScrollbarX =
a290fa5a 808 m_updateScrollbarY = true;
1e6feb95
VZ
809
810 event.Skip();
811}
812
813void wxListBox::DoSetFirstItem(int n)
814{
815 SetCurrentItem(n);
816}
817
818void wxListBox::DoSetSize(int x, int y,
819 int width, int height,
820 int sizeFlags)
821{
822 if ( GetWindowStyle() & wxLB_INT_HEIGHT )
823 {
824 // we must round up the height to an entire number of rows
825
826 // the client area must contain an int number of rows, so take borders
827 // into account
828 wxRect rectBorders = GetRenderer()->GetBorderDimensions(GetBorder());
829 wxCoord hBorders = rectBorders.y + rectBorders.height;
830
831 wxCoord hLine = GetLineHeight();
832 height = ((height - hBorders + hLine - 1) / hLine)*hLine + hBorders;
833 }
834
61fef19b 835 wxListBoxBase::DoSetSize(x, y, width, height, sizeFlags);
1e6feb95
VZ
836}
837
838wxSize wxListBox::DoGetBestClientSize() const
839{
840 wxCoord width = 0,
841 height = 0;
842
6f34862c 843 unsigned int count = GetCount();
aa61d352 844 for ( unsigned int n = 0; n < count; n++ )
1e6feb95
VZ
845 {
846 wxCoord w,h;
e1d6e01c 847 GetTextExtent(this->GetString(n), &w, &h);
1e6feb95
VZ
848
849 if ( w > width )
850 width = w;
851 if ( h > height )
852 height = h;
853 }
854
855 // if the listbox is empty, still give it some non zero (even if
856 // arbitrary) size - otherwise, leave small margin around the strings
857 if ( !width )
858 width = 100;
859 else
860 width += 3*GetCharWidth();
861
862 if ( !height )
863 height = GetCharHeight();
864
865 // we need the height of the entire listbox, not just of one line
866 height *= wxMax(count, 7);
867
868 return wxSize(width, height);
869}
870
871// ----------------------------------------------------------------------------
872// listbox actions
873// ----------------------------------------------------------------------------
874
875bool wxListBox::SendEvent(wxEventType type, int item)
876{
1e6feb95
VZ
877 wxCommandEvent event(type, m_windowId);
878 event.SetEventObject(this);
879
880 // use the current item by default
881 if ( item == -1 )
882 {
883 item = m_current;
884 }
885
886 // client data and string parameters only make sense if we have an item
887 if ( item != -1 )
888 {
889 if ( HasClientObjectData() )
890 event.SetClientObject(GetClientObject(item));
891 else if ( HasClientUntypedData() )
892 event.SetClientData(GetClientData(item));
893
894 event.SetString(GetString(item));
895 }
896
687706f5 897 event.SetInt(item);
1e6feb95
VZ
898
899 return GetEventHandler()->ProcessEvent(event);
900}
901
902void wxListBox::SetCurrentItem(int n)
903{
904 if ( n != m_current )
905 {
906 if ( m_current != -1 )
907 RefreshItem(m_current);
908
909 m_current = n;
910
911 if ( m_current != -1 )
912 {
a290fa5a 913 m_currentChanged = true;
1e6feb95
VZ
914
915 RefreshItem(m_current);
916 }
917 }
918 //else: nothing to do
919}
920
921bool wxListBox::FindItem(const wxString& prefix, bool strictlyAfter)
922{
aa61d352 923 unsigned int count = GetCount();
8228b893 924 if ( count==0 )
1e6feb95
VZ
925 {
926 // empty listbox, we can't find anything in it
a290fa5a 927 return false;
1e6feb95
VZ
928 }
929
930 // start either from the current item or from the next one if strictlyAfter
931 // is true
932 int first;
933 if ( strictlyAfter )
934 {
935 // the following line will set first correctly to 0 if there is no
936 // selection (m_current == -1)
e95c145c 937 first = m_current == (int)(count - 1) ? 0 : m_current + 1;
1e6feb95
VZ
938 }
939 else // start with the current
940 {
941 first = m_current == -1 ? 0 : m_current;
942 }
943
944 int last = first == 0 ? count - 1 : first - 1;
945
946 // if this is not true we'd never exit from the loop below!
9a83f860 947 wxASSERT_MSG( first < (int)count && last < (int)count, wxT("logic error") );
1e6feb95
VZ
948
949 // precompute it outside the loop
950 size_t len = prefix.length();
951
952 // loop over all items in the listbox
e95c145c 953 for ( int item = first; item != (int)last; item < (int)(count - 1) ? item++ : item = 0 )
1e6feb95 954 {
e1d6e01c 955 if ( wxStrnicmp(this->GetString(item).c_str(), prefix, len) == 0 )
1e6feb95
VZ
956 {
957 SetCurrentItem(item);
958
959 if ( !(GetWindowStyle() & wxLB_MULTIPLE) )
960 {
961 DeselectAll(item);
962 SelectAndNotify(item);
963
964 if ( GetWindowStyle() & wxLB_EXTENDED )
965 AnchorSelection(item);
966 }
967
a290fa5a 968 return true;
1e6feb95
VZ
969 }
970 }
971
972 // nothing found
a290fa5a 973 return false;
1e6feb95
VZ
974}
975
976void wxListBox::EnsureVisible(int n)
977{
978 if ( m_updateScrollbarY )
979 {
980 UpdateScrollbars();
981
982 m_updateScrollbarX =
a290fa5a 983 m_updateScrollbarY = false;
1e6feb95
VZ
984 }
985
986 DoEnsureVisible(n);
987}
988
989void wxListBox::DoEnsureVisible(int n)
990{
991 if ( !m_showScrollbarY )
992 {
993 // nothing to do - everything is shown anyhow
994 return;
995 }
996
997 int first;
998 GetViewStart(0, &first);
999 if ( first > n )
1000 {
1001 // we need to scroll upwards, so make the current item appear on top
1002 // of the shown range
1003 Scroll(0, n);
1004 }
1005 else
1006 {
1007 int last = first + GetClientSize().y / GetLineHeight() - 1;
1008 if ( last < n )
1009 {
1010 // scroll down: the current item appears at the bottom of the
1011 // range
1012 Scroll(0, n - (last - first));
1013 }
1014 }
1015}
1016
1017void wxListBox::ChangeCurrent(int diff)
1018{
1019 int current = m_current == -1 ? 0 : m_current;
1020
1021 current += diff;
1022
1023 int last = GetCount() - 1;
1024 if ( current < 0 )
1025 current = 0;
1026 else if ( current > last )
1027 current = last;
1028
1029 SetCurrentItem(current);
1030}
1031
1032void wxListBox::ExtendSelection(int itemTo)
1033{
1034 // if we don't have the explicit values for selection start/end, make them
1035 // up
1036 if ( m_selAnchor == -1 )
1037 m_selAnchor = m_current;
1038
1039 if ( itemTo == -1 )
1040 itemTo = m_current;
1041
1042 // swap the start/end of selection range if necessary
1043 int itemFrom = m_selAnchor;
1044 if ( itemFrom > itemTo )
1045 {
1046 int itemTmp = itemFrom;
1047 itemFrom = itemTo;
1048 itemTo = itemTmp;
1049 }
1050
1051 // the selection should now include all items in the range between the
1052 // anchor and the specified item and only them
1053
1054 int n;
1055 for ( n = 0; n < itemFrom; n++ )
1056 {
1057 Deselect(n);
1058 }
1059
1060 for ( ; n <= itemTo; n++ )
1061 {
1062 SetSelection(n);
1063 }
1064
aa61d352 1065 unsigned int count = GetCount();
8228b893 1066 for ( ; n < (int)count; n++ )
1e6feb95
VZ
1067 {
1068 Deselect(n);
1069 }
1070}
1071
2b5f62a0 1072void wxListBox::DoSelect(int item, bool sel)
1e6feb95
VZ
1073{
1074 if ( item != -1 )
1075 {
1076 // go to this item first
1077 SetCurrentItem(item);
1078 }
1079
1080 // the current item is the one we want to change: either it was just
1081 // changed above to be the same as item or item == -1 in which we case we
1082 // are supposed to use the current one anyhow
1083 if ( m_current != -1 )
1084 {
1085 // [de]select it
1086 SetSelection(m_current, sel);
1087 }
1088}
1089
1090void wxListBox::SelectAndNotify(int item)
1091{
2b5f62a0 1092 DoSelect(item);
1e6feb95 1093
ce7fe42e 1094 SendEvent(wxEVT_LISTBOX);
1e6feb95
VZ
1095}
1096
1097void wxListBox::Activate(int item)
1098{
1099 if ( item != -1 )
1100 SetCurrentItem(item);
1101 else
1102 item = m_current;
1103
1104 if ( !(GetWindowStyle() & wxLB_MULTIPLE) )
1105 {
1106 DeselectAll(item);
1107 }
1108
1109 if ( item != -1 )
1110 {
2b5f62a0 1111 DoSelect(item);
1e6feb95 1112
ce7fe42e 1113 SendEvent(wxEVT_LISTBOX_DCLICK);
1e6feb95
VZ
1114 }
1115}
1116
1117// ----------------------------------------------------------------------------
1118// input handling
1119// ----------------------------------------------------------------------------
1120
55f095d4
VZ
1121/*
1122 The numArg here is the listbox item index while the strArg is used
1123 differently for the different actions:
1124
1125 a) for wxACTION_LISTBOX_FIND it has the natural meaning: this is the string
1126 to find
1127
1128 b) for wxACTION_LISTBOX_SELECT and wxACTION_LISTBOX_EXTENDSEL it is used
1129 to decide if the listbox should send the notification event (it is empty)
1130 or not (it is not): this allows us to reuse the same action for when the
1131 user is dragging the mouse when it has been released although in the
1132 first case no notification is sent while in the second it is sent.
1133 */
1e6feb95
VZ
1134bool wxListBox::PerformAction(const wxControlAction& action,
1135 long numArg,
1136 const wxString& strArg)
1137{
1138 int item = (int)numArg;
1139
1140 if ( action == wxACTION_LISTBOX_SETFOCUS )
ae1daed0 1141 {
1e6feb95 1142 SetCurrentItem(item);
ae1daed0 1143 }
1e6feb95 1144 else if ( action == wxACTION_LISTBOX_ACTIVATE )
ae1daed0 1145 {
1e6feb95 1146 Activate(item);
ae1daed0 1147 }
1e6feb95
VZ
1148 else if ( action == wxACTION_LISTBOX_TOGGLE )
1149 {
1150 if ( item == -1 )
1151 item = m_current;
1152
1153 if ( IsSelected(item) )
2b5f62a0 1154 DoUnselect(item);
1e6feb95
VZ
1155 else
1156 SelectAndNotify(item);
1157 }
1158 else if ( action == wxACTION_LISTBOX_SELECT )
1159 {
1160 DeselectAll(item);
55f095d4
VZ
1161
1162 if ( strArg.empty() )
1163 SelectAndNotify(item);
1164 else
2b5f62a0 1165 DoSelect(item);
1e6feb95
VZ
1166 }
1167 else if ( action == wxACTION_LISTBOX_SELECTADD )
2b5f62a0 1168 DoSelect(item);
1e6feb95 1169 else if ( action == wxACTION_LISTBOX_UNSELECT )
2b5f62a0 1170 DoUnselect(item);
1e6feb95
VZ
1171 else if ( action == wxACTION_LISTBOX_MOVEDOWN )
1172 ChangeCurrent(1);
1173 else if ( action == wxACTION_LISTBOX_MOVEUP )
1174 ChangeCurrent(-1);
1175 else if ( action == wxACTION_LISTBOX_PAGEDOWN )
1176 ChangeCurrent(GetItemsPerPage());
1177 else if ( action == wxACTION_LISTBOX_PAGEUP )
1178 ChangeCurrent(-GetItemsPerPage());
1179 else if ( action == wxACTION_LISTBOX_START )
1180 SetCurrentItem(0);
1181 else if ( action == wxACTION_LISTBOX_END )
1182 SetCurrentItem(GetCount() - 1);
1183 else if ( action == wxACTION_LISTBOX_UNSELECTALL )
1184 DeselectAll(item);
1185 else if ( action == wxACTION_LISTBOX_EXTENDSEL )
1186 ExtendSelection(item);
1187 else if ( action == wxACTION_LISTBOX_FIND )
1188 FindNextItem(strArg);
1189 else if ( action == wxACTION_LISTBOX_ANCHOR )
1190 AnchorSelection(item == -1 ? m_current : item);
1191 else if ( action == wxACTION_LISTBOX_SELECTALL ||
1192 action == wxACTION_LISTBOX_SELTOGGLE )
9a83f860 1193 wxFAIL_MSG(wxT("unimplemented yet"));
1e6feb95
VZ
1194 else
1195 return wxControl::PerformAction(action, numArg, strArg);
1196
a290fa5a 1197 return true;
1e6feb95
VZ
1198}
1199
9467bdb7
VZ
1200/* static */
1201wxInputHandler *wxListBox::GetStdInputHandler(wxInputHandler *handlerDef)
1202{
1203 static wxStdListboxInputHandler s_handler(handlerDef);
1204
1205 return &s_handler;
1206}
1207
1e6feb95
VZ
1208// ============================================================================
1209// implementation of wxStdListboxInputHandler
1210// ============================================================================
1211
1212wxStdListboxInputHandler::wxStdListboxInputHandler(wxInputHandler *handler,
1213 bool toggleOnPressAlways)
1214 : wxStdInputHandler(handler)
1215{
1216 m_btnCapture = 0;
1217 m_toggleOnPressAlways = toggleOnPressAlways;
1218 m_actionMouse = wxACTION_NONE;
a290fa5a 1219 m_trackMouseOutside = true;
1e6feb95
VZ
1220}
1221
1222int wxStdListboxInputHandler::HitTest(const wxListBox *lbox,
1223 const wxMouseEvent& event)
1224{
1225 int item = HitTestUnsafe(lbox, event);
1226
1227 return FixItemIndex(lbox, item);
1228}
1229
1230int wxStdListboxInputHandler::HitTestUnsafe(const wxListBox *lbox,
1231 const wxMouseEvent& event)
1232{
1233 wxPoint pt = event.GetPosition();
1234 pt -= lbox->GetClientAreaOrigin();
1235 int y;
1236 lbox->CalcUnscrolledPosition(0, pt.y, NULL, &y);
1237 return y / lbox->GetLineHeight();
1238}
1239
1240int wxStdListboxInputHandler::FixItemIndex(const wxListBox *lbox,
1241 int item)
1242{
1243 if ( item < 0 )
1244 {
1245 // mouse is above the first item
1246 item = 0;
1247 }
aa61d352 1248 else if ( (unsigned int)item >= lbox->GetCount() )
1e6feb95
VZ
1249 {
1250 // mouse is below the last item
1251 item = lbox->GetCount() - 1;
1252 }
1253
1254 return item;
1255}
1256
1257bool wxStdListboxInputHandler::IsValidIndex(const wxListBox *lbox, int item)
1258{
aa61d352 1259 return item >= 0 && (unsigned int)item < lbox->GetCount();
1e6feb95
VZ
1260}
1261
1262wxControlAction
1263wxStdListboxInputHandler::SetupCapture(wxListBox *lbox,
1264 const wxMouseEvent& event,
1265 int item)
1266{
1267 // we currently only allow selecting with the left mouse button, if we
1268 // do need to allow using other buttons too we might use the code
1269 // inside #if 0
1270#if 0
1271 m_btnCapture = event.LeftDown()
1272 ? 1
1273 : event.RightDown()
1274 ? 3
1275 : 2;
1276#else
1277 m_btnCapture = 1;
1278#endif // 0/1
1279
1280 wxControlAction action;
1281 if ( lbox->HasMultipleSelection() )
1282 {
1283 if ( lbox->GetWindowStyle() & wxLB_MULTIPLE )
1284 {
1285 if ( m_toggleOnPressAlways )
1286 {
1287 // toggle the item right now
1288 action = wxACTION_LISTBOX_TOGGLE;
1289 }
1290 //else: later
1291
1292 m_actionMouse = wxACTION_LISTBOX_SETFOCUS;
1293 }
1294 else // wxLB_EXTENDED listbox
1295 {
1296 // simple click in an extended sel listbox clears the old
1297 // selection and adds the clicked item to it then, ctrl-click
1298 // toggles an item to it and shift-click adds a range between
1299 // the old selection anchor and the clicked item
1300 if ( event.ControlDown() )
1301 {
1302 lbox->PerformAction(wxACTION_LISTBOX_ANCHOR, item);
1303
1304 action = wxACTION_LISTBOX_TOGGLE;
1305 }
1306 else if ( event.ShiftDown() )
1307 {
1308 action = wxACTION_LISTBOX_EXTENDSEL;
1309 }
1310 else // simple click
1311 {
1312 lbox->PerformAction(wxACTION_LISTBOX_ANCHOR, item);
1313
1314 action = wxACTION_LISTBOX_SELECT;
1315 }
1316
1317 m_actionMouse = wxACTION_LISTBOX_EXTENDSEL;
1318 }
1319 }
1320 else // single selection
1321 {
1322 m_actionMouse =
1323 action = wxACTION_LISTBOX_SELECT;
1324 }
1325
1326 // by default we always do track it
a290fa5a 1327 m_trackMouseOutside = true;
1e6feb95
VZ
1328
1329 return action;
1330}
1331
23645bfa 1332bool wxStdListboxInputHandler::HandleKey(wxInputConsumer *consumer,
1e6feb95
VZ
1333 const wxKeyEvent& event,
1334 bool pressed)
1335{
1336 // we're only interested in the key press events
1337 if ( pressed && !event.AltDown() )
1338 {
a290fa5a 1339 bool isMoveCmd = true;
23645bfa 1340 int style = consumer->GetInputWindow()->GetWindowStyle();
1e6feb95
VZ
1341
1342 wxControlAction action;
1343 wxString strArg;
1344
1345 int keycode = event.GetKeyCode();
1346 switch ( keycode )
1347 {
1348 // movement
55f095d4
VZ
1349 case WXK_UP:
1350 action = wxACTION_LISTBOX_MOVEUP;
1351 break;
1352
1353 case WXK_DOWN:
1354 action = wxACTION_LISTBOX_MOVEDOWN;
1355 break;
1356
187c183c 1357 case WXK_PAGEUP:
55f095d4
VZ
1358 action = wxACTION_LISTBOX_PAGEUP;
1359 break;
1360
187c183c 1361 case WXK_PAGEDOWN:
55f095d4
VZ
1362 action = wxACTION_LISTBOX_PAGEDOWN;
1363 break;
1364
1365 case WXK_HOME:
1366 action = wxACTION_LISTBOX_START;
1367 break;
1368
1369 case WXK_END:
1370 action = wxACTION_LISTBOX_END;
1371 break;
1e6feb95
VZ
1372
1373 // selection
1374 case WXK_SPACE:
1375 if ( style & wxLB_MULTIPLE )
1376 {
1377 action = wxACTION_LISTBOX_TOGGLE;
a290fa5a 1378 isMoveCmd = false;
1e6feb95
VZ
1379 }
1380 break;
1381
1382 case WXK_RETURN:
1383 action = wxACTION_LISTBOX_ACTIVATE;
a290fa5a 1384 isMoveCmd = false;
1e6feb95
VZ
1385 break;
1386
1387 default:
32b13913 1388 if ( (keycode < 255) && wxIsalnum((wxChar)keycode) )
1e6feb95
VZ
1389 {
1390 action = wxACTION_LISTBOX_FIND;
1391 strArg = (wxChar)keycode;
1392 }
1393 }
1394
a290fa5a 1395 if ( !action.IsEmpty() )
1e6feb95 1396 {
23645bfa 1397 consumer->PerformAction(action, -1, strArg);
1e6feb95
VZ
1398
1399 if ( isMoveCmd )
1400 {
1401 if ( style & wxLB_SINGLE )
1402 {
1403 // the current item is always the one selected
23645bfa 1404 consumer->PerformAction(wxACTION_LISTBOX_SELECT);
1e6feb95
VZ
1405 }
1406 else if ( style & wxLB_EXTENDED )
1407 {
1408 if ( event.ShiftDown() )
23645bfa 1409 consumer->PerformAction(wxACTION_LISTBOX_EXTENDSEL);
1e6feb95
VZ
1410 else
1411 {
1412 // select the item and make it the new selection anchor
23645bfa
VS
1413 consumer->PerformAction(wxACTION_LISTBOX_SELECT);
1414 consumer->PerformAction(wxACTION_LISTBOX_ANCHOR);
1e6feb95
VZ
1415 }
1416 }
1417 //else: nothing to do for multiple selection listboxes
1418 }
1419
a290fa5a 1420 return true;
1e6feb95
VZ
1421 }
1422 }
1423
23645bfa 1424 return wxStdInputHandler::HandleKey(consumer, event, pressed);
1e6feb95
VZ
1425}
1426
23645bfa 1427bool wxStdListboxInputHandler::HandleMouse(wxInputConsumer *consumer,
1e6feb95
VZ
1428 const wxMouseEvent& event)
1429{
23645bfa 1430 wxListBox *lbox = wxStaticCast(consumer->GetInputWindow(), wxListBox);
1e6feb95
VZ
1431 int item = HitTest(lbox, event);
1432 wxControlAction action;
1433
1434 // when the left mouse button is pressed, capture the mouse and track the
1435 // item under mouse (if the mouse leaves the window, we will still be
1436 // getting the mouse move messages generated by wxScrollWindow)
1437 if ( event.LeftDown() )
1438 {
1439 // capture the mouse to track the selected item
1440 lbox->CaptureMouse();
1441
1442 action = SetupCapture(lbox, event, item);
1443 }
1444 else if ( m_btnCapture && event.ButtonUp(m_btnCapture) )
1445 {
1446 // when the left mouse button is released, release the mouse too
1447 wxWindow *winCapture = wxWindow::GetCapture();
1448 if ( winCapture )
1449 {
1450 winCapture->ReleaseMouse();
1451 m_btnCapture = 0;
1452
1e6feb95
VZ
1453 action = m_actionMouse;
1454 }
1455 //else: the mouse wasn't presed over the listbox, only released here
1456 }
1457 else if ( event.LeftDClick() )
1458 {
1459 action = wxACTION_LISTBOX_ACTIVATE;
1460 }
1461
a290fa5a 1462 if ( !action.IsEmpty() )
1e6feb95
VZ
1463 {
1464 lbox->PerformAction(action, item);
1465
a290fa5a 1466 return true;
1e6feb95
VZ
1467 }
1468
23645bfa 1469 return wxStdInputHandler::HandleMouse(consumer, event);
1e6feb95
VZ
1470}
1471
23645bfa 1472bool wxStdListboxInputHandler::HandleMouseMove(wxInputConsumer *consumer,
1e6feb95
VZ
1473 const wxMouseEvent& event)
1474{
1475 wxWindow *winCapture = wxWindow::GetCapture();
1476 if ( winCapture && (event.GetEventObject() == winCapture) )
1477 {
23645bfa 1478 wxListBox *lbox = wxStaticCast(consumer->GetInputWindow(), wxListBox);
1e6feb95
VZ
1479
1480 if ( !m_btnCapture || !m_trackMouseOutside )
1481 {
1482 // someone captured the mouse for us (we always set m_btnCapture
1483 // when we do it ourselves): in this case we only react to
1484 // the mouse messages when they happen inside the listbox
1485 if ( lbox->HitTest(event.GetPosition()) != wxHT_WINDOW_INSIDE )
a290fa5a 1486 return false;
1e6feb95
VZ
1487 }
1488
1489 int item = HitTest(lbox, event);
1490 if ( !m_btnCapture )
1491 {
1492 // now that we have the mouse inside the listbox, do capture it
1493 // normally - but ensure that we will still ignore the outside
1494 // events
1495 SetupCapture(lbox, event, item);
1496
a290fa5a 1497 m_trackMouseOutside = false;
1e6feb95
VZ
1498 }
1499
1500 if ( IsValidIndex(lbox, item) )
1501 {
55f095d4
VZ
1502 // pass something into strArg to tell the listbox that it shouldn't
1503 // send the notification message: see PerformAction() above
9a83f860 1504 lbox->PerformAction(m_actionMouse, item, wxT("no"));
1e6feb95
VZ
1505 }
1506 // else: don't pass invalid index to the listbox
1507 }
1508 else // we don't have capture any more
1509 {
1510 if ( m_btnCapture )
1511 {
1512 // if we lost capture unexpectedly (someone else took the capture
1513 // from us), return to a consistent state
1514 m_btnCapture = 0;
1515 }
1516 }
1517
23645bfa 1518 return wxStdInputHandler::HandleMouseMove(consumer, event);
1e6feb95
VZ
1519}
1520
1521#endif // wxUSE_LISTBOX