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