]> git.saurik.com Git - wxWidgets.git/blame - src/generic/odcombo.cpp
Fix horizontal mouse wheel scrolling in wxGTK.
[wxWidgets.git] / src / generic / odcombo.cpp
CommitLineData
a340b80d 1/////////////////////////////////////////////////////////////////////////////
85fed18c 2// Name: src/generic/odcombo.cpp
a340b80d
VZ
3// Purpose: wxOwnerDrawnComboBox, wxVListBoxComboPopup
4// Author: Jaakko Salli
5// Modified by:
6// Created: Apr-30-2006
a340b80d
VZ
7// Copyright: (c) 2005 Jaakko Salli
8// Licence: wxWindows licence
9/////////////////////////////////////////////////////////////////////////////
10
11// ============================================================================
12// declarations
13// ============================================================================
14
15// ----------------------------------------------------------------------------
16// headers
17// ----------------------------------------------------------------------------
18
19#include "wx/wxprec.h"
20
21#ifdef __BORLANDC__
22 #pragma hdrstop
23#endif
24
a57d600f 25#if wxUSE_ODCOMBOBOX
a340b80d 26
85fed18c
WS
27#include "wx/odcombo.h"
28
a340b80d
VZ
29#ifndef WX_PRECOMP
30 #include "wx/log.h"
31 #include "wx/combobox.h"
32 #include "wx/dcclient.h"
33 #include "wx/settings.h"
34 #include "wx/dialog.h"
0ec1179b 35 #include "wx/textctrl.h"
a340b80d
VZ
36#endif
37
38#include "wx/combo.h"
a340b80d
VZ
39
40// ============================================================================
41// implementation
42// ============================================================================
43
dc8a1aa5
AB
44// time in milliseconds before partial completion buffer drops
45#define wxODCB_PARTIAL_COMPLETION_TIME 1000
a340b80d
VZ
46
47// ----------------------------------------------------------------------------
48// wxVListBoxComboPopup is a wxVListBox customized to act as a popup control
49//
50// ----------------------------------------------------------------------------
51
52
53BEGIN_EVENT_TABLE(wxVListBoxComboPopup, wxVListBox)
54 EVT_MOTION(wxVListBoxComboPopup::OnMouseMove)
55 EVT_KEY_DOWN(wxVListBoxComboPopup::OnKey)
c765e6fb 56 EVT_CHAR(wxVListBoxComboPopup::OnChar)
a340b80d
VZ
57 EVT_LEFT_UP(wxVListBoxComboPopup::OnLeftClick)
58END_EVENT_TABLE()
59
60
6d0ce565 61void wxVListBoxComboPopup::Init()
a340b80d
VZ
62{
63 m_widestWidth = 0;
e5d63342
WS
64 m_widestItem = -1;
65 m_widthsDirty = false;
66 m_findWidest = false;
a340b80d
VZ
67 m_itemHeight = 0;
68 m_value = -1;
69 m_itemHover = -1;
70 m_clientDataItemsType = wxClientData_None;
dc8a1aa5 71 m_partialCompletionString = wxEmptyString;
a340b80d
VZ
72}
73
74bool wxVListBoxComboPopup::Create(wxWindow* parent)
75{
76 if ( !wxVListBox::Create(parent,
77 wxID_ANY,
78 wxDefaultPosition,
79 wxDefaultSize,
80 wxBORDER_SIMPLE | wxLB_INT_HEIGHT | wxWANTS_CHARS) )
81 return false;
82
6d0ce565 83 m_useFont = m_combo->GetFont();
a340b80d
VZ
84
85 wxVListBox::SetItemCount(m_strings.GetCount());
86
87 // TODO: Move this to SetFont
88 m_itemHeight = GetCharHeight() + 0;
89
90 return true;
91}
92
93wxVListBoxComboPopup::~wxVListBoxComboPopup()
94{
95 Clear();
96}
97
c0c05e79
JS
98void wxVListBoxComboPopup::SetFocus()
99{
100 // Suppress SetFocus() warning by simply not calling it. This combo popup
101 // has already been designed with the assumption that SetFocus() may not
102 // do anything useful, so it really doesn't need to be called.
103#ifdef __WXMSW__
104 //
105#else
106 wxVListBox::SetFocus();
107#endif
108}
109
a340b80d
VZ
110bool wxVListBoxComboPopup::LazyCreate()
111{
112 // NB: There is a bug with wxVListBox that can be avoided by creating
113 // it later (bug causes empty space to be shown if initial selection
114 // is at the end of a list longer than the control can show at once).
115 return true;
116}
117
118// paint the control itself
119void wxVListBoxComboPopup::PaintComboControl( wxDC& dc, const wxRect& rect )
120{
121 if ( !(m_combo->GetWindowStyle() & wxODCB_STD_CONTROL_PAINT) )
122 {
ce22ac45
RR
123 int flags = wxODCB_PAINTING_CONTROL;
124
125 if ( m_combo->ShouldDrawFocus() )
126 flags |= wxODCB_PAINTING_SELECTED;
127
128 OnDrawBg(dc, rect, m_value, flags);
129
a340b80d
VZ
130 if ( m_value >= 0 )
131 {
b4ff336e 132 OnDrawItem(dc,rect,m_value,flags);
6d0ce565 133 return;
a340b80d
VZ
134 }
135 }
136
137 wxComboPopup::PaintComboControl(dc,rect);
138}
139
140void wxVListBoxComboPopup::OnDrawItem(wxDC& dc, const wxRect& rect, size_t n) const
141{
6d0ce565
VZ
142 // TODO: Maybe this code could be moved to wxVListBox::OnPaint?
143 dc.SetFont(m_useFont);
a340b80d 144
b4ff336e
VZ
145 int flags = 0;
146
a340b80d 147 // Set correct text colour for selected items
6d0ce565 148 if ( wxVListBox::GetSelection() == (int) n )
b4ff336e 149 {
a340b80d 150 dc.SetTextForeground( wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT) );
b4ff336e
VZ
151 flags |= wxODCB_PAINTING_SELECTED;
152 }
a340b80d 153 else
b4ff336e 154 {
a340b80d 155 dc.SetTextForeground( wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT) );
b4ff336e 156 }
a340b80d 157
b4ff336e 158 OnDrawItem(dc,rect,(int)n,flags);
a340b80d
VZ
159}
160
40b26d75 161wxCoord wxVListBoxComboPopup::OnMeasureItem(size_t n) const
a340b80d 162{
40b26d75
WS
163 wxOwnerDrawnComboBox* combo = (wxOwnerDrawnComboBox*) m_combo;
164
345c78ca 165 wxASSERT_MSG( wxDynamicCast(combo, wxOwnerDrawnComboBox),
40b26d75
WS
166 wxT("you must subclass wxVListBoxComboPopup for drawing and measuring methods") );
167
168 wxCoord h = combo->OnMeasureItem(n);
169 if ( h < 0 )
170 h = m_itemHeight;
171 return h;
6d0ce565 172}
a340b80d 173
40b26d75 174wxCoord wxVListBoxComboPopup::OnMeasureItemWidth(size_t n) const
6d0ce565 175{
40b26d75
WS
176 wxOwnerDrawnComboBox* combo = (wxOwnerDrawnComboBox*) m_combo;
177
345c78ca 178 wxASSERT_MSG( wxDynamicCast(combo, wxOwnerDrawnComboBox),
40b26d75
WS
179 wxT("you must subclass wxVListBoxComboPopup for drawing and measuring methods") );
180
181 return combo->OnMeasureItemWidth(n);
182}
183
184void wxVListBoxComboPopup::OnDrawBg( wxDC& dc,
185 const wxRect& rect,
186 int item,
187 int flags ) const
188{
189 wxOwnerDrawnComboBox* combo = (wxOwnerDrawnComboBox*) m_combo;
190
345c78ca 191 wxASSERT_MSG( wxDynamicCast(combo, wxOwnerDrawnComboBox),
40b26d75
WS
192 wxT("you must subclass wxVListBoxComboPopup for drawing and measuring methods") );
193
ce22ac45
RR
194 if ( IsCurrent((size_t)item) && !(flags & wxODCB_PAINTING_CONTROL) )
195 flags |= wxODCB_PAINTING_SELECTED;
196
40b26d75 197 combo->OnDrawBackground(dc,rect,item,flags);
a340b80d
VZ
198}
199
200void wxVListBoxComboPopup::OnDrawBackground(wxDC& dc, const wxRect& rect, size_t n) const
201{
40b26d75 202 OnDrawBg(dc,rect,(int)n,0);
a340b80d
VZ
203}
204
6d0ce565
VZ
205// This is called from wxVListBoxComboPopup::OnDrawItem, with text colour and font prepared
206void wxVListBoxComboPopup::OnDrawItem( wxDC& dc, const wxRect& rect, int item, int flags ) const
207{
40b26d75
WS
208 wxOwnerDrawnComboBox* combo = (wxOwnerDrawnComboBox*) m_combo;
209
345c78ca 210 wxASSERT_MSG( wxDynamicCast(combo, wxOwnerDrawnComboBox),
40b26d75
WS
211 wxT("you must subclass wxVListBoxComboPopup for drawing and measuring methods") );
212
213 combo->OnDrawItem(dc,rect,item,flags);
6d0ce565
VZ
214}
215
216void wxVListBoxComboPopup::DismissWithEvent()
217{
dc8a1aa5
AB
218 StopPartialCompletion();
219
6d0ce565
VZ
220 int selection = wxVListBox::GetSelection();
221
222 Dismiss();
223
6d0ce565 224 if ( selection != wxNOT_FOUND )
fdb47e62 225 m_stringValue = m_strings[selection];
6d0ce565 226 else
fdb47e62 227 m_stringValue = wxEmptyString;
6d0ce565 228
fdb47e62 229 if ( m_stringValue != m_combo->GetValue() )
0306e73e 230 m_combo->SetValueByUser(m_stringValue);
6d0ce565 231
fdb47e62 232 m_value = selection;
6d0ce565
VZ
233
234 SendComboBoxEvent(selection);
235}
236
237void wxVListBoxComboPopup::SendComboBoxEvent( int selection )
a340b80d 238{
ce7fe42e 239 wxCommandEvent evt(wxEVT_COMBOBOX,m_combo->GetId());
a340b80d
VZ
240
241 evt.SetEventObject(m_combo);
6d0ce565 242
a340b80d
VZ
243 evt.SetInt(selection);
244
245 // Set client data, if any
246 if ( selection >= 0 && (int)m_clientDatas.GetCount() > selection )
247 {
248 void* clientData = m_clientDatas[selection];
249 if ( m_clientDataItemsType == wxClientData_Object )
250 evt.SetClientObject((wxClientData*)clientData);
251 else
252 evt.SetClientData(clientData);
253 }
254
255 m_combo->GetEventHandler()->AddPendingEvent(evt);
256}
257
258// returns true if key was consumed
c765e6fb 259bool wxVListBoxComboPopup::HandleKey( int keycode, bool saturate, wxChar keychar )
a340b80d 260{
704bc436
VZ
261 const int itemCount = GetCount();
262
263 // keys do nothing in the empty control and returning immediately avoids
264 // using invalid indices below
265 if ( !itemCount )
266 return false;
267
a340b80d 268 int value = m_value;
dc8a1aa5
AB
269 int comboStyle = m_combo->GetWindowStyle();
270
c765e6fb 271 if ( keychar > 0 )
dc8a1aa5 272 {
c765e6fb
VS
273 // we have character equivalent of the keycode; filter out these that
274 // are not printable characters
275 if ( !wxIsprint(keychar) )
276 keychar = 0;
dc8a1aa5 277 }
a340b80d 278
39191cbf
VZ
279 const bool readOnly = (comboStyle & wxCB_READONLY) != 0;
280
281 if ( keycode == WXK_DOWN || keycode == WXK_NUMPAD_DOWN || ( keycode == WXK_RIGHT && readOnly ) )
a340b80d
VZ
282 {
283 value++;
dc8a1aa5 284 StopPartialCompletion();
a340b80d 285 }
39191cbf 286 else if ( keycode == WXK_UP || keycode == WXK_NUMPAD_UP || ( keycode == WXK_LEFT && readOnly ) )
a340b80d
VZ
287 {
288 value--;
dc8a1aa5 289 StopPartialCompletion();
a340b80d 290 }
293987e8 291 else if ( keycode == WXK_PAGEDOWN || keycode == WXK_NUMPAD_PAGEDOWN )
a340b80d
VZ
292 {
293 value+=10;
dc8a1aa5 294 StopPartialCompletion();
a340b80d 295 }
293987e8 296 else if ( keycode == WXK_PAGEUP || keycode == WXK_NUMPAD_PAGEUP )
a340b80d
VZ
297 {
298 value-=10;
dc8a1aa5
AB
299 StopPartialCompletion();
300 }
39191cbf 301 else if ( ( keycode == WXK_HOME || keycode == WXK_NUMPAD_HOME ) && readOnly )
10265e6c
VS
302 {
303 value=0;
304 StopPartialCompletion();
305 }
39191cbf 306 else if ( ( keycode == WXK_END || keycode == WXK_NUMPAD_END ) && readOnly )
10265e6c
VS
307 {
308 value=itemCount-1;
309 StopPartialCompletion();
310 }
39191cbf 311 else if ( keychar && readOnly )
dc8a1aa5
AB
312 {
313 // Try partial completion
314
315 // find the new partial completion string
f0fa8b47 316#if wxUSE_TIMER
dc8a1aa5
AB
317 if (m_partialCompletionTimer.IsRunning())
318 m_partialCompletionString+=wxString(keychar);
319 else
f0fa8b47 320#endif // wxUSE_TIMER
dc8a1aa5
AB
321 m_partialCompletionString=wxString(keychar);
322
323 // now search through the values to see if this is found
324 int found = -1;
4dd31ff5 325 unsigned int length=m_partialCompletionString.length();
dc8a1aa5
AB
326 int i;
327 for (i=0; i<itemCount; i++)
328 {
329 wxString item=GetString(i);
4dd31ff5 330 if (( item.length() >= length) && (! m_partialCompletionString.CmpNoCase(item.Left(length))))
dc8a1aa5
AB
331 {
332 found=i;
333 break;
334 }
335 }
336
337 if (found<0)
338 {
339 StopPartialCompletion();
340 ::wxBell();
341 return true; // to stop the first value being set
342 }
343 else
344 {
345 value=i;
f0fa8b47 346#if wxUSE_TIMER
dc8a1aa5 347 m_partialCompletionTimer.Start(wxODCB_PARTIAL_COMPLETION_TIME, true);
f0fa8b47 348#endif // wxUSE_TIMER
dc8a1aa5 349 }
a340b80d 350 }
a340b80d
VZ
351 else
352 return false;
353
354 if ( saturate )
355 {
356 if ( value >= itemCount )
357 value = itemCount - 1;
358 else if ( value < 0 )
359 value = 0;
360 }
361 else
362 {
363 if ( value >= itemCount )
364 value -= itemCount;
365 else if ( value < 0 )
366 value += itemCount;
367 }
368
369 if ( value == m_value )
370 // Even if value was same, don't skip the event
371 // (good for consistency)
372 return true;
373
a340b80d 374 if ( value >= 0 )
65d30b79 375 m_combo->ChangeValue(m_strings[value]);
a340b80d 376
71dd05d5
VS
377 // The m_combo->SetValue() call above sets m_value to the index of this
378 // string. But if there are more identical string, the index is of the
e75e8815 379 // first occurrence, which may be wrong, so set the index explicitly here,
71dd05d5
VS
380 // _after_ the SetValue() call.
381 m_value = value;
382
6d0ce565 383 SendComboBoxEvent(m_value);
a340b80d
VZ
384
385 return true;
386}
387
dc8a1aa5
AB
388// stop partial completion
389void wxVListBoxComboPopup::StopPartialCompletion()
390{
391 m_partialCompletionString = wxEmptyString;
f0fa8b47 392#if wxUSE_TIMER
dc8a1aa5 393 m_partialCompletionTimer.Stop();
f0fa8b47 394#endif // wxUSE_TIMER
dc8a1aa5
AB
395}
396
a340b80d
VZ
397void wxVListBoxComboPopup::OnComboDoubleClick()
398{
399 // Cycle on dclick (disable saturation to allow true cycling).
400 if ( !::wxGetKeyState(WXK_SHIFT) )
401 HandleKey(WXK_DOWN,false);
402 else
403 HandleKey(WXK_UP,false);
404}
405
406void wxVListBoxComboPopup::OnComboKeyEvent( wxKeyEvent& event )
407{
408 // Saturated key movement on
c765e6fb
VS
409 if ( !HandleKey(event.GetKeyCode(), true) )
410 event.Skip();
411}
412
413void wxVListBoxComboPopup::OnComboCharEvent( wxKeyEvent& event )
414{
415 // unlike in OnComboKeyEvent, wxEVT_CHAR contains meaningful
416 // printable character information, so pass it
dc8a1aa5 417#if wxUSE_UNICODE
c765e6fb 418 const wxChar charcode = event.GetUnicodeKey();
dc8a1aa5 419#else
c765e6fb 420 const wxChar charcode = (wxChar)event.GetKeyCode();
dc8a1aa5 421#endif
c765e6fb
VS
422
423 if ( !HandleKey(event.GetKeyCode(), true, charcode) )
a340b80d
VZ
424 event.Skip();
425}
426
427void wxVListBoxComboPopup::OnPopup()
428{
429 // *must* set value after size is set (this is because of a vlbox bug)
430 wxVListBox::SetSelection(m_value);
431}
432
433void wxVListBoxComboPopup::OnMouseMove(wxMouseEvent& event)
434{
276ebe79
WS
435 event.Skip();
436
a340b80d 437 // Move selection to cursor if it is inside the popup
a340b80d 438
276ebe79
WS
439 int y = event.GetPosition().y;
440 int fromBottom = GetClientSize().y - y;
441
442 // Since in any case we need to find out if the last item is only
443 // partially visible, we might just as well replicate the HitTest
444 // loop here.
445 const size_t lineMax = GetVisibleEnd();
446 for ( size_t line = GetVisibleBegin(); line < lineMax; line++ )
447 {
e02c72fa 448 y -= OnGetRowHeight(line);
276ebe79
WS
449 if ( y < 0 )
450 {
451 // Only change selection if item is fully visible
452 if ( (y + fromBottom) >= 0 )
453 {
454 wxVListBox::SetSelection((int)line);
455 return;
456 }
457 }
458 }
a340b80d
VZ
459}
460
461void wxVListBoxComboPopup::OnLeftClick(wxMouseEvent& WXUNUSED(event))
462{
6d0ce565 463 DismissWithEvent();
a340b80d
VZ
464}
465
466void wxVListBoxComboPopup::OnKey(wxKeyEvent& event)
467{
b445b6a7
VZ
468 // Hide popup if certain key or key combination was pressed
469 if ( m_combo->IsKeyPopupToggle(event) )
dc8a1aa5
AB
470 {
471 StopPartialCompletion();
a340b80d 472 Dismiss();
dc8a1aa5 473 }
b445b6a7
VZ
474 else if ( event.AltDown() )
475 {
476 // On both wxGTK and wxMSW, pressing Alt down seems to
477 // completely freeze things in popup (ie. arrow keys and
478 // enter won't work).
479 return;
480 }
481 // Select item if ENTER is pressed
482 else if ( event.GetKeyCode() == WXK_RETURN || event.GetKeyCode() == WXK_NUMPAD_ENTER )
483 {
484 DismissWithEvent();
485 }
a340b80d 486 else
dc8a1aa5 487 {
c765e6fb
VS
488 // completion is handled in OnChar() below
489 event.Skip();
490 }
491}
492
493void wxVListBoxComboPopup::OnChar(wxKeyEvent& event)
494{
495 if ( m_combo->GetWindowStyle() & wxCB_READONLY )
496 {
497 // Process partial completion key codes here, but not the arrow keys as
498 // the base class will do that for us
499#if wxUSE_UNICODE
500 const wxChar charcode = event.GetUnicodeKey();
501#else
502 const wxChar charcode = (wxChar)event.GetKeyCode();
503#endif
504 if ( wxIsprint(charcode) )
dc8a1aa5 505 {
c765e6fb 506 OnComboCharEvent(event);
dc8a1aa5 507 SetSelection(m_value); // ensure the highlight bar moves
c765e6fb 508 return; // don't skip the event
dc8a1aa5 509 }
dc8a1aa5 510 }
c765e6fb
VS
511
512 event.Skip();
a340b80d
VZ
513}
514
a340b80d
VZ
515void wxVListBoxComboPopup::Insert( const wxString& item, int pos )
516{
517 // Need to change selection?
518 wxString strValue;
519 if ( !(m_combo->GetWindowStyle() & wxCB_READONLY) &&
520 m_combo->GetValue() == item )
6d0ce565 521 {
a340b80d 522 m_value = pos;
6d0ce565 523 }
a340b80d
VZ
524
525 m_strings.Insert(item,pos);
697c314b 526 if ( (int)m_clientDatas.size() >= pos )
57306cd4 527 m_clientDatas.Insert(NULL, pos);
a236aa20 528
e5d63342
WS
529 m_widths.Insert(-1,pos);
530 m_widthsDirty = true;
a340b80d
VZ
531
532 if ( IsCreated() )
533 wxVListBox::SetItemCount( wxVListBox::GetItemCount()+1 );
a340b80d
VZ
534}
535
536int wxVListBoxComboPopup::Append(const wxString& item)
537{
538 int pos = (int)m_strings.GetCount();
539
540 if ( m_combo->GetWindowStyle() & wxCB_SORT )
541 {
542 // Find position
543 // TODO: Could be optimized with binary search
544 wxArrayString strings = m_strings;
545 unsigned int i;
546
547 for ( i=0; i<strings.GetCount(); i++ )
548 {
f74ab004 549 if ( item.CmpNoCase(strings.Item(i)) <= 0 )
a340b80d
VZ
550 {
551 pos = (int)i;
552 break;
553 }
554 }
555 }
556
557 Insert(item,pos);
558
559 return pos;
560}
561
562void wxVListBoxComboPopup::Clear()
563{
564 wxASSERT(m_combo);
565
566 m_strings.Empty();
e5d63342
WS
567 m_widths.Empty();
568
569 m_widestWidth = 0;
570 m_widestItem = -1;
a340b80d
VZ
571
572 ClearClientDatas();
573
cdc99ddd
WS
574 m_value = wxNOT_FOUND;
575
a340b80d
VZ
576 if ( IsCreated() )
577 wxVListBox::SetItemCount(0);
578}
579
580void wxVListBoxComboPopup::ClearClientDatas()
581{
582 if ( m_clientDataItemsType == wxClientData_Object )
583 {
584 size_t i;
585 for ( i=0; i<m_clientDatas.GetCount(); i++ )
586 delete (wxClientData*) m_clientDatas[i];
587 }
588
589 m_clientDatas.Empty();
590}
591
592void wxVListBoxComboPopup::SetItemClientData( unsigned int n,
593 void* clientData,
594 wxClientDataType clientDataItemsType )
595{
596 // It should be sufficient to update this variable only here
597 m_clientDataItemsType = clientDataItemsType;
598
a340b80d 599 m_clientDatas[n] = clientData;
e5d63342
WS
600
601 ItemWidthChanged(n);
a340b80d
VZ
602}
603
604void* wxVListBoxComboPopup::GetItemClientData(unsigned int n) const
605{
606 if ( m_clientDatas.GetCount() > n )
607 return m_clientDatas[n];
608
609 return NULL;
610}
611
612void wxVListBoxComboPopup::Delete( unsigned int item )
613{
614 // Remove client data, if set
615 if ( m_clientDatas.GetCount() )
616 {
617 if ( m_clientDataItemsType == wxClientData_Object )
618 delete (wxClientData*) m_clientDatas[item];
619
620 m_clientDatas.RemoveAt(item);
621 }
622
623 m_strings.RemoveAt(item);
e5d63342
WS
624 m_widths.RemoveAt(item);
625
626 if ( (int)item == m_widestItem )
627 m_findWidest = true;
a340b80d 628
fa3ebb12
RR
629 int sel = GetSelection();
630
a340b80d
VZ
631 if ( IsCreated() )
632 wxVListBox::SetItemCount( wxVListBox::GetItemCount()-1 );
fa3ebb12
RR
633
634 // Fix selection
635 if ( (int)item < sel )
636 SetSelection(sel-1);
637 else if ( (int)item == sel )
638 SetSelection(wxNOT_FOUND);
a340b80d
VZ
639}
640
9e6aca68 641int wxVListBoxComboPopup::FindString(const wxString& s, bool bCase) const
a340b80d 642{
9e6aca68 643 return m_strings.Index(s, bCase);
a340b80d
VZ
644}
645
238b33ab
JS
646bool wxVListBoxComboPopup::FindItem(const wxString& item, wxString* trueItem)
647{
648 int idx = m_strings.Index(item, false);
649 if ( idx == wxNOT_FOUND )
650 return false;
651 if ( trueItem != NULL )
652 *trueItem = m_strings[idx];
653 return true;
654}
655
a340b80d
VZ
656unsigned int wxVListBoxComboPopup::GetCount() const
657{
658 return m_strings.GetCount();
659}
660
661wxString wxVListBoxComboPopup::GetString( int item ) const
662{
663 return m_strings[item];
664}
665
666void wxVListBoxComboPopup::SetString( int item, const wxString& str )
667{
668 m_strings[item] = str;
e5d63342 669 ItemWidthChanged(item);
a340b80d
VZ
670}
671
672wxString wxVListBoxComboPopup::GetStringValue() const
673{
fdb47e62 674 return m_stringValue;
a340b80d
VZ
675}
676
677void wxVListBoxComboPopup::SetSelection( int item )
678{
85fed18c
WS
679 wxCHECK_RET( item == wxNOT_FOUND || ((unsigned int)item < GetCount()),
680 wxT("invalid index in wxVListBoxComboPopup::SetSelection") );
a340b80d
VZ
681
682 m_value = item;
683
fdb47e62
VZ
684 if ( item >= 0 )
685 m_stringValue = m_strings[item];
686 else
687 m_stringValue = wxEmptyString;
688
a340b80d
VZ
689 if ( IsCreated() )
690 wxVListBox::SetSelection(item);
691}
692
6d0ce565
VZ
693int wxVListBoxComboPopup::GetSelection() const
694{
695 return m_value;
696}
697
a340b80d
VZ
698void wxVListBoxComboPopup::SetStringValue( const wxString& value )
699{
700 int index = m_strings.Index(value);
701
fdb47e62 702 m_stringValue = value;
a340b80d 703
fdb47e62
VZ
704 if ( index >= 0 && index < (int)wxVListBox::GetItemCount() )
705 {
a340b80d 706 wxVListBox::SetSelection(index);
fdb47e62
VZ
707 m_value = index;
708 }
a340b80d
VZ
709}
710
d9e0958c 711void wxVListBoxComboPopup::CalcWidths()
a340b80d 712{
e5d63342
WS
713 bool doFindWidest = m_findWidest;
714
715 // Measure items with dirty width.
716 if ( m_widthsDirty )
717 {
718 unsigned int i;
719 unsigned int n = m_widths.GetCount();
720 int dirtyHandled = 0;
721 wxArrayInt& widths = m_widths;
722
723 // I think using wxDC::GetTextExtent is faster than
724 // wxWindow::GetTextExtent (assuming same dc is used
725 // for all calls, as we do here).
726 wxClientDC dc(m_combo);
e2ac737f
VZ
727 if ( !m_useFont.IsOk() )
728 m_useFont = m_combo->GetFont();
e5d63342
WS
729 dc.SetFont(m_useFont);
730
731 for ( i=0; i<n; i++ )
732 {
733 if ( widths[i] < 0 )
734 {
735 wxCoord x = OnMeasureItemWidth(i);
736
737 if ( x < 0 )
738 {
739 const wxString& text = m_strings[i];
740
741 // To make sure performance won't suck in extreme scenarios,
742 // we'll estimate length after some arbitrary number of items
743 // have been checked precily.
744 if ( dirtyHandled < 1024 )
745 {
746 wxCoord y;
747 dc.GetTextExtent(text, &x, &y, 0, 0);
748 x += 4;
749 }
750 else
751 {
752 x = text.length() * (dc.GetCharWidth()+1);
753 }
754 }
755
756 widths[i] = x;
757
758 if ( x >= m_widestWidth )
759 {
760 m_widestWidth = x;
761 m_widestItem = (int)i;
762 }
763 else if ( (int)i == m_widestItem )
764 {
765 // Width of previously widest item has been decreased, so
766 // we'll have to check all to find current widest item.
767 doFindWidest = true;
768 }
769
770 dirtyHandled++;
771 }
772 }
773
774 m_widthsDirty = false;
775 }
776
777 if ( doFindWidest )
778 {
779 unsigned int i;
780 unsigned int n = m_widths.GetCount();
781
782 int bestWidth = -1;
783 int bestIndex = -1;
784
785 for ( i=0; i<n; i++ )
786 {
787 int w = m_widths[i];
788 if ( w > bestWidth )
789 {
790 bestIndex = (int)i;
791 bestWidth = w;
792 }
793 }
794
795 m_widestWidth = bestWidth;
796 m_widestItem = bestIndex;
797
798 m_findWidest = false;
799 }
d9e0958c
AB
800}
801
802wxSize wxVListBoxComboPopup::GetAdjustedSize( int minWidth, int prefHeight, int maxHeight )
803{
804 int height = 250;
805
7962f85a
RR
806 maxHeight -= 2; // Must take borders into account
807
d9e0958c
AB
808 if ( m_strings.GetCount() )
809 {
810 if ( prefHeight > 0 )
811 height = prefHeight;
812
813 if ( height > maxHeight )
814 height = maxHeight;
815
816 int totalHeight = GetTotalHeight(); // + 3;
a44c4abe
JS
817
818 // Take borders into account on Mac or scrollbars always appear
819#if defined(__WXMAC__)
820 totalHeight += 2;
821#endif
d9e0958c
AB
822 if ( height >= totalHeight )
823 {
824 height = totalHeight;
825 }
826 else
827 {
828 // Adjust height to a multiple of the height of the first item
829 // NB: Calculations that take variable height into account
830 // are unnecessary.
831 int fih = GetLineHeight(0);
7962f85a 832 height -= height % fih;
d9e0958c
AB
833 }
834 }
835 else
836 height = 50;
837
838 CalcWidths();
e5d63342 839
a340b80d
VZ
840 // Take scrollbar into account in width calculations
841 int widestWidth = m_widestWidth + wxSystemSettings::GetMetric(wxSYS_VSCROLL_X);
842 return wxSize(minWidth > widestWidth ? minWidth : widestWidth,
843 height+2);
844}
845
6d0ce565
VZ
846//void wxVListBoxComboPopup::Populate( int n, const wxString choices[] )
847void wxVListBoxComboPopup::Populate( const wxArrayString& choices )
a340b80d
VZ
848{
849 int i;
850
6d0ce565
VZ
851 int n = choices.GetCount();
852
a340b80d
VZ
853 for ( i=0; i<n; i++ )
854 {
6d0ce565 855 const wxString& item = choices.Item(i);
a340b80d 856 m_strings.Add(item);
a340b80d
VZ
857 }
858
e5d63342
WS
859 m_widths.SetCount(n,-1);
860 m_widthsDirty = true;
861
a340b80d
VZ
862 if ( IsCreated() )
863 wxVListBox::SetItemCount(n);
864
865 // Sort the initial choices
866 if ( m_combo->GetWindowStyle() & wxCB_SORT )
867 m_strings.Sort();
868
869 // Find initial selection
870 wxString strValue = m_combo->GetValue();
6636ef8d 871 if ( !strValue.empty() )
a340b80d
VZ
872 m_value = m_strings.Index(strValue);
873}
874
875// ----------------------------------------------------------------------------
876// wxOwnerDrawnComboBox
877// ----------------------------------------------------------------------------
878
879
a57d600f 880BEGIN_EVENT_TABLE(wxOwnerDrawnComboBox, wxComboCtrl)
a340b80d
VZ
881END_EVENT_TABLE()
882
a340b80d
VZ
883void wxOwnerDrawnComboBox::Init()
884{
885}
886
887bool wxOwnerDrawnComboBox::Create(wxWindow *parent,
888 wxWindowID id,
889 const wxString& value,
890 const wxPoint& pos,
891 const wxSize& size,
892 long style,
893 const wxValidator& validator,
894 const wxString& name)
895{
a57d600f 896 return wxComboCtrl::Create(parent,id,value,pos,size,style,validator,name);
a340b80d
VZ
897}
898
899wxOwnerDrawnComboBox::wxOwnerDrawnComboBox(wxWindow *parent,
900 wxWindowID id,
901 const wxString& value,
902 const wxPoint& pos,
903 const wxSize& size,
904 const wxArrayString& choices,
905 long style,
906 const wxValidator& validator,
907 const wxString& name)
a340b80d
VZ
908{
909 Init();
910
911 Create(parent,id,value,pos,size,choices,style, validator, name);
912}
913
914bool wxOwnerDrawnComboBox::Create(wxWindow *parent,
915 wxWindowID id,
916 const wxString& value,
917 const wxPoint& pos,
918 const wxSize& size,
919 const wxArrayString& choices,
920 long style,
921 const wxValidator& validator,
922 const wxString& name)
923{
6d0ce565
VZ
924 m_initChs = choices;
925 //wxCArrayString chs(choices);
a340b80d 926
6d0ce565
VZ
927 //return Create(parent, id, value, pos, size, chs.GetCount(),
928 // chs.GetStrings(), style, validator, name);
929 return Create(parent, id, value, pos, size, 0,
930 NULL, style, validator, name);
a340b80d
VZ
931}
932
933bool wxOwnerDrawnComboBox::Create(wxWindow *parent,
934 wxWindowID id,
935 const wxString& value,
936 const wxPoint& pos,
937 const wxSize& size,
938 int n,
939 const wxString choices[],
940 long style,
941 const wxValidator& validator,
942 const wxString& name)
943{
944
945 if ( !Create(parent, id, value, pos, size, style,
946 validator, name) )
947 {
948 return false;
949 }
950
6d0ce565
VZ
951 int i;
952 for ( i=0; i<n; i++ )
953 m_initChs.Add(choices[i]);
a340b80d
VZ
954
955 return true;
956}
957
958wxOwnerDrawnComboBox::~wxOwnerDrawnComboBox()
959{
960 if ( m_popupInterface )
57693473 961 GetVListBoxComboPopup()->ClearClientDatas();
a340b80d
VZ
962}
963
db53c6ea 964void wxOwnerDrawnComboBox::DoSetPopupControl(wxComboPopup* popup)
6d0ce565
VZ
965{
966 if ( !popup )
967 {
968 popup = new wxVListBoxComboPopup();
969 }
970
db53c6ea 971 wxComboCtrl::DoSetPopupControl(popup);
6d0ce565
VZ
972
973 wxASSERT(popup);
6d0ce565
VZ
974
975 // Add initial choices to the wxVListBox
57693473 976 if ( !GetVListBoxComboPopup()->GetCount() )
6d0ce565 977 {
57693473 978 GetVListBoxComboPopup()->Populate(m_initChs);
6d0ce565
VZ
979 m_initChs.Clear();
980 }
981}
982
a340b80d
VZ
983// ----------------------------------------------------------------------------
984// wxOwnerDrawnComboBox item manipulation methods
985// ----------------------------------------------------------------------------
986
a236aa20 987void wxOwnerDrawnComboBox::DoClear()
a340b80d 988{
6d0ce565 989 EnsurePopupControl();
a340b80d 990
57693473 991 GetVListBoxComboPopup()->Clear();
a340b80d 992
65d30b79
JS
993 // NB: This really needs to be SetValue() instead of ChangeValue(),
994 // as wxTextEntry API expects an event to be sent.
cdc99ddd 995 SetValue(wxEmptyString);
a340b80d
VZ
996}
997
eae20c90
JS
998void wxOwnerDrawnComboBox::Clear()
999{
1000 DoClear();
1001}
1002
a236aa20 1003void wxOwnerDrawnComboBox::DoDeleteOneItem(unsigned int n)
a340b80d 1004{
9a83f860 1005 wxCHECK_RET( IsValid(n), wxT("invalid index in wxOwnerDrawnComboBox::Delete") );
a340b80d
VZ
1006
1007 if ( GetSelection() == (int) n )
65d30b79 1008 ChangeValue(wxEmptyString);
a340b80d 1009
57693473 1010 GetVListBoxComboPopup()->Delete(n);
a340b80d
VZ
1011}
1012
1013unsigned int wxOwnerDrawnComboBox::GetCount() const
1014{
54a61762
WS
1015 if ( !m_popupInterface )
1016 return m_initChs.GetCount();
1017
57693473 1018 return GetVListBoxComboPopup()->GetCount();
a340b80d
VZ
1019}
1020
1021wxString wxOwnerDrawnComboBox::GetString(unsigned int n) const
1022{
9a83f860 1023 wxCHECK_MSG( IsValid(n), wxEmptyString, wxT("invalid index in wxOwnerDrawnComboBox::GetString") );
54a61762
WS
1024
1025 if ( !m_popupInterface )
1026 return m_initChs.Item(n);
1027
57693473 1028 return GetVListBoxComboPopup()->GetString(n);
a340b80d
VZ
1029}
1030
1031void wxOwnerDrawnComboBox::SetString(unsigned int n, const wxString& s)
1032{
54a61762
WS
1033 EnsurePopupControl();
1034
9a83f860 1035 wxCHECK_RET( IsValid(n), wxT("invalid index in wxOwnerDrawnComboBox::SetString") );
54a61762 1036
57693473 1037 GetVListBoxComboPopup()->SetString(n,s);
a340b80d
VZ
1038}
1039
9e6aca68 1040int wxOwnerDrawnComboBox::FindString(const wxString& s, bool bCase) const
a340b80d 1041{
54a61762
WS
1042 if ( !m_popupInterface )
1043 return m_initChs.Index(s, bCase);
1044
57693473 1045 return GetVListBoxComboPopup()->FindString(s, bCase);
a340b80d
VZ
1046}
1047
1048void wxOwnerDrawnComboBox::Select(int n)
1049{
6d0ce565 1050 EnsurePopupControl();
a340b80d 1051
9a83f860 1052 wxCHECK_RET( (n == wxNOT_FOUND) || IsValid(n), wxT("invalid index in wxOwnerDrawnComboBox::Select") );
40b26d75 1053
57693473 1054 GetVListBoxComboPopup()->SetSelection(n);
a340b80d
VZ
1055
1056 wxString str;
1057 if ( n >= 0 )
57693473 1058 str = GetVListBoxComboPopup()->GetString(n);
a340b80d
VZ
1059
1060 // Refresh text portion in control
1061 if ( m_text )
65d30b79 1062 m_text->ChangeValue( str );
a340b80d
VZ
1063 else
1064 m_valueString = str;
1065
1066 Refresh();
1067}
1068
1069int wxOwnerDrawnComboBox::GetSelection() const
1070{
54a61762
WS
1071 if ( !m_popupInterface )
1072 return m_initChs.Index(m_valueString);
1073
57693473 1074 return GetVListBoxComboPopup()->GetSelection();
a340b80d
VZ
1075}
1076
9ff4b6cc
JS
1077void wxOwnerDrawnComboBox::GetSelection(long *from, long *to) const
1078{
2debe2b1 1079 wxComboCtrl::GetSelection(from, to);
9ff4b6cc
JS
1080}
1081
a236aa20
VZ
1082int wxOwnerDrawnComboBox::DoInsertItems(const wxArrayStringsAdapter& items,
1083 unsigned int pos,
1084 void **clientData,
1085 wxClientDataType type)
a340b80d 1086{
40b26d75
WS
1087 EnsurePopupControl();
1088
a236aa20 1089 const unsigned int count = items.GetCount();
fd11f777
VZ
1090
1091 if ( HasFlag(wxCB_SORT) )
a236aa20 1092 {
fd11f777
VZ
1093 int n = pos;
1094
1095 for ( unsigned int i = 0; i < count; ++i )
1096 {
31329795 1097 n = GetVListBoxComboPopup()->Append(items[i]);
fd11f777
VZ
1098 AssignNewItemClientData(n, clientData, i, type);
1099 }
1100
1101 return n;
a236aa20 1102 }
fd11f777
VZ
1103 else
1104 {
1105 for ( unsigned int i = 0; i < count; ++i, ++pos )
1106 {
1107 GetVListBoxComboPopup()->Insert(items[i], pos);
1108 AssignNewItemClientData(pos, clientData, i, type);
1109 }
a340b80d 1110
fd11f777
VZ
1111 return pos - 1;
1112 }
a340b80d
VZ
1113}
1114
1115void wxOwnerDrawnComboBox::DoSetItemClientData(unsigned int n, void* clientData)
1116{
6d0ce565 1117 EnsurePopupControl();
57693473 1118
131b1fba
VZ
1119 GetVListBoxComboPopup()->SetItemClientData(n, clientData,
1120 GetClientDataType());
a340b80d
VZ
1121}
1122
1123void* wxOwnerDrawnComboBox::DoGetItemClientData(unsigned int n) const
1124{
54a61762
WS
1125 if ( !m_popupInterface )
1126 return NULL;
1127
57693473 1128 return GetVListBoxComboPopup()->GetItemClientData(n);
a340b80d
VZ
1129}
1130
40b26d75
WS
1131// ----------------------------------------------------------------------------
1132// wxOwnerDrawnComboBox item drawing and measuring default implementations
1133// ----------------------------------------------------------------------------
1134
1135void wxOwnerDrawnComboBox::OnDrawItem( wxDC& dc,
1136 const wxRect& rect,
1137 int item,
1138 int flags ) const
1139{
1140 if ( flags & wxODCB_PAINTING_CONTROL )
1141 {
107defe3
JS
1142 wxString text;
1143
1144 if ( !ShouldUseHintText() )
1145 {
1146 text = GetValue();
1147 }
1148 else
1149 {
1150 text = GetHint();
1151 wxColour col = wxSystemSettings::GetColour(wxSYS_COLOUR_GRAYTEXT);
1152 dc.SetTextForeground(col);
1153 }
1154
1155 dc.DrawText( text,
0847e36e 1156 rect.x + GetMargins().x,
40b26d75
WS
1157 (rect.height-dc.GetCharHeight())/2 + rect.y );
1158 }
1159 else
1160 {
57693473 1161 dc.DrawText( GetVListBoxComboPopup()->GetString(item), rect.x + 2, rect.y );
40b26d75
WS
1162 }
1163}
1164
1165wxCoord wxOwnerDrawnComboBox::OnMeasureItem( size_t WXUNUSED(item) ) const
1166{
1167 return -1;
1168}
1169
1170wxCoord wxOwnerDrawnComboBox::OnMeasureItemWidth( size_t WXUNUSED(item) ) const
1171{
1172 return -1;
1173}
1174
e2ac737f
VZ
1175wxSize wxOwnerDrawnComboBox::DoGetBestSize() const
1176{
07fb285c
VS
1177 if ( GetCount() == 0 )
1178 return wxComboCtrlBase::DoGetBestSize();
1179
1180 wxOwnerDrawnComboBox* odc = const_cast<wxOwnerDrawnComboBox*>(this);
1181 // TODO: this class may also have GetHightestItemHeight() and
1182 // GetHightestItem() methods, and so set the whole (edit part + arrow)
1183 // control's height according with this max height, not only max width.
1184 return GetSizeFromTextSize(odc->GetWidestItemWidth());
e2ac737f
VZ
1185}
1186
ce22ac45
RR
1187void wxOwnerDrawnComboBox::OnDrawBackground(wxDC& dc,
1188 const wxRect& rect,
1189 int WXUNUSED(item),
1190 int flags) const
40b26d75 1191{
ce22ac45
RR
1192 // We need only to explicitly draw background for items
1193 // that should have selected background. Also, call PrepareBackground
1194 // always when painting the control so that clipping is done properly.
1195
1196 if ( (flags & wxODCB_PAINTING_SELECTED) ||
1197 ((flags & wxODCB_PAINTING_CONTROL) && HasFlag(wxCB_READONLY)) )
40b26d75 1198 {
118f5fbd 1199 int bgFlags = wxCONTROL_SELECTED;
4dd31ff5 1200
ce22ac45 1201 if ( !(flags & wxODCB_PAINTING_CONTROL) )
118f5fbd 1202 bgFlags |= wxCONTROL_ISSUBMENU;
ce22ac45
RR
1203
1204 PrepareBackground(dc, rect, bgFlags);
40b26d75 1205 }
40b26d75
WS
1206}
1207
a57d600f 1208#endif // wxUSE_ODCOMBOBOX