1 /////////////////////////////////////////////////////////////////////////////
3 // Purpose: wxOwnerDrawnComboBox, wxVListBoxComboPopup
4 // Author: Jaakko Salli
6 // Created: Apr-30-2006
8 // Copyright: (c) 2005 Jaakko Salli
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
12 // ============================================================================
14 // ============================================================================
16 // ----------------------------------------------------------------------------
18 // ----------------------------------------------------------------------------
20 #include "wx/wxprec.h"
26 #if wxUSE_OWNERDRAWNCOMBOBOX
30 #include "wx/combobox.h"
31 #include "wx/dcclient.h"
32 #include "wx/settings.h"
33 #include "wx/dialog.h"
37 #include "wx/odcombo.h"
40 // ============================================================================
42 // ============================================================================
45 // ----------------------------------------------------------------------------
46 // wxVListBoxComboPopup is a wxVListBox customized to act as a popup control
48 // ----------------------------------------------------------------------------
51 BEGIN_EVENT_TABLE(wxVListBoxComboPopup
, wxVListBox
)
52 EVT_MOTION(wxVListBoxComboPopup
::OnMouseMove
)
53 EVT_KEY_DOWN(wxVListBoxComboPopup
::OnKey
)
54 EVT_LEFT_UP(wxVListBoxComboPopup
::OnLeftClick
)
58 void wxVListBoxComboPopup
::Init()
66 m_clientDataItemsType
= wxClientData_None
;
69 bool wxVListBoxComboPopup
::Create(wxWindow
* parent
)
71 if ( !wxVListBox
::Create(parent
,
75 wxBORDER_SIMPLE
| wxLB_INT_HEIGHT
| wxWANTS_CHARS
) )
78 m_useFont
= m_combo
->GetFont();
80 wxVListBox
::SetItemCount(m_strings
.GetCount());
82 // TODO: Move this to SetFont
83 m_itemHeight
= GetCharHeight() + 0;
88 wxVListBoxComboPopup
::~wxVListBoxComboPopup()
93 bool wxVListBoxComboPopup
::LazyCreate()
95 // NB: There is a bug with wxVListBox that can be avoided by creating
96 // it later (bug causes empty space to be shown if initial selection
97 // is at the end of a list longer than the control can show at once).
101 // paint the control itself
102 void wxVListBoxComboPopup
::PaintComboControl( wxDC
& dc
, const wxRect
& rect
)
104 if ( !(m_combo
->GetWindowStyle() & wxODCB_STD_CONTROL_PAINT
) )
106 m_combo
->DrawFocusBackground(dc
,rect
,0);
109 OnDrawItem(dc
,rect
,m_value
,wxCP_PAINTING_CONTROL
);
114 wxComboPopup
::PaintComboControl(dc
,rect
);
117 void wxVListBoxComboPopup
::OnDrawItem(wxDC
& dc
, const wxRect
& rect
, size_t n
) const
119 // TODO: Maybe this code could be moved to wxVListBox::OnPaint?
120 dc
.SetFont(m_useFont
);
122 // Set correct text colour for selected items
123 if ( wxVListBox
::GetSelection() == (int) n
)
124 dc
.SetTextForeground( wxSystemSettings
::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT
) );
126 dc
.SetTextForeground( wxSystemSettings
::GetColour(wxSYS_COLOUR_WINDOWTEXT
) );
128 OnDrawItem(dc
,rect
,(int)n
,0);
131 wxCoord wxVListBoxComboPopup
::OnMeasureItem(size_t WXUNUSED(n
)) const
136 wxCoord wxVListBoxComboPopup
::OnMeasureItemWidth(size_t WXUNUSED(n
)) const
138 //return OnMeasureListItemWidth(n);
142 void wxVListBoxComboPopup
::OnDrawBackground(wxDC
& dc
, const wxRect
& rect
, size_t n
) const
144 // we need to render selected and current items differently
147 m_combo
->DrawFocusBackground( dc
, rect
, wxCONTROL_ISSUBMENU
|wxCONTROL_SELECTED
);
149 //else: do nothing for the normal items
152 // This is called from wxVListBoxComboPopup::OnDrawItem, with text colour and font prepared
153 void wxVListBoxComboPopup
::OnDrawItem( wxDC
& dc
, const wxRect
& rect
, int item
, int flags
) const
155 if ( flags
& wxCP_PAINTING_CONTROL
)
157 dc
.DrawText( m_combo
->GetValue(),
158 rect
.x
+ m_combo
->GetTextIndent(),
159 (rect
.height
-dc
.GetCharHeight())/2 + rect
.y
);
163 dc
.DrawText( GetString(item
), rect
.x
+ 2, rect
.y
);
167 void wxVListBoxComboPopup
::DismissWithEvent()
169 int selection
= wxVListBox
::GetSelection();
174 if ( selection
!= wxNOT_FOUND
)
175 valStr
= m_strings
[selection
];
177 valStr
= wxEmptyString
;
181 if ( valStr
!= m_combo
->GetValue() )
182 m_combo
->SetValue(valStr
);
184 SendComboBoxEvent(selection
);
187 void wxVListBoxComboPopup
::SendComboBoxEvent( int selection
)
189 wxCommandEvent
evt(wxEVT_COMMAND_COMBOBOX_SELECTED
,m_combo
->GetId());
191 evt
.SetEventObject(m_combo
);
193 evt
.SetInt(selection
);
195 // Set client data, if any
196 if ( selection
>= 0 && (int)m_clientDatas
.GetCount() > selection
)
198 void* clientData
= m_clientDatas
[selection
];
199 if ( m_clientDataItemsType
== wxClientData_Object
)
200 evt
.SetClientObject((wxClientData
*)clientData
);
202 evt
.SetClientData(clientData
);
205 m_combo
->GetEventHandler()->AddPendingEvent(evt
);
208 // returns true if key was consumed
209 bool wxVListBoxComboPopup
::HandleKey( int keycode
, bool saturate
)
212 int itemCount
= GetCount();
214 if ( keycode
== WXK_DOWN
|| keycode
== WXK_RIGHT
)
218 else if ( keycode
== WXK_UP
|| keycode
== WXK_LEFT
)
222 else if ( keycode
== WXK_PAGEDOWN
)
226 else if ( keycode
== WXK_PAGEUP
)
235 if ( value
>= itemCount
)
236 value
= itemCount
- 1;
237 else if ( value
< 0 )
242 if ( value
>= itemCount
)
244 else if ( value
< 0 )
248 if ( value
== m_value
)
249 // Even if value was same, don't skip the event
250 // (good for consistency)
256 m_combo
->SetValue(m_strings
[value
]);
258 SendComboBoxEvent(m_value
);
263 void wxVListBoxComboPopup
::OnComboDoubleClick()
265 // Cycle on dclick (disable saturation to allow true cycling).
266 if ( !::wxGetKeyState(WXK_SHIFT
) )
267 HandleKey(WXK_DOWN
,false);
269 HandleKey(WXK_UP
,false);
272 void wxVListBoxComboPopup
::OnComboKeyEvent( wxKeyEvent
& event
)
274 // Saturated key movement on
275 if ( !HandleKey(event
.GetKeyCode(),true) )
279 void wxVListBoxComboPopup
::OnPopup()
281 // *must* set value after size is set (this is because of a vlbox bug)
282 wxVListBox
::SetSelection(m_value
);
285 void wxVListBoxComboPopup
::OnMouseMove(wxMouseEvent
& event
)
287 // Move selection to cursor if it is inside the popup
288 int itemHere
= GetItemAtPosition(event
.GetPosition());
290 wxVListBox
::SetSelection(itemHere
);
295 void wxVListBoxComboPopup
::OnLeftClick(wxMouseEvent
& WXUNUSED(event
))
300 void wxVListBoxComboPopup
::OnKey(wxKeyEvent
& event
)
302 // Select item if ENTER is pressed
303 if ( event
.GetKeyCode() == WXK_RETURN
|| event
.GetKeyCode() == WXK_NUMPAD_ENTER
)
307 // Hide popup if ESC is pressed
308 else if ( event
.GetKeyCode() == WXK_ESCAPE
)
314 void wxVListBoxComboPopup
::CheckWidth( int pos
)
316 wxCoord x
= OnMeasureItemWidth(pos
);
320 if ( !m_useFont
.Ok() )
321 m_useFont
= m_combo
->GetFont();
324 m_combo
->GetTextExtent(m_strings
[pos
], &x
, &y
, 0, 0, &m_useFont
);
328 if ( m_widestWidth
< x
)
334 void wxVListBoxComboPopup
::Insert( const wxString
& item
, int pos
)
336 // Need to change selection?
338 if ( !(m_combo
->GetWindowStyle() & wxCB_READONLY
) &&
339 m_combo
->GetValue() == item
)
344 m_strings
.Insert(item
,pos
);
347 wxVListBox
::SetItemCount( wxVListBox
::GetItemCount()+1 );
353 int wxVListBoxComboPopup
::Append(const wxString
& item
)
355 int pos
= (int)m_strings
.GetCount();
357 if ( m_combo
->GetWindowStyle() & wxCB_SORT
)
360 // TODO: Could be optimized with binary search
361 wxArrayString strings
= m_strings
;
364 for ( i
=0; i
<strings
.GetCount(); i
++ )
366 if ( item
.Cmp(strings
.Item(i
)) < 0 )
379 void wxVListBoxComboPopup
::Clear()
388 wxVListBox
::SetItemCount(0);
391 void wxVListBoxComboPopup
::ClearClientDatas()
393 if ( m_clientDataItemsType
== wxClientData_Object
)
396 for ( i
=0; i
<m_clientDatas
.GetCount(); i
++ )
397 delete (wxClientData
*) m_clientDatas
[i
];
400 m_clientDatas
.Empty();
403 void wxVListBoxComboPopup
::SetItemClientData( unsigned int n
,
405 wxClientDataType clientDataItemsType
)
407 // It should be sufficient to update this variable only here
408 m_clientDataItemsType
= clientDataItemsType
;
410 m_clientDatas
.SetCount(n
+1,NULL
);
411 m_clientDatas
[n
] = clientData
;
414 void* wxVListBoxComboPopup
::GetItemClientData(unsigned int n
) const
416 if ( m_clientDatas
.GetCount() > n
)
417 return m_clientDatas
[n
];
422 void wxVListBoxComboPopup
::Delete( unsigned int item
)
424 // Remove client data, if set
425 if ( m_clientDatas
.GetCount() )
427 if ( m_clientDataItemsType
== wxClientData_Object
)
428 delete (wxClientData
*) m_clientDatas
[item
];
430 m_clientDatas
.RemoveAt(item
);
433 m_strings
.RemoveAt(item
);
436 wxVListBox
::SetItemCount( wxVListBox
::GetItemCount()-1 );
439 int wxVListBoxComboPopup
::FindString(const wxString
& s
) const
441 return m_strings
.Index(s
);
444 unsigned int wxVListBoxComboPopup
::GetCount() const
446 return m_strings
.GetCount();
449 wxString wxVListBoxComboPopup
::GetString( int item
) const
451 return m_strings
[item
];
454 void wxVListBoxComboPopup
::SetString( int item
, const wxString
& str
)
456 m_strings
[item
] = str
;
459 wxString wxVListBoxComboPopup
::GetStringValue() const
462 return m_strings
[m_value
];
463 return wxEmptyString
;
466 void wxVListBoxComboPopup
::SetSelection( int item
)
468 // This seems to be necessary (2.5.3 w/ MingW atleast)
469 if ( item
< -1 || item
>= (int)m_strings
.GetCount() )
475 wxVListBox
::SetSelection(item
);
478 int wxVListBoxComboPopup
::GetSelection() const
483 void wxVListBoxComboPopup
::SetStringValue( const wxString
& value
)
485 int index
= m_strings
.Index(value
);
489 if ( index
>= -1 && index
< (int)wxVListBox
::GetItemCount() )
490 wxVListBox
::SetSelection(index
);
493 wxSize wxVListBoxComboPopup
::GetAdjustedSize( int minWidth
, int prefHeight
, int maxHeight
)
497 if ( m_strings
.GetCount() )
499 if ( prefHeight
> 0 )
502 if ( height
> maxHeight
)
505 int totalHeight
= GetTotalHeight(); // + 3;
506 if ( height
>= totalHeight
)
508 height
= totalHeight
;
512 // Adjust height to a multiple of the height of the first item
513 // NB: Calculations that take variable height into account
515 int fih
= GetLineHeight(0);
516 int shown
= height
/fih
;
517 height
= shown
* fih
;
523 // Take scrollbar into account in width calculations
524 int widestWidth
= m_widestWidth
+ wxSystemSettings
::GetMetric(wxSYS_VSCROLL_X
);
525 return wxSize(minWidth
> widestWidth ? minWidth
: widestWidth
,
529 //void wxVListBoxComboPopup::Populate( int n, const wxString choices[] )
530 void wxVListBoxComboPopup
::Populate( const wxArrayString
& choices
)
534 int n
= choices
.GetCount();
536 for ( i
=0; i
<n
; i
++ )
538 const wxString
& item
= choices
.Item(i
);
544 wxVListBox
::SetItemCount(n
);
546 // Sort the initial choices
547 if ( m_combo
->GetWindowStyle() & wxCB_SORT
)
550 // Find initial selection
551 wxString strValue
= m_combo
->GetValue();
552 if ( strValue
.Length() )
553 m_value
= m_strings
.Index(strValue
);
556 // ----------------------------------------------------------------------------
557 // wxOwnerDrawnComboBox
558 // ----------------------------------------------------------------------------
561 BEGIN_EVENT_TABLE(wxOwnerDrawnComboBox
, wxComboControl
)
565 IMPLEMENT_DYNAMIC_CLASS2(wxOwnerDrawnComboBox
, wxComboControl
, wxControlWithItems
)
567 void wxOwnerDrawnComboBox
::Init()
569 m_popupInterface
= NULL
;
572 bool wxOwnerDrawnComboBox
::Create(wxWindow
*parent
,
574 const wxString
& value
,
578 const wxValidator
& validator
,
579 const wxString
& name
)
581 return wxComboControl
::Create(parent
,id
,value
,pos
,size
,style
,validator
,name
);
584 wxOwnerDrawnComboBox
::wxOwnerDrawnComboBox(wxWindow
*parent
,
586 const wxString
& value
,
589 const wxArrayString
& choices
,
591 const wxValidator
& validator
,
592 const wxString
& name
)
597 Create(parent
,id
,value
,pos
,size
,choices
,style
, validator
, name
);
600 bool wxOwnerDrawnComboBox
::Create(wxWindow
*parent
,
602 const wxString
& value
,
605 const wxArrayString
& choices
,
607 const wxValidator
& validator
,
608 const wxString
& name
)
611 //wxCArrayString chs(choices);
613 //return Create(parent, id, value, pos, size, chs.GetCount(),
614 // chs.GetStrings(), style, validator, name);
615 return Create(parent
, id
, value
, pos
, size
, 0,
616 NULL
, style
, validator
, name
);
619 bool wxOwnerDrawnComboBox
::Create(wxWindow
*parent
,
621 const wxString
& value
,
625 const wxString choices
[],
627 const wxValidator
& validator
,
628 const wxString
& name
)
631 if ( !Create(parent
, id
, value
, pos
, size
, style
,
638 for ( i
=0; i
<n
; i
++ )
639 m_initChs
.Add(choices
[i
]);
644 wxOwnerDrawnComboBox
::~wxOwnerDrawnComboBox()
646 if ( m_popupInterface
)
647 m_popupInterface
->ClearClientDatas();
650 void wxOwnerDrawnComboBox
::SetPopupControl( wxComboPopup
* popup
)
654 popup
= new wxVListBoxComboPopup();
657 wxComboControl
::SetPopupControl(popup
);
660 m_popupInterface
= (wxVListBoxComboPopup
*) popup
;
662 // Add initial choices to the wxVListBox
663 if ( !m_popupInterface
->GetCount() )
665 //m_popupInterface->Populate(m_initChs.GetCount(),m_initChs.GetStrings());
666 m_popupInterface
->Populate(m_initChs
);
671 // ----------------------------------------------------------------------------
672 // wxOwnerDrawnComboBox item manipulation methods
673 // ----------------------------------------------------------------------------
675 void wxOwnerDrawnComboBox
::Clear()
677 EnsurePopupControl();
679 m_popupInterface
->Clear();
681 GetTextCtrl()->SetValue(wxEmptyString
);
684 void wxOwnerDrawnComboBox
::Delete(unsigned int n
)
686 wxCHECK_RET( n
< GetCount(), _T("invalid index in wxOwnerDrawnComboBox::Delete") );
688 if ( GetSelection() == (int) n
)
689 SetValue(wxEmptyString
);
691 m_popupInterface
->Delete(n
);
694 unsigned int wxOwnerDrawnComboBox
::GetCount() const
696 wxASSERT_MSG( m_popupInterface
, wxT("no popup interface") );
697 return m_popupInterface
->GetCount();
700 wxString wxOwnerDrawnComboBox
::GetString(unsigned int n
) const
702 wxCHECK_MSG( n
< GetCount(), wxEmptyString
, _T("invalid index in wxOwnerDrawnComboBox::GetString") );
703 return m_popupInterface
->GetString(n
);
706 void wxOwnerDrawnComboBox
::SetString(unsigned int n
, const wxString
& s
)
708 wxCHECK_RET( n
< GetCount(), _T("invalid index in wxOwnerDrawnComboBox::SetString") );
709 m_popupInterface
->SetString(n
,s
);
712 int wxOwnerDrawnComboBox
::FindString(const wxString
& s
) const
714 wxASSERT_MSG( m_popupInterface
, wxT("no popup interface") );
715 return m_popupInterface
->FindString(s
);
718 void wxOwnerDrawnComboBox
::Select(int n
)
720 wxCHECK_RET( (n
>= -1) && (n
< (int)GetCount()), _T("invalid index in wxOwnerDrawnComboBox::Select") );
721 EnsurePopupControl();
723 m_popupInterface
->SetSelection(n
);
727 str
= m_popupInterface
->GetString(n
);
729 // Refresh text portion in control
731 m_text
->SetValue( str
);
738 int wxOwnerDrawnComboBox
::GetSelection() const
740 wxASSERT_MSG( m_popupInterface
, wxT("no popup interface") );
741 return m_popupInterface
->GetSelection();
744 int wxOwnerDrawnComboBox
::DoAppend(const wxString
& item
)
746 EnsurePopupControl();
747 wxASSERT(m_popupInterface
);
748 return m_popupInterface
->Append(item
);
751 int wxOwnerDrawnComboBox
::DoInsert(const wxString
& item
, unsigned int pos
)
753 wxCHECK_MSG(!(GetWindowStyle() & wxCB_SORT
), -1, wxT("can't insert into sorted list"));
754 wxCHECK_MSG(pos
<=GetCount(), -1, wxT("invalid index"));
756 EnsurePopupControl();
757 m_popupInterface
->Insert(item
,pos
);
762 void wxOwnerDrawnComboBox
::DoSetItemClientData(unsigned int n
, void* clientData
)
764 EnsurePopupControl();
765 m_popupInterface
->SetItemClientData(n
,clientData
,m_clientDataItemsType
);
768 void* wxOwnerDrawnComboBox
::DoGetItemClientData(unsigned int n
) const
770 wxASSERT_MSG( m_popupInterface
, wxT("no popup interface") );
771 return m_popupInterface
->GetItemClientData(n
);
774 void wxOwnerDrawnComboBox
::DoSetItemClientObject(unsigned int n
, wxClientData
* clientData
)
776 DoSetItemClientData(n
, (void*) clientData
);
779 wxClientData
* wxOwnerDrawnComboBox
::DoGetItemClientObject(unsigned int n
) const
781 return (wxClientData
*) DoGetItemClientData(n
);
784 #endif // wxUSE_OWNERDRAWNCOMBOBOX