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 wxVListBoxComboPopup::wxVListBoxComboPopup(wxComboControlBase
* combo
)
68 m_clientDataItemsType
= wxClientData_None
;
71 bool wxVListBoxComboPopup::Create(wxWindow
* parent
)
73 if ( !wxVListBox::Create(parent
,
77 wxBORDER_SIMPLE
| wxLB_INT_HEIGHT
| wxWANTS_CHARS
) )
80 wxASSERT( GetParent()->GetParent() );
81 SetFont( GetParent()->GetParent()->GetFont() );
83 wxVListBox::SetItemCount(m_strings
.GetCount());
85 // TODO: Move this to SetFont
86 m_itemHeight
= GetCharHeight() + 0;
91 wxVListBoxComboPopup::~wxVListBoxComboPopup()
96 bool wxVListBoxComboPopup::LazyCreate()
98 // NB: There is a bug with wxVListBox that can be avoided by creating
99 // it later (bug causes empty space to be shown if initial selection
100 // is at the end of a list longer than the control can show at once).
104 // paint the control itself
105 void wxVListBoxComboPopup::PaintComboControl( wxDC
& dc
, const wxRect
& rect
)
107 if ( !(m_combo
->GetWindowStyle() & wxODCB_STD_CONTROL_PAINT
) )
109 m_combo
->DrawFocusBackground(dc
,rect
,0);
112 if ( m_combo
->OnDrawListItem(dc
,rect
,m_value
,wxCC_PAINTING_CONTROL
) )
117 wxComboPopup::PaintComboControl(dc
,rect
);
120 void wxVListBoxComboPopup::OnDrawItem(wxDC
& dc
, const wxRect
& rect
, size_t n
) const
122 dc
.SetFont( m_font
);
124 bool isHilited
= GetSelection() == (int) n
;
126 // Set correct text colour for selected items
127 // (must always set the correct colour - atleast GTK may have lost it
128 // in between calls).
130 dc
.SetTextForeground( wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT
) );
132 dc
.SetTextForeground( wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT
) );
134 if ( !m_combo
->OnDrawListItem(dc
,rect
,(int)n
,0) )
135 dc
.DrawText( GetString(n
), rect
.x
+ 2, rect
.y
);
138 wxCoord
wxVListBoxComboPopup::OnMeasureItem(size_t n
) const
140 int itemHeight
= m_combo
->OnMeasureListItem(n
);
141 if ( itemHeight
< 0 )
142 itemHeight
= m_itemHeight
;
147 void wxVListBoxComboPopup::OnDrawBackground(wxDC
& dc
, const wxRect
& rect
, size_t n
) const
149 // we need to render selected and current items differently
152 m_combo
->DrawFocusBackground( dc
, rect
, wxCONTROL_ISSUBMENU
|wxCONTROL_SELECTED
);
154 //else: do nothing for the normal items
157 void wxVListBoxComboPopup::SendComboBoxEvent()
159 wxCommandEvent
evt(wxEVT_COMMAND_COMBOBOX_SELECTED
,m_combo
->GetId());
160 int selection
= m_value
;
162 evt
.SetEventObject(m_combo
);
163 evt
.SetInt(selection
);
165 // Set client data, if any
166 if ( selection
>= 0 && (int)m_clientDatas
.GetCount() > selection
)
168 void* clientData
= m_clientDatas
[selection
];
169 if ( m_clientDataItemsType
== wxClientData_Object
)
170 evt
.SetClientObject((wxClientData
*)clientData
);
172 evt
.SetClientData(clientData
);
175 m_combo
->GetEventHandler()->AddPendingEvent(evt
);
178 // returns true if key was consumed
179 bool wxVListBoxComboPopup::HandleKey( int keycode
, bool saturate
)
182 int itemCount
= GetCount();
184 if ( keycode
== WXK_DOWN
|| keycode
== WXK_RIGHT
)
188 else if ( keycode
== WXK_UP
|| keycode
== WXK_LEFT
)
192 else if ( keycode
== WXK_PAGEDOWN
)
196 else if ( keycode
== WXK_PAGEUP
)
201 else if ( keycode == WXK_END )
205 else if ( keycode == WXK_HOME )
215 if ( value
>= itemCount
)
216 value
= itemCount
- 1;
217 else if ( value
< 0 )
222 if ( value
>= itemCount
)
224 else if ( value
< 0 )
228 if ( value
== m_value
)
229 // Even if value was same, don't skip the event
230 // (good for consistency)
237 m_combo
->SetValue(m_strings
[value
]);
244 void wxVListBoxComboPopup::OnComboDoubleClick()
246 // Cycle on dclick (disable saturation to allow true cycling).
247 if ( !::wxGetKeyState(WXK_SHIFT
) )
248 HandleKey(WXK_DOWN
,false);
250 HandleKey(WXK_UP
,false);
253 void wxVListBoxComboPopup::OnComboKeyEvent( wxKeyEvent
& event
)
255 // Saturated key movement on
256 if ( !HandleKey(event
.GetKeyCode(),true) )
260 void wxVListBoxComboPopup::OnPopup()
262 // *must* set value after size is set (this is because of a vlbox bug)
263 wxVListBox::SetSelection(m_value
);
266 void wxVListBoxComboPopup::OnMouseMove(wxMouseEvent
& event
)
268 // Move selection to cursor if it is inside the popup
269 int itemHere
= GetItemAtPosition(event
.GetPosition());
271 wxVListBox::SetSelection(itemHere
);
276 void wxVListBoxComboPopup::OnLeftClick(wxMouseEvent
& WXUNUSED(event
))
278 m_value
= wxVListBox::GetSelection();
283 void wxVListBoxComboPopup::OnKey(wxKeyEvent
& event
)
285 // Select item if ENTER is pressed
286 if ( event
.GetKeyCode() == WXK_RETURN
|| event
.GetKeyCode() == WXK_NUMPAD_ENTER
)
288 m_value
= wxVListBox::GetSelection();
292 // Hide popup if ESC is pressed
293 else if ( event
.GetKeyCode() == WXK_ESCAPE
)
299 void wxVListBoxComboPopup::CheckWidth( int pos
)
301 wxCoord x
= m_combo
->OnMeasureListItemWidth(pos
);
306 m_font
= m_combo
->GetFont();
309 m_combo
->GetTextExtent(m_strings
[pos
], &x
, &y
, 0, 0, &m_font
);
313 if ( m_widestWidth
< x
)
319 void wxVListBoxComboPopup::Insert( const wxString
& item
, int pos
)
321 // Need to change selection?
323 if ( !(m_combo
->GetWindowStyle() & wxCB_READONLY
) &&
324 m_combo
->GetValue() == item
)
327 m_strings
.Insert(item
,pos
);
330 wxVListBox::SetItemCount( wxVListBox::GetItemCount()+1 );
336 int wxVListBoxComboPopup::Append(const wxString
& item
)
338 int pos
= (int)m_strings
.GetCount();
340 if ( m_combo
->GetWindowStyle() & wxCB_SORT
)
343 // TODO: Could be optimized with binary search
344 wxArrayString strings
= m_strings
;
347 for ( i
=0; i
<strings
.GetCount(); i
++ )
349 if ( item
.Cmp(strings
.Item(i
)) < 0 )
362 void wxVListBoxComboPopup::Clear()
371 wxVListBox::SetItemCount(0);
374 void wxVListBoxComboPopup::ClearClientDatas()
376 if ( m_clientDataItemsType
== wxClientData_Object
)
379 for ( i
=0; i
<m_clientDatas
.GetCount(); i
++ )
380 delete (wxClientData
*) m_clientDatas
[i
];
383 m_clientDatas
.Empty();
386 void wxVListBoxComboPopup::SetItemClientData( unsigned int n
,
388 wxClientDataType clientDataItemsType
)
390 // It should be sufficient to update this variable only here
391 m_clientDataItemsType
= clientDataItemsType
;
393 m_clientDatas
.SetCount(n
+1,NULL
);
394 m_clientDatas
[n
] = clientData
;
397 void* wxVListBoxComboPopup::GetItemClientData(unsigned int n
) const
399 if ( m_clientDatas
.GetCount() > n
)
400 return m_clientDatas
[n
];
405 void wxVListBoxComboPopup::Delete( unsigned int item
)
407 // Remove client data, if set
408 if ( m_clientDatas
.GetCount() )
410 if ( m_clientDataItemsType
== wxClientData_Object
)
411 delete (wxClientData
*) m_clientDatas
[item
];
413 m_clientDatas
.RemoveAt(item
);
416 m_strings
.RemoveAt(item
);
419 wxVListBox::SetItemCount( wxVListBox::GetItemCount()-1 );
422 int wxVListBoxComboPopup::FindString(const wxString
& s
) const
424 return m_strings
.Index(s
);
427 unsigned int wxVListBoxComboPopup::GetCount() const
429 return m_strings
.GetCount();
432 wxString
wxVListBoxComboPopup::GetString( int item
) const
434 return m_strings
[item
];
437 void wxVListBoxComboPopup::SetString( int item
, const wxString
& str
)
439 m_strings
[item
] = str
;
442 wxString
wxVListBoxComboPopup::GetStringValue() const
445 return m_strings
[m_value
];
446 return wxEmptyString
;
449 void wxVListBoxComboPopup::SetSelection( int item
)
451 // This seems to be necessary (2.5.3 w/ MingW atleast)
452 if ( item
< -1 || item
>= (int)m_strings
.GetCount() )
458 wxVListBox::SetSelection(item
);
461 void wxVListBoxComboPopup::SetStringValue( const wxString
& value
)
463 int index
= m_strings
.Index(value
);
467 if ( index
>= -1 && index
< (int)wxVListBox::GetItemCount() )
468 wxVListBox::SetSelection(index
);
471 wxSize
wxVListBoxComboPopup::GetAdjustedSize( int minWidth
, int prefHeight
, int maxHeight
)
475 if ( m_strings
.GetCount() )
477 if ( prefHeight
> 0 )
480 if ( height
> maxHeight
)
483 int totalHeight
= GetTotalHeight(); // + 3;
484 if ( height
>= totalHeight
)
486 height
= totalHeight
;
490 // Adjust height to a multiple of the height of the first item
491 // NB: Calculations that take variable height into account
493 int fih
= GetLineHeight(0);
494 int shown
= height
/fih
;
495 height
= shown
* fih
;
501 // Take scrollbar into account in width calculations
502 int widestWidth
= m_widestWidth
+ wxSystemSettings::GetMetric(wxSYS_VSCROLL_X
);
503 return wxSize(minWidth
> widestWidth
? minWidth
: widestWidth
,
507 void wxVListBoxComboPopup::Populate( int n
, const wxString choices
[] )
511 for ( i
=0; i
<n
; i
++ )
513 const wxString
& item
= choices
[i
];
519 wxVListBox::SetItemCount(n
);
521 // Sort the initial choices
522 if ( m_combo
->GetWindowStyle() & wxCB_SORT
)
525 // Find initial selection
526 wxString strValue
= m_combo
->GetValue();
527 if ( strValue
.Length() )
528 m_value
= m_strings
.Index(strValue
);
531 // ----------------------------------------------------------------------------
532 // wxOwnerDrawnComboBox
533 // ----------------------------------------------------------------------------
536 BEGIN_EVENT_TABLE(wxOwnerDrawnComboBox
, wxComboControl
)
540 IMPLEMENT_DYNAMIC_CLASS2(wxOwnerDrawnComboBox
, wxComboControl
, wxControlWithItems
)
542 void wxOwnerDrawnComboBox::Init()
546 bool wxOwnerDrawnComboBox::Create(wxWindow
*parent
,
548 const wxString
& value
,
552 const wxValidator
& validator
,
553 const wxString
& name
)
555 return wxComboControl::Create(parent
,id
,value
,pos
,size
,style
,validator
,name
);
558 wxOwnerDrawnComboBox::wxOwnerDrawnComboBox(wxWindow
*parent
,
560 const wxString
& value
,
563 const wxArrayString
& choices
,
565 const wxValidator
& validator
,
566 const wxString
& name
)
571 Create(parent
,id
,value
,pos
,size
,choices
,style
, validator
, name
);
574 bool wxOwnerDrawnComboBox::Create(wxWindow
*parent
,
576 const wxString
& value
,
579 const wxArrayString
& choices
,
581 const wxValidator
& validator
,
582 const wxString
& name
)
584 wxCArrayString
chs(choices
);
586 return Create(parent
, id
, value
, pos
, size
, chs
.GetCount(),
587 chs
.GetStrings(), style
, validator
, name
);
590 bool wxOwnerDrawnComboBox::Create(wxWindow
*parent
,
592 const wxString
& value
,
596 const wxString choices
[],
598 const wxValidator
& validator
,
599 const wxString
& name
)
602 if ( !Create(parent
, id
, value
, pos
, size
, style
,
608 wxVListBoxComboPopup
* iface
= new wxVListBoxComboPopup(this);
609 SetPopupControl(iface
);
611 // m_popupInterface has been overridden as wxVListBoxComboPopup
612 m_popupInterface
= iface
;
614 // Add initial choices to the wxVListBox
615 iface
->Populate(n
,choices
);
620 wxOwnerDrawnComboBox::~wxOwnerDrawnComboBox()
622 if ( m_popupInterface
)
623 m_popupInterface
->ClearClientDatas();
626 // ----------------------------------------------------------------------------
627 // wxOwnerDrawnComboBox item manipulation methods
628 // ----------------------------------------------------------------------------
630 void wxOwnerDrawnComboBox::Clear()
632 wxASSERT( m_popupInterface
);
634 m_popupInterface
->Clear();
636 GetTextCtrl()->SetValue(wxEmptyString
);
639 void wxOwnerDrawnComboBox::Delete(unsigned int n
)
641 wxCHECK_RET( (n
>= 0) && (n
< GetCount()), _T("invalid index in wxOwnerDrawnComboBox::Delete") );
643 if ( GetSelection() == (int) n
)
644 SetValue(wxEmptyString
);
646 m_popupInterface
->Delete(n
);
649 unsigned int wxOwnerDrawnComboBox::GetCount() const
651 wxASSERT( m_popupInterface
);
652 return m_popupInterface
->GetCount();
655 wxString
wxOwnerDrawnComboBox::GetString(unsigned int n
) const
657 wxCHECK_MSG( (n
>= 0) && (n
< GetCount()), wxEmptyString
, _T("invalid index in wxOwnerDrawnComboBox::GetString") );
658 return m_popupInterface
->GetString(n
);
661 void wxOwnerDrawnComboBox::SetString(unsigned int n
, const wxString
& s
)
663 wxCHECK_RET( (n
>= 0) && (n
< GetCount()), _T("invalid index in wxOwnerDrawnComboBox::SetString") );
664 m_popupInterface
->SetString(n
,s
);
667 int wxOwnerDrawnComboBox::FindString(const wxString
& s
) const
669 wxASSERT( m_popupInterface
);
670 return m_popupInterface
->FindString(s
);
673 void wxOwnerDrawnComboBox::Select(int n
)
675 wxCHECK_RET( (n
>= -1) && (n
< (int)GetCount()), _T("invalid index in wxOwnerDrawnComboBox::Select") );
676 wxASSERT( m_popupInterface
);
678 m_popupInterface
->SetSelection(n
);
682 str
= m_popupInterface
->GetString(n
);
684 // Refresh text portion in control
686 m_text
->SetValue( str
);
693 int wxOwnerDrawnComboBox::GetSelection() const
695 wxASSERT( m_popupInterface
);
696 return m_popupInterface
->GetSelection();
699 int wxOwnerDrawnComboBox::DoAppend(const wxString
& item
)
701 wxASSERT( m_popupInterface
);
702 return m_popupInterface
->Append(item
);
705 int wxOwnerDrawnComboBox::DoInsert(const wxString
& item
, unsigned int pos
)
707 wxCHECK_MSG(!(GetWindowStyle() & wxCB_SORT
), -1, wxT("can't insert into sorted list"));
708 wxCHECK_MSG((pos
>=0) && (pos
<=GetCount()), -1, wxT("invalid index"));
710 m_popupInterface
->Insert(item
,pos
);
715 void wxOwnerDrawnComboBox::DoSetItemClientData(unsigned int n
, void* clientData
)
717 wxASSERT(m_popupInterface
);
718 m_popupInterface
->SetItemClientData(n
,clientData
,m_clientDataItemsType
);
721 void* wxOwnerDrawnComboBox::DoGetItemClientData(unsigned int n
) const
723 wxASSERT(m_popupInterface
);
724 return m_popupInterface
->GetItemClientData(n
);
727 void wxOwnerDrawnComboBox::DoSetItemClientObject(unsigned int n
, wxClientData
* clientData
)
729 DoSetItemClientData(n
, (void*) clientData
);
732 wxClientData
* wxOwnerDrawnComboBox::DoGetItemClientObject(unsigned int n
) const
734 return (wxClientData
*) DoGetItemClientData(n
);
737 #endif // wxUSE_OWNERDRAWNCOMBOBOX