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
53BEGIN_EVENT_TABLE(wxVListBoxComboPopup, wxVListBox)
54    EVT_MOTION(wxVListBoxComboPopup::OnMouseMove)
55    EVT_KEY_DOWN(wxVListBoxComboPopup::OnKey)
56    EVT_LEFT_UP(wxVListBoxComboPopup::OnLeftClick)
57END_EVENT_TABLE()
58
59
60void 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
73bool 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
92wxVListBoxComboPopup::~wxVListBoxComboPopup()
93{
94    Clear();
95}
96
97#ifdef __WXMSW__
98
99void 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
108bool 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
117void 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
138void 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
159wxCoord 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
172wxCoord 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
182void 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
198void 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
204void 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
214void 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
236void 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
258bool 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
384void wxVListBoxComboPopup::StopPartialCompletion()
385{
386    m_partialCompletionString = wxEmptyString;
387#if wxUSE_TIMER
388    m_partialCompletionTimer.Stop();
389#endif // wxUSE_TIMER
390}
391
392void 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
401void 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
414void 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
420void 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
448void wxVListBoxComboPopup::OnLeftClick(wxMouseEvent& WXUNUSED(event))
449{
450    DismissWithEvent();
451}
452
453void 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
489void 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
507int 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
533void 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
551void 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
563void 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
576void* wxVListBoxComboPopup::GetItemClientData(unsigned int n) const
577{
578    if ( m_clientDatas.GetCount() > n )
579        return m_clientDatas[n];
580
581    return NULL;
582}
583
584void 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
613int wxVListBoxComboPopup::FindString(const wxString& s, bool bCase) const
614{
615    return m_strings.Index(s, bCase);
616}
617
618unsigned int wxVListBoxComboPopup::GetCount() const
619{
620    return m_strings.GetCount();
621}
622
623wxString wxVListBoxComboPopup::GetString( int item ) const
624{
625    return m_strings[item];
626}
627
628void wxVListBoxComboPopup::SetString( int item, const wxString& str )
629{
630    m_strings[item] = str;
631    ItemWidthChanged(item);
632}
633
634wxString wxVListBoxComboPopup::GetStringValue() const
635{
636    if ( m_value >= 0 )
637        return m_strings[m_value];
638    return wxEmptyString;
639}
640
641void 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
652int wxVListBoxComboPopup::GetSelection() const
653{
654    return m_value;
655}
656
657void 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
668void 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
757wxSize 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[] )
802void 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
835BEGIN_EVENT_TABLE(wxOwnerDrawnComboBox, wxComboCtrl)
836END_EVENT_TABLE()
837
838
839IMPLEMENT_DYNAMIC_CLASS2(wxOwnerDrawnComboBox, wxComboCtrl, wxControlWithItems)
840
841void wxOwnerDrawnComboBox::Init()
842{
843}
844
845bool 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
857wxOwnerDrawnComboBox::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
873bool 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
892bool 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
917wxOwnerDrawnComboBox::~wxOwnerDrawnComboBox()
918{
919    if ( m_popupInterface )
920        GetVListBoxComboPopup()->ClearClientDatas();
921}
922
923void 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
946void wxOwnerDrawnComboBox::Clear()
947{
948    EnsurePopupControl();
949
950    GetVListBoxComboPopup()->Clear();
951
952    SetValue(wxEmptyString);
953}
954
955void 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
965unsigned int wxOwnerDrawnComboBox::GetCount() const
966{
967    if ( !m_popupInterface )
968        return m_initChs.GetCount();
969
970    return GetVListBoxComboPopup()->GetCount();
971}
972
973wxString 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
983void 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
992int 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
1000void 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
1021int wxOwnerDrawnComboBox::GetSelection() const
1022{
1023    if ( !m_popupInterface )
1024        return m_initChs.Index(m_valueString);
1025
1026    return GetVListBoxComboPopup()->GetSelection();
1027}
1028
1029int wxOwnerDrawnComboBox::DoAppend(const wxString& item)
1030{
1031    EnsurePopupControl();
1032    wxASSERT(m_popupInterface);
1033
1034    return GetVListBoxComboPopup()->Append(item);
1035}
1036
1037int 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
1049void wxOwnerDrawnComboBox::DoSetItemClientData(unsigned int n, void* clientData)
1050{
1051    EnsurePopupControl();
1052
1053    GetVListBoxComboPopup()->SetItemClientData(n,clientData,m_clientDataItemsType);
1054}
1055
1056void* wxOwnerDrawnComboBox::DoGetItemClientData(unsigned int n) const
1057{
1058    if ( !m_popupInterface )
1059        return NULL;
1060
1061    return GetVListBoxComboPopup()->GetItemClientData(n);
1062}
1063
1064void wxOwnerDrawnComboBox::DoSetItemClientObject(unsigned int n, wxClientData* clientData)
1065{
1066    DoSetItemClientData(n, (void*) clientData);
1067}
1068
1069wxClientData* 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
1078void 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
1095wxCoord wxOwnerDrawnComboBox::OnMeasureItem( size_t WXUNUSED(item) ) const
1096{
1097    return -1;
1098}
1099
1100wxCoord wxOwnerDrawnComboBox::OnMeasureItemWidth( size_t WXUNUSED(item) ) const
1101{
1102    return -1;
1103}
1104
1105void 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