]> git.saurik.com Git - wxWidgets.git/blame - src/univ/listbox.cpp
rebaked after listctrl/imagelist and wxUniv changes
[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
199 SetBestSize(size);
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 {
479 if ( m_selections.Index(n) == wxNOT_FOUND )
480 {
481 if ( !HasMultipleSelection() )
482 {
483 // selecting an item in a single selection listbox deselects
484 // all the others
485 DeselectAll();
486 }
487
488 m_selections.Add(n);
489
490 RefreshItem(n);
491 }
492 //else: already selected
493 }
494 else // unselect
495 {
496 int index = m_selections.Index(n);
497 if ( index != wxNOT_FOUND )
498 {
499 m_selections.RemoveAt(index);
500
501 RefreshItem(n);
502 }
503 //else: not selected
504 }
505
506 // sanity check: a single selection listbox can't have more than one item
507 // selected
508 wxASSERT_MSG( HasMultipleSelection() || (m_selections.GetCount() < 2),
509 _T("multiple selected items in single selection lbox?") );
510
511 if ( select )
512 {
513 // the newly selected item becomes the current one
514 SetCurrentItem(n);
515 }
516}
517
518int wxListBox::GetSelection() const
519{
e95c145c 520 wxCHECK_MSG( !HasMultipleSelection(), wxNOT_FOUND,
1e6feb95
VZ
521 _T("use wxListBox::GetSelections for ths listbox") );
522
e95c145c 523 return m_selections.IsEmpty() ? wxNOT_FOUND : m_selections[0];
1e6feb95
VZ
524}
525
526int wxCMPFUNC_CONV wxCompareInts(int *n, int *m)
527{
528 return *n - *m;
529}
530
531int wxListBox::GetSelections(wxArrayInt& selections) const
532{
533 // always return sorted array to the user
534 selections = m_selections;
aa61d352 535 unsigned int count = m_selections.GetCount();
1e6feb95
VZ
536
537 // don't call sort on an empty array
538 if ( count )
539 {
540 selections.Sort(wxCompareInts);
541 }
542
543 return count;
544}
545
546// ----------------------------------------------------------------------------
547// refresh logic: we use delayed refreshing which allows to avoid multiple
548// refreshes (and hence flicker) in case when several listbox items are
549// added/deleted/changed subsequently
550// ----------------------------------------------------------------------------
551
552void wxListBox::RefreshFromItemToEnd(int from)
553{
554 RefreshItems(from, GetCount() - from);
555}
556
557void wxListBox::RefreshItems(int from, int count)
558{
559 switch ( m_updateCount )
560 {
561 case 0:
562 m_updateFrom = from;
563 m_updateCount = count;
564 break;
565
566 case -1:
567 // we refresh everything anyhow
568 break;
569
570 default:
571 // add these items to the others which we have to refresh
572 if ( m_updateFrom < from )
573 {
574 count += from - m_updateFrom;
575 if ( m_updateCount < count )
576 m_updateCount = count;
577 }
578 else // m_updateFrom >= from
579 {
580 int updateLast = wxMax(m_updateFrom + m_updateCount,
581 from + count);
582 m_updateFrom = from;
583 m_updateCount = updateLast - m_updateFrom;
584 }
585 }
586}
587
588void wxListBox::RefreshItem(int n)
589{
590 switch ( m_updateCount )
591 {
592 case 0:
593 // refresh this item only
594 m_updateFrom = n;
595 m_updateCount = 1;
596 break;
597
598 case -1:
599 // we refresh everything anyhow
600 break;
601
602 default:
603 // add this item to the others which we have to refresh
604 if ( m_updateFrom < n )
605 {
606 if ( m_updateCount < n - m_updateFrom + 1 )
607 m_updateCount = n - m_updateFrom + 1;
608 }
609 else // n <= m_updateFrom
610 {
611 m_updateCount += m_updateFrom - n;
612 m_updateFrom = n;
613 }
614 }
615}
616
617void wxListBox::RefreshAll()
618{
619 m_updateCount = -1;
620}
621
622void wxListBox::RefreshHorzScrollbar()
623{
624 m_maxWidth = 0; // recalculate it
a290fa5a 625 m_updateScrollbarX = true;
1e6feb95
VZ
626}
627
628void wxListBox::UpdateScrollbars()
629{
630 wxSize size = GetClientSize();
631
632 // is our height enough to show all items?
aa61d352 633 unsigned int nLines = GetCount();
1e6feb95 634 wxCoord lineHeight = GetLineHeight();
e95c145c 635 bool showScrollbarY = (int)nLines*lineHeight > size.y;
1e6feb95
VZ
636
637 // check the width too if required
638 wxCoord charWidth, maxWidth;
639 bool showScrollbarX;
640 if ( HasHorzScrollbar() )
641 {
642 charWidth = GetCharWidth();
643 maxWidth = GetMaxWidth();
644 showScrollbarX = maxWidth > size.x;
645 }
646 else // never show it
647 {
648 charWidth = maxWidth = 0;
a290fa5a 649 showScrollbarX = false;
1e6feb95
VZ
650 }
651
652 // what should be the scrollbar range now?
653 int scrollRangeX = showScrollbarX
654 ? (maxWidth + charWidth - 1) / charWidth + 2 // FIXME
655 : 0;
656 int scrollRangeY = showScrollbarY
657 ? nLines +
658 (size.y % lineHeight + lineHeight - 1) / lineHeight
659 : 0;
660
661 // reset scrollbars if something changed: either the visibility status
662 // or the range of a scrollbar which is shown
663 if ( (showScrollbarY != m_showScrollbarY) ||
664 (showScrollbarX != m_showScrollbarX) ||
665 (showScrollbarY && (scrollRangeY != m_scrollRangeY)) ||
666 (showScrollbarX && (scrollRangeX != m_scrollRangeX)) )
667 {
668 int x, y;
669 GetViewStart(&x, &y);
670 SetScrollbars(charWidth, lineHeight,
671 scrollRangeX, scrollRangeY,
672 x, y);
673
674 m_showScrollbarX = showScrollbarX;
675 m_showScrollbarY = showScrollbarY;
676
677 m_scrollRangeX = scrollRangeX;
678 m_scrollRangeY = scrollRangeY;
679 }
680}
681
682void wxListBox::UpdateItems()
683{
684 // only refresh the items which must be refreshed
685 if ( m_updateCount == -1 )
686 {
687 // refresh all
688 wxLogTrace(_T("listbox"), _T("Refreshing all"));
689
690 Refresh();
691 }
692 else
693 {
694 wxSize size = GetClientSize();
695 wxRect rect;
696 rect.width = size.x;
697 rect.height = size.y;
698 rect.y += m_updateFrom*GetLineHeight();
699 rect.height = m_updateCount*GetLineHeight();
700
701 // we don't need to calculate x position as we always refresh the
702 // entire line(s)
703 CalcScrolledPosition(0, rect.y, NULL, &rect.y);
704
705 wxLogTrace(_T("listbox"), _T("Refreshing items %d..%d (%d-%d)"),
706 m_updateFrom, m_updateFrom + m_updateCount - 1,
707 rect.GetTop(), rect.GetBottom());
708
a290fa5a 709 Refresh(true, &rect);
1e6feb95
VZ
710 }
711}
712
e39af974 713void wxListBox::OnInternalIdle()
1e6feb95
VZ
714{
715 if ( m_updateScrollbarY || m_updateScrollbarX )
716 {
717 UpdateScrollbars();
718
719 m_updateScrollbarX =
a290fa5a 720 m_updateScrollbarY = false;
1e6feb95
VZ
721 }
722
723 if ( m_currentChanged )
724 {
725 DoEnsureVisible(m_current);
726
a290fa5a 727 m_currentChanged = false;
1e6feb95
VZ
728 }
729
730 if ( m_updateCount )
731 {
732 UpdateItems();
733
734 m_updateCount = 0;
735 }
6f4767ac 736 wxListBoxBase::OnInternalIdle();
1e6feb95
VZ
737}
738
739// ----------------------------------------------------------------------------
740// drawing
741// ----------------------------------------------------------------------------
742
743wxBorder wxListBox::GetDefaultBorder() const
744{
745 return wxBORDER_SUNKEN;
746}
747
748void wxListBox::DoDraw(wxControlRenderer *renderer)
749{
750 // adjust the DC to account for scrolling
751 wxDC& dc = renderer->GetDC();
752 PrepareDC(dc);
753 dc.SetFont(GetFont());
754
755 // get the update rect
756 wxRect rectUpdate = GetUpdateClientRect();
757
758 int yTop, yBottom;
759 CalcUnscrolledPosition(0, rectUpdate.GetTop(), NULL, &yTop);
760 CalcUnscrolledPosition(0, rectUpdate.GetBottom(), NULL, &yBottom);
761
762 // get the items which must be redrawn
763 wxCoord lineHeight = GetLineHeight();
aa61d352
VZ
764 unsigned int itemFirst = yTop / lineHeight,
765 itemLast = (yBottom + lineHeight - 1) / lineHeight,
766 itemMax = m_strings->GetCount();
1e6feb95
VZ
767
768 if ( itemFirst >= itemMax )
769 return;
770
771 if ( itemLast > itemMax )
772 itemLast = itemMax;
773
774 // do draw them
775 wxLogTrace(_T("listbox"), _T("Repainting items %d..%d"),
776 itemFirst, itemLast);
777
778 DoDrawRange(renderer, itemFirst, itemLast);
779}
780
781void wxListBox::DoDrawRange(wxControlRenderer *renderer,
782 int itemFirst, int itemLast)
783{
784 renderer->DrawItems(this, itemFirst, itemLast);
785}
786
787// ----------------------------------------------------------------------------
788// size calculations
789// ----------------------------------------------------------------------------
790
791bool wxListBox::SetFont(const wxFont& font)
792{
793 if ( !wxControl::SetFont(font) )
a290fa5a 794 return false;
1e6feb95
VZ
795
796 CalcItemsPerPage();
797
798 RefreshAll();
799
a290fa5a 800 return true;
1e6feb95
VZ
801}
802
803void wxListBox::CalcItemsPerPage()
804{
805 m_lineHeight = GetRenderer()->GetListboxItemHeight(GetCharHeight());
806 m_itemsPerPage = GetClientSize().y / m_lineHeight;
807}
808
809int wxListBox::GetItemsPerPage() const
810{
811 if ( !m_itemsPerPage )
812 {
813 wxConstCast(this, wxListBox)->CalcItemsPerPage();
814 }
815
816 return m_itemsPerPage;
817}
818
819wxCoord wxListBox::GetLineHeight() const
820{
821 if ( !m_lineHeight )
822 {
823 wxConstCast(this, wxListBox)->CalcItemsPerPage();
824 }
825
826 return m_lineHeight;
827}
828
829wxCoord wxListBox::GetMaxWidth() const
830{
831 if ( m_maxWidth == 0 )
832 {
833 wxListBox *self = wxConstCast(this, wxListBox);
834 wxCoord width;
aa61d352
VZ
835 unsigned int count = m_strings->GetCount();
836 for ( unsigned int n = 0; n < count; n++ )
1e6feb95 837 {
e1d6e01c 838 GetTextExtent(this->GetString(n), &width, NULL);
1e6feb95
VZ
839 if ( width > m_maxWidth )
840 {
841 self->m_maxWidth = width;
842 self->m_maxWidthItem = n;
843 }
844 }
845 }
846
847 return m_maxWidth;
848}
849
850void wxListBox::OnSize(wxSizeEvent& event)
851{
852 // recalculate the number of items per page
853 CalcItemsPerPage();
854
855 // the scrollbars might [dis]appear
856 m_updateScrollbarX =
a290fa5a 857 m_updateScrollbarY = true;
1e6feb95
VZ
858
859 event.Skip();
860}
861
862void wxListBox::DoSetFirstItem(int n)
863{
864 SetCurrentItem(n);
865}
866
867void wxListBox::DoSetSize(int x, int y,
868 int width, int height,
869 int sizeFlags)
870{
871 if ( GetWindowStyle() & wxLB_INT_HEIGHT )
872 {
873 // we must round up the height to an entire number of rows
874
875 // the client area must contain an int number of rows, so take borders
876 // into account
877 wxRect rectBorders = GetRenderer()->GetBorderDimensions(GetBorder());
878 wxCoord hBorders = rectBorders.y + rectBorders.height;
879
880 wxCoord hLine = GetLineHeight();
881 height = ((height - hBorders + hLine - 1) / hLine)*hLine + hBorders;
882 }
883
61fef19b 884 wxListBoxBase::DoSetSize(x, y, width, height, sizeFlags);
1e6feb95
VZ
885}
886
887wxSize wxListBox::DoGetBestClientSize() const
888{
889 wxCoord width = 0,
890 height = 0;
891
aa61d352
VZ
892 unsigned int count = m_strings->GetCount();
893 for ( unsigned int n = 0; n < count; n++ )
1e6feb95
VZ
894 {
895 wxCoord w,h;
e1d6e01c 896 GetTextExtent(this->GetString(n), &w, &h);
1e6feb95
VZ
897
898 if ( w > width )
899 width = w;
900 if ( h > height )
901 height = h;
902 }
903
904 // if the listbox is empty, still give it some non zero (even if
905 // arbitrary) size - otherwise, leave small margin around the strings
906 if ( !width )
907 width = 100;
908 else
909 width += 3*GetCharWidth();
910
911 if ( !height )
912 height = GetCharHeight();
913
914 // we need the height of the entire listbox, not just of one line
915 height *= wxMax(count, 7);
916
917 return wxSize(width, height);
918}
919
920// ----------------------------------------------------------------------------
921// listbox actions
922// ----------------------------------------------------------------------------
923
924bool wxListBox::SendEvent(wxEventType type, int item)
925{
1e6feb95
VZ
926 wxCommandEvent event(type, m_windowId);
927 event.SetEventObject(this);
928
929 // use the current item by default
930 if ( item == -1 )
931 {
932 item = m_current;
933 }
934
935 // client data and string parameters only make sense if we have an item
936 if ( item != -1 )
937 {
938 if ( HasClientObjectData() )
939 event.SetClientObject(GetClientObject(item));
940 else if ( HasClientUntypedData() )
941 event.SetClientData(GetClientData(item));
942
943 event.SetString(GetString(item));
944 }
945
687706f5 946 event.SetInt(item);
1e6feb95
VZ
947
948 return GetEventHandler()->ProcessEvent(event);
949}
950
951void wxListBox::SetCurrentItem(int n)
952{
953 if ( n != m_current )
954 {
955 if ( m_current != -1 )
956 RefreshItem(m_current);
957
958 m_current = n;
959
960 if ( m_current != -1 )
961 {
a290fa5a 962 m_currentChanged = true;
1e6feb95
VZ
963
964 RefreshItem(m_current);
965 }
966 }
967 //else: nothing to do
968}
969
970bool wxListBox::FindItem(const wxString& prefix, bool strictlyAfter)
971{
aa61d352 972 unsigned int count = GetCount();
8228b893 973 if ( count==0 )
1e6feb95
VZ
974 {
975 // empty listbox, we can't find anything in it
a290fa5a 976 return false;
1e6feb95
VZ
977 }
978
979 // start either from the current item or from the next one if strictlyAfter
980 // is true
981 int first;
982 if ( strictlyAfter )
983 {
984 // the following line will set first correctly to 0 if there is no
985 // selection (m_current == -1)
e95c145c 986 first = m_current == (int)(count - 1) ? 0 : m_current + 1;
1e6feb95
VZ
987 }
988 else // start with the current
989 {
990 first = m_current == -1 ? 0 : m_current;
991 }
992
993 int last = first == 0 ? count - 1 : first - 1;
994
995 // if this is not true we'd never exit from the loop below!
8228b893 996 wxASSERT_MSG( first < (int)count && last < (int)count, _T("logic error") );
1e6feb95
VZ
997
998 // precompute it outside the loop
999 size_t len = prefix.length();
1000
1001 // loop over all items in the listbox
e95c145c 1002 for ( int item = first; item != (int)last; item < (int)(count - 1) ? item++ : item = 0 )
1e6feb95 1003 {
e1d6e01c 1004 if ( wxStrnicmp(this->GetString(item).c_str(), prefix, len) == 0 )
1e6feb95
VZ
1005 {
1006 SetCurrentItem(item);
1007
1008 if ( !(GetWindowStyle() & wxLB_MULTIPLE) )
1009 {
1010 DeselectAll(item);
1011 SelectAndNotify(item);
1012
1013 if ( GetWindowStyle() & wxLB_EXTENDED )
1014 AnchorSelection(item);
1015 }
1016
a290fa5a 1017 return true;
1e6feb95
VZ
1018 }
1019 }
1020
1021 // nothing found
a290fa5a 1022 return false;
1e6feb95
VZ
1023}
1024
1025void wxListBox::EnsureVisible(int n)
1026{
1027 if ( m_updateScrollbarY )
1028 {
1029 UpdateScrollbars();
1030
1031 m_updateScrollbarX =
a290fa5a 1032 m_updateScrollbarY = false;
1e6feb95
VZ
1033 }
1034
1035 DoEnsureVisible(n);
1036}
1037
1038void wxListBox::DoEnsureVisible(int n)
1039{
1040 if ( !m_showScrollbarY )
1041 {
1042 // nothing to do - everything is shown anyhow
1043 return;
1044 }
1045
1046 int first;
1047 GetViewStart(0, &first);
1048 if ( first > n )
1049 {
1050 // we need to scroll upwards, so make the current item appear on top
1051 // of the shown range
1052 Scroll(0, n);
1053 }
1054 else
1055 {
1056 int last = first + GetClientSize().y / GetLineHeight() - 1;
1057 if ( last < n )
1058 {
1059 // scroll down: the current item appears at the bottom of the
1060 // range
1061 Scroll(0, n - (last - first));
1062 }
1063 }
1064}
1065
1066void wxListBox::ChangeCurrent(int diff)
1067{
1068 int current = m_current == -1 ? 0 : m_current;
1069
1070 current += diff;
1071
1072 int last = GetCount() - 1;
1073 if ( current < 0 )
1074 current = 0;
1075 else if ( current > last )
1076 current = last;
1077
1078 SetCurrentItem(current);
1079}
1080
1081void wxListBox::ExtendSelection(int itemTo)
1082{
1083 // if we don't have the explicit values for selection start/end, make them
1084 // up
1085 if ( m_selAnchor == -1 )
1086 m_selAnchor = m_current;
1087
1088 if ( itemTo == -1 )
1089 itemTo = m_current;
1090
1091 // swap the start/end of selection range if necessary
1092 int itemFrom = m_selAnchor;
1093 if ( itemFrom > itemTo )
1094 {
1095 int itemTmp = itemFrom;
1096 itemFrom = itemTo;
1097 itemTo = itemTmp;
1098 }
1099
1100 // the selection should now include all items in the range between the
1101 // anchor and the specified item and only them
1102
1103 int n;
1104 for ( n = 0; n < itemFrom; n++ )
1105 {
1106 Deselect(n);
1107 }
1108
1109 for ( ; n <= itemTo; n++ )
1110 {
1111 SetSelection(n);
1112 }
1113
aa61d352 1114 unsigned int count = GetCount();
8228b893 1115 for ( ; n < (int)count; n++ )
1e6feb95
VZ
1116 {
1117 Deselect(n);
1118 }
1119}
1120
2b5f62a0 1121void wxListBox::DoSelect(int item, bool sel)
1e6feb95
VZ
1122{
1123 if ( item != -1 )
1124 {
1125 // go to this item first
1126 SetCurrentItem(item);
1127 }
1128
1129 // the current item is the one we want to change: either it was just
1130 // changed above to be the same as item or item == -1 in which we case we
1131 // are supposed to use the current one anyhow
1132 if ( m_current != -1 )
1133 {
1134 // [de]select it
1135 SetSelection(m_current, sel);
1136 }
1137}
1138
1139void wxListBox::SelectAndNotify(int item)
1140{
2b5f62a0 1141 DoSelect(item);
1e6feb95
VZ
1142
1143 SendEvent(wxEVT_COMMAND_LISTBOX_SELECTED);
1144}
1145
1146void wxListBox::Activate(int item)
1147{
1148 if ( item != -1 )
1149 SetCurrentItem(item);
1150 else
1151 item = m_current;
1152
1153 if ( !(GetWindowStyle() & wxLB_MULTIPLE) )
1154 {
1155 DeselectAll(item);
1156 }
1157
1158 if ( item != -1 )
1159 {
2b5f62a0 1160 DoSelect(item);
1e6feb95
VZ
1161
1162 SendEvent(wxEVT_COMMAND_LISTBOX_DOUBLECLICKED);
1163 }
1164}
1165
1166// ----------------------------------------------------------------------------
1167// input handling
1168// ----------------------------------------------------------------------------
1169
55f095d4
VZ
1170/*
1171 The numArg here is the listbox item index while the strArg is used
1172 differently for the different actions:
1173
1174 a) for wxACTION_LISTBOX_FIND it has the natural meaning: this is the string
1175 to find
1176
1177 b) for wxACTION_LISTBOX_SELECT and wxACTION_LISTBOX_EXTENDSEL it is used
1178 to decide if the listbox should send the notification event (it is empty)
1179 or not (it is not): this allows us to reuse the same action for when the
1180 user is dragging the mouse when it has been released although in the
1181 first case no notification is sent while in the second it is sent.
1182 */
1e6feb95
VZ
1183bool wxListBox::PerformAction(const wxControlAction& action,
1184 long numArg,
1185 const wxString& strArg)
1186{
1187 int item = (int)numArg;
1188
1189 if ( action == wxACTION_LISTBOX_SETFOCUS )
ae1daed0 1190 {
1e6feb95 1191 SetCurrentItem(item);
ae1daed0 1192 }
1e6feb95 1193 else if ( action == wxACTION_LISTBOX_ACTIVATE )
ae1daed0 1194 {
1e6feb95 1195 Activate(item);
ae1daed0 1196 }
1e6feb95
VZ
1197 else if ( action == wxACTION_LISTBOX_TOGGLE )
1198 {
1199 if ( item == -1 )
1200 item = m_current;
1201
1202 if ( IsSelected(item) )
2b5f62a0 1203 DoUnselect(item);
1e6feb95
VZ
1204 else
1205 SelectAndNotify(item);
1206 }
1207 else if ( action == wxACTION_LISTBOX_SELECT )
1208 {
1209 DeselectAll(item);
55f095d4
VZ
1210
1211 if ( strArg.empty() )
1212 SelectAndNotify(item);
1213 else
2b5f62a0 1214 DoSelect(item);
1e6feb95
VZ
1215 }
1216 else if ( action == wxACTION_LISTBOX_SELECTADD )
2b5f62a0 1217 DoSelect(item);
1e6feb95 1218 else if ( action == wxACTION_LISTBOX_UNSELECT )
2b5f62a0 1219 DoUnselect(item);
1e6feb95
VZ
1220 else if ( action == wxACTION_LISTBOX_MOVEDOWN )
1221 ChangeCurrent(1);
1222 else if ( action == wxACTION_LISTBOX_MOVEUP )
1223 ChangeCurrent(-1);
1224 else if ( action == wxACTION_LISTBOX_PAGEDOWN )
1225 ChangeCurrent(GetItemsPerPage());
1226 else if ( action == wxACTION_LISTBOX_PAGEUP )
1227 ChangeCurrent(-GetItemsPerPage());
1228 else if ( action == wxACTION_LISTBOX_START )
1229 SetCurrentItem(0);
1230 else if ( action == wxACTION_LISTBOX_END )
1231 SetCurrentItem(GetCount() - 1);
1232 else if ( action == wxACTION_LISTBOX_UNSELECTALL )
1233 DeselectAll(item);
1234 else if ( action == wxACTION_LISTBOX_EXTENDSEL )
1235 ExtendSelection(item);
1236 else if ( action == wxACTION_LISTBOX_FIND )
1237 FindNextItem(strArg);
1238 else if ( action == wxACTION_LISTBOX_ANCHOR )
1239 AnchorSelection(item == -1 ? m_current : item);
1240 else if ( action == wxACTION_LISTBOX_SELECTALL ||
1241 action == wxACTION_LISTBOX_SELTOGGLE )
1242 wxFAIL_MSG(_T("unimplemented yet"));
1243 else
1244 return wxControl::PerformAction(action, numArg, strArg);
1245
a290fa5a 1246 return true;
1e6feb95
VZ
1247}
1248
9467bdb7
VZ
1249/* static */
1250wxInputHandler *wxListBox::GetStdInputHandler(wxInputHandler *handlerDef)
1251{
1252 static wxStdListboxInputHandler s_handler(handlerDef);
1253
1254 return &s_handler;
1255}
1256
1e6feb95
VZ
1257// ============================================================================
1258// implementation of wxStdListboxInputHandler
1259// ============================================================================
1260
1261wxStdListboxInputHandler::wxStdListboxInputHandler(wxInputHandler *handler,
1262 bool toggleOnPressAlways)
1263 : wxStdInputHandler(handler)
1264{
1265 m_btnCapture = 0;
1266 m_toggleOnPressAlways = toggleOnPressAlways;
1267 m_actionMouse = wxACTION_NONE;
a290fa5a 1268 m_trackMouseOutside = true;
1e6feb95
VZ
1269}
1270
1271int wxStdListboxInputHandler::HitTest(const wxListBox *lbox,
1272 const wxMouseEvent& event)
1273{
1274 int item = HitTestUnsafe(lbox, event);
1275
1276 return FixItemIndex(lbox, item);
1277}
1278
1279int wxStdListboxInputHandler::HitTestUnsafe(const wxListBox *lbox,
1280 const wxMouseEvent& event)
1281{
1282 wxPoint pt = event.GetPosition();
1283 pt -= lbox->GetClientAreaOrigin();
1284 int y;
1285 lbox->CalcUnscrolledPosition(0, pt.y, NULL, &y);
1286 return y / lbox->GetLineHeight();
1287}
1288
1289int wxStdListboxInputHandler::FixItemIndex(const wxListBox *lbox,
1290 int item)
1291{
1292 if ( item < 0 )
1293 {
1294 // mouse is above the first item
1295 item = 0;
1296 }
aa61d352 1297 else if ( (unsigned int)item >= lbox->GetCount() )
1e6feb95
VZ
1298 {
1299 // mouse is below the last item
1300 item = lbox->GetCount() - 1;
1301 }
1302
1303 return item;
1304}
1305
1306bool wxStdListboxInputHandler::IsValidIndex(const wxListBox *lbox, int item)
1307{
aa61d352 1308 return item >= 0 && (unsigned int)item < lbox->GetCount();
1e6feb95
VZ
1309}
1310
1311wxControlAction
1312wxStdListboxInputHandler::SetupCapture(wxListBox *lbox,
1313 const wxMouseEvent& event,
1314 int item)
1315{
1316 // we currently only allow selecting with the left mouse button, if we
1317 // do need to allow using other buttons too we might use the code
1318 // inside #if 0
1319#if 0
1320 m_btnCapture = event.LeftDown()
1321 ? 1
1322 : event.RightDown()
1323 ? 3
1324 : 2;
1325#else
1326 m_btnCapture = 1;
1327#endif // 0/1
1328
1329 wxControlAction action;
1330 if ( lbox->HasMultipleSelection() )
1331 {
1332 if ( lbox->GetWindowStyle() & wxLB_MULTIPLE )
1333 {
1334 if ( m_toggleOnPressAlways )
1335 {
1336 // toggle the item right now
1337 action = wxACTION_LISTBOX_TOGGLE;
1338 }
1339 //else: later
1340
1341 m_actionMouse = wxACTION_LISTBOX_SETFOCUS;
1342 }
1343 else // wxLB_EXTENDED listbox
1344 {
1345 // simple click in an extended sel listbox clears the old
1346 // selection and adds the clicked item to it then, ctrl-click
1347 // toggles an item to it and shift-click adds a range between
1348 // the old selection anchor and the clicked item
1349 if ( event.ControlDown() )
1350 {
1351 lbox->PerformAction(wxACTION_LISTBOX_ANCHOR, item);
1352
1353 action = wxACTION_LISTBOX_TOGGLE;
1354 }
1355 else if ( event.ShiftDown() )
1356 {
1357 action = wxACTION_LISTBOX_EXTENDSEL;
1358 }
1359 else // simple click
1360 {
1361 lbox->PerformAction(wxACTION_LISTBOX_ANCHOR, item);
1362
1363 action = wxACTION_LISTBOX_SELECT;
1364 }
1365
1366 m_actionMouse = wxACTION_LISTBOX_EXTENDSEL;
1367 }
1368 }
1369 else // single selection
1370 {
1371 m_actionMouse =
1372 action = wxACTION_LISTBOX_SELECT;
1373 }
1374
1375 // by default we always do track it
a290fa5a 1376 m_trackMouseOutside = true;
1e6feb95
VZ
1377
1378 return action;
1379}
1380
23645bfa 1381bool wxStdListboxInputHandler::HandleKey(wxInputConsumer *consumer,
1e6feb95
VZ
1382 const wxKeyEvent& event,
1383 bool pressed)
1384{
1385 // we're only interested in the key press events
1386 if ( pressed && !event.AltDown() )
1387 {
a290fa5a 1388 bool isMoveCmd = true;
23645bfa 1389 int style = consumer->GetInputWindow()->GetWindowStyle();
1e6feb95
VZ
1390
1391 wxControlAction action;
1392 wxString strArg;
1393
1394 int keycode = event.GetKeyCode();
1395 switch ( keycode )
1396 {
1397 // movement
55f095d4
VZ
1398 case WXK_UP:
1399 action = wxACTION_LISTBOX_MOVEUP;
1400 break;
1401
1402 case WXK_DOWN:
1403 action = wxACTION_LISTBOX_MOVEDOWN;
1404 break;
1405
187c183c 1406 case WXK_PAGEUP:
55f095d4
VZ
1407 action = wxACTION_LISTBOX_PAGEUP;
1408 break;
1409
187c183c 1410 case WXK_PAGEDOWN:
55f095d4
VZ
1411 action = wxACTION_LISTBOX_PAGEDOWN;
1412 break;
1413
1414 case WXK_HOME:
1415 action = wxACTION_LISTBOX_START;
1416 break;
1417
1418 case WXK_END:
1419 action = wxACTION_LISTBOX_END;
1420 break;
1e6feb95
VZ
1421
1422 // selection
1423 case WXK_SPACE:
1424 if ( style & wxLB_MULTIPLE )
1425 {
1426 action = wxACTION_LISTBOX_TOGGLE;
a290fa5a 1427 isMoveCmd = false;
1e6feb95
VZ
1428 }
1429 break;
1430
1431 case WXK_RETURN:
1432 action = wxACTION_LISTBOX_ACTIVATE;
a290fa5a 1433 isMoveCmd = false;
1e6feb95
VZ
1434 break;
1435
1436 default:
32b13913 1437 if ( (keycode < 255) && wxIsalnum((wxChar)keycode) )
1e6feb95
VZ
1438 {
1439 action = wxACTION_LISTBOX_FIND;
1440 strArg = (wxChar)keycode;
1441 }
1442 }
1443
a290fa5a 1444 if ( !action.IsEmpty() )
1e6feb95 1445 {
23645bfa 1446 consumer->PerformAction(action, -1, strArg);
1e6feb95
VZ
1447
1448 if ( isMoveCmd )
1449 {
1450 if ( style & wxLB_SINGLE )
1451 {
1452 // the current item is always the one selected
23645bfa 1453 consumer->PerformAction(wxACTION_LISTBOX_SELECT);
1e6feb95
VZ
1454 }
1455 else if ( style & wxLB_EXTENDED )
1456 {
1457 if ( event.ShiftDown() )
23645bfa 1458 consumer->PerformAction(wxACTION_LISTBOX_EXTENDSEL);
1e6feb95
VZ
1459 else
1460 {
1461 // select the item and make it the new selection anchor
23645bfa
VS
1462 consumer->PerformAction(wxACTION_LISTBOX_SELECT);
1463 consumer->PerformAction(wxACTION_LISTBOX_ANCHOR);
1e6feb95
VZ
1464 }
1465 }
1466 //else: nothing to do for multiple selection listboxes
1467 }
1468
a290fa5a 1469 return true;
1e6feb95
VZ
1470 }
1471 }
1472
23645bfa 1473 return wxStdInputHandler::HandleKey(consumer, event, pressed);
1e6feb95
VZ
1474}
1475
23645bfa 1476bool wxStdListboxInputHandler::HandleMouse(wxInputConsumer *consumer,
1e6feb95
VZ
1477 const wxMouseEvent& event)
1478{
23645bfa 1479 wxListBox *lbox = wxStaticCast(consumer->GetInputWindow(), wxListBox);
1e6feb95
VZ
1480 int item = HitTest(lbox, event);
1481 wxControlAction action;
1482
1483 // when the left mouse button is pressed, capture the mouse and track the
1484 // item under mouse (if the mouse leaves the window, we will still be
1485 // getting the mouse move messages generated by wxScrollWindow)
1486 if ( event.LeftDown() )
1487 {
1488 // capture the mouse to track the selected item
1489 lbox->CaptureMouse();
1490
1491 action = SetupCapture(lbox, event, item);
1492 }
1493 else if ( m_btnCapture && event.ButtonUp(m_btnCapture) )
1494 {
1495 // when the left mouse button is released, release the mouse too
1496 wxWindow *winCapture = wxWindow::GetCapture();
1497 if ( winCapture )
1498 {
1499 winCapture->ReleaseMouse();
1500 m_btnCapture = 0;
1501
1e6feb95
VZ
1502 action = m_actionMouse;
1503 }
1504 //else: the mouse wasn't presed over the listbox, only released here
1505 }
1506 else if ( event.LeftDClick() )
1507 {
1508 action = wxACTION_LISTBOX_ACTIVATE;
1509 }
1510
a290fa5a 1511 if ( !action.IsEmpty() )
1e6feb95
VZ
1512 {
1513 lbox->PerformAction(action, item);
1514
a290fa5a 1515 return true;
1e6feb95
VZ
1516 }
1517
23645bfa 1518 return wxStdInputHandler::HandleMouse(consumer, event);
1e6feb95
VZ
1519}
1520
23645bfa 1521bool wxStdListboxInputHandler::HandleMouseMove(wxInputConsumer *consumer,
1e6feb95
VZ
1522 const wxMouseEvent& event)
1523{
1524 wxWindow *winCapture = wxWindow::GetCapture();
1525 if ( winCapture && (event.GetEventObject() == winCapture) )
1526 {
23645bfa 1527 wxListBox *lbox = wxStaticCast(consumer->GetInputWindow(), wxListBox);
1e6feb95
VZ
1528
1529 if ( !m_btnCapture || !m_trackMouseOutside )
1530 {
1531 // someone captured the mouse for us (we always set m_btnCapture
1532 // when we do it ourselves): in this case we only react to
1533 // the mouse messages when they happen inside the listbox
1534 if ( lbox->HitTest(event.GetPosition()) != wxHT_WINDOW_INSIDE )
a290fa5a 1535 return false;
1e6feb95
VZ
1536 }
1537
1538 int item = HitTest(lbox, event);
1539 if ( !m_btnCapture )
1540 {
1541 // now that we have the mouse inside the listbox, do capture it
1542 // normally - but ensure that we will still ignore the outside
1543 // events
1544 SetupCapture(lbox, event, item);
1545
a290fa5a 1546 m_trackMouseOutside = false;
1e6feb95
VZ
1547 }
1548
1549 if ( IsValidIndex(lbox, item) )
1550 {
55f095d4
VZ
1551 // pass something into strArg to tell the listbox that it shouldn't
1552 // send the notification message: see PerformAction() above
1553 lbox->PerformAction(m_actionMouse, item, _T("no"));
1e6feb95
VZ
1554 }
1555 // else: don't pass invalid index to the listbox
1556 }
1557 else // we don't have capture any more
1558 {
1559 if ( m_btnCapture )
1560 {
1561 // if we lost capture unexpectedly (someone else took the capture
1562 // from us), return to a consistent state
1563 m_btnCapture = 0;
1564 }
1565 }
1566
23645bfa 1567 return wxStdInputHandler::HandleMouseMove(consumer, event);
1e6feb95
VZ
1568}
1569
1570#endif // wxUSE_LISTBOX