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