1 /////////////////////////////////////////////////////////////////////////////
2 // Name:        src/msw/bmpcbox.cpp
3 // Purpose:     wxBitmapComboBox
4 // Author:      Jaakko Salli
5 // Created:     2008-04-06
6 // Copyright:   (c) 2008 Jaakko Salli
7 // Licence:     wxWindows licence
8 /////////////////////////////////////////////////////////////////////////////
9 
10 // ============================================================================
11 // declarations
12 // ============================================================================
13 
14 // ----------------------------------------------------------------------------
15 // headers
16 // ----------------------------------------------------------------------------
17 
18 #include "wx/wxprec.h"
19 
20 #ifdef __BORLANDC__
21     #pragma hdrstop
22 #endif
23 
24 #if wxUSE_BITMAPCOMBOBOX
25 
26 #include "wx/bmpcbox.h"
27 
28 #ifndef WX_PRECOMP
29     #include "wx/log.h"
30 #endif
31 
32 #include "wx/settings.h"
33 #include "wx/vector.h"
34 
35 #include "wx/msw/dcclient.h"
36 #include "wx/msw/private.h"
37 
38 // For wxODCB_XXX flags
39 #include "wx/odcombo.h"
40 
41 
42 #define IMAGE_SPACING_CTRL_VERTICAL 7  // Spacing used in control size calculation
43 
44 
45 // ============================================================================
46 // implementation
47 // ============================================================================
48 
49 
BEGIN_EVENT_TABLE(wxBitmapComboBox,wxComboBox)50 BEGIN_EVENT_TABLE(wxBitmapComboBox, wxComboBox)
51     EVT_SIZE(wxBitmapComboBox::OnSize)
52 END_EVENT_TABLE()
53 
54 
55 IMPLEMENT_DYNAMIC_CLASS(wxBitmapComboBox, wxComboBox)
56 
57 
58 // ----------------------------------------------------------------------------
59 // wxBitmapComboBox creation
60 // ----------------------------------------------------------------------------
61 
62 void wxBitmapComboBox::Init()
63 {
64     m_inResize = false;
65 }
66 
wxBitmapComboBox(wxWindow * parent,wxWindowID id,const wxString & value,const wxPoint & pos,const wxSize & size,const wxArrayString & choices,long style,const wxValidator & validator,const wxString & name)67 wxBitmapComboBox::wxBitmapComboBox(wxWindow *parent,
68                                   wxWindowID id,
69                                   const wxString& value,
70                                   const wxPoint& pos,
71                                   const wxSize& size,
72                                   const wxArrayString& choices,
73                                   long style,
74                                   const wxValidator& validator,
75                                   const wxString& name)
76     : wxComboBox(),
77       wxBitmapComboBoxBase()
78 {
79     Init();
80 
81     Create(parent,id,value,pos,size,choices,style,validator,name);
82 }
83 
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)84 bool wxBitmapComboBox::Create(wxWindow *parent,
85                               wxWindowID id,
86                               const wxString& value,
87                               const wxPoint& pos,
88                               const wxSize& size,
89                               const wxArrayString& choices,
90                               long style,
91                               const wxValidator& validator,
92                               const wxString& name)
93 {
94     wxCArrayString chs(choices);
95     return Create(parent, id, value, pos, size, chs.GetCount(),
96                   chs.GetStrings(), style, validator, name);
97 }
98 
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)99 bool wxBitmapComboBox::Create(wxWindow *parent,
100                               wxWindowID id,
101                               const wxString& value,
102                               const wxPoint& pos,
103                               const wxSize& size,
104                               int n,
105                               const wxString choices[],
106                               long style,
107                               const wxValidator& validator,
108                               const wxString& name)
109 {
110     if ( !wxComboBox::Create(parent, id, value, pos, size,
111                              n, choices, style, validator, name) )
112         return false;
113 
114     UpdateInternals();
115 
116     return true;
117 }
118 
MSWGetStyle(long style,WXDWORD * exstyle) const119 WXDWORD wxBitmapComboBox::MSWGetStyle(long style, WXDWORD *exstyle) const
120 {
121     return wxComboBox::MSWGetStyle(style, exstyle) | CBS_OWNERDRAWFIXED | CBS_HASSTRINGS;
122 }
123 
RecreateControl()124 void wxBitmapComboBox::RecreateControl()
125 {
126     //
127     // Recreate control so that WM_MEASUREITEM gets called again.
128     // Can't use CBS_OWNERDRAWVARIABLE because it has odd
129     // mouse-wheel behaviour.
130     //
131     wxString value = GetValue();
132     wxPoint pos = GetPosition();
133     wxSize size = GetSize();
134     size.y = GetBestSize().y;
135     const wxArrayString strings = GetStrings();
136     const unsigned numItems = strings.size();
137     unsigned i;
138 
139     // Save the client data pointers before clearing the control, if any.
140     const wxClientDataType clientDataType = GetClientDataType();
141     wxVector<wxClientData*> objectClientData;
142     wxVector<void*> voidClientData;
143     switch ( clientDataType )
144     {
145         case wxClientData_None:
146             break;
147 
148         case wxClientData_Object:
149             objectClientData.reserve(numItems);
150             for ( i = 0; i < numItems; ++i )
151                 objectClientData.push_back(GetClientObject(i));
152             break;
153 
154         case wxClientData_Void:
155             voidClientData.reserve(numItems);
156             for ( i = 0; i < numItems; ++i )
157                 voidClientData.push_back(GetClientData(i));
158             break;
159     }
160 
161     wxComboBox::DoClear();
162 
163     HWND hwnd = GetHwnd();
164     DissociateHandle();
165     ::DestroyWindow(hwnd);
166 
167     if ( !MSWCreateControl(wxT("COMBOBOX"), wxEmptyString, pos, size) )
168         return;
169 
170     // initialize the controls contents
171     for ( i = 0; i < numItems; i++ )
172     {
173         wxComboBox::Append(strings[i]);
174 
175         if ( !objectClientData.empty() )
176             SetClientObject(i, objectClientData[i]);
177         else if ( !voidClientData.empty() )
178             SetClientData(i, voidClientData[i]);
179     }
180 
181     // and make sure it has the same attributes as before
182     if ( m_hasFont )
183     {
184         // calling SetFont(m_font) would do nothing as the code would
185         // notice that the font didn't change, so force it to believe
186         // that it did
187         wxFont font = m_font;
188         m_font = wxNullFont;
189         SetFont(font);
190     }
191 
192     if ( m_hasFgCol )
193     {
194         wxColour colFg = m_foregroundColour;
195         m_foregroundColour = wxNullColour;
196         SetForegroundColour(colFg);
197     }
198 
199     if ( m_hasBgCol )
200     {
201         wxColour colBg = m_backgroundColour;
202         m_backgroundColour = wxNullColour;
203         SetBackgroundColour(colBg);
204     }
205     else
206     {
207         SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
208     }
209 
210     ::SendMessage(GetHwnd(), CB_SETITEMHEIGHT, 0, MeasureItem(0));
211 
212     // Revert the old string value
213     if ( !HasFlag(wxCB_READONLY) )
214         ChangeValue(value);
215 }
216 
~wxBitmapComboBox()217 wxBitmapComboBox::~wxBitmapComboBox()
218 {
219     Clear();
220 }
221 
DoGetBestSize() const222 wxSize wxBitmapComboBox::DoGetBestSize() const
223 {
224     wxSize best = wxComboBox::DoGetBestSize();
225     wxSize bitmapSize = GetBitmapSize();
226 
227     wxCoord useHeightBitmap = EDIT_HEIGHT_FROM_CHAR_HEIGHT(bitmapSize.y);
228     if ( best.y < useHeightBitmap )
229     {
230         best.y = useHeightBitmap;
231         CacheBestSize(best);
232     }
233     return best;
234 }
235 
236 // ----------------------------------------------------------------------------
237 // Item manipulation
238 // ----------------------------------------------------------------------------
239 
SetItemBitmap(unsigned int n,const wxBitmap & bitmap)240 void wxBitmapComboBox::SetItemBitmap(unsigned int n, const wxBitmap& bitmap)
241 {
242     OnAddBitmap(bitmap);
243     DoSetItemBitmap(n, bitmap);
244 
245     if ( (int)n == GetSelection() )
246         Refresh();
247 }
248 
Append(const wxString & item,const wxBitmap & bitmap)249 int wxBitmapComboBox::Append(const wxString& item, const wxBitmap& bitmap)
250 {
251     OnAddBitmap(bitmap);
252     const int n = wxComboBox::Append(item);
253     if ( n != wxNOT_FOUND )
254         DoSetItemBitmap(n, bitmap);
255     return n;
256 }
257 
Append(const wxString & item,const wxBitmap & bitmap,void * clientData)258 int wxBitmapComboBox::Append(const wxString& item, const wxBitmap& bitmap,
259                              void *clientData)
260 {
261     OnAddBitmap(bitmap);
262     const int n = wxComboBox::Append(item, clientData);
263     if ( n != wxNOT_FOUND )
264         DoSetItemBitmap(n, bitmap);
265     return n;
266 }
267 
Append(const wxString & item,const wxBitmap & bitmap,wxClientData * clientData)268 int wxBitmapComboBox::Append(const wxString& item, const wxBitmap& bitmap,
269                              wxClientData *clientData)
270 {
271     OnAddBitmap(bitmap);
272     const int n = wxComboBox::Append(item, clientData);
273     if ( n != wxNOT_FOUND )
274         DoSetItemBitmap(n, bitmap);
275     return n;
276 }
277 
Insert(const wxString & item,const wxBitmap & bitmap,unsigned int pos)278 int wxBitmapComboBox::Insert(const wxString& item,
279                              const wxBitmap& bitmap,
280                              unsigned int pos)
281 {
282     OnAddBitmap(bitmap);
283     const int n = wxComboBox::Insert(item, pos);
284     if ( n != wxNOT_FOUND )
285         DoSetItemBitmap(n, bitmap);
286     return n;
287 }
288 
Insert(const wxString & item,const wxBitmap & bitmap,unsigned int pos,void * clientData)289 int wxBitmapComboBox::Insert(const wxString& item, const wxBitmap& bitmap,
290                              unsigned int pos, void *clientData)
291 {
292     OnAddBitmap(bitmap);
293     const int n = wxComboBox::Insert(item, pos, clientData);
294     if ( n != wxNOT_FOUND )
295         DoSetItemBitmap(n, bitmap);
296     return n;
297 }
298 
Insert(const wxString & item,const wxBitmap & bitmap,unsigned int pos,wxClientData * clientData)299 int wxBitmapComboBox::Insert(const wxString& item, const wxBitmap& bitmap,
300                              unsigned int pos, wxClientData *clientData)
301 {
302     OnAddBitmap(bitmap);
303     const int n = wxComboBox::Insert(item, pos, clientData);
304     if ( n != wxNOT_FOUND )
305         DoSetItemBitmap(n, bitmap);
306     return n;
307 }
308 
DoInsertItems(const wxArrayStringsAdapter & items,unsigned int pos,void ** clientData,wxClientDataType type)309 int wxBitmapComboBox::DoInsertItems(const wxArrayStringsAdapter & items,
310                                     unsigned int pos,
311                                     void **clientData, wxClientDataType type)
312 {
313     const unsigned int numItems = items.GetCount();
314     const unsigned int countNew = GetCount() + numItems;
315 
316     wxASSERT( numItems == 1 || !HasFlag(wxCB_SORT) );  // Sanity check
317 
318     m_bitmaps.Alloc(countNew);
319 
320     for ( unsigned int i = 0; i < numItems; i++ )
321     {
322         m_bitmaps.Insert(new wxBitmap(wxNullBitmap), pos + i);
323     }
324 
325     const int index = wxComboBox::DoInsertItems(items, pos,
326                                                 clientData, type);
327 
328     if ( index == wxNOT_FOUND )
329     {
330         for ( int i = numItems-1; i >= 0; i-- )
331             BCBDoDeleteOneItem(pos + i);
332     }
333     else if ( ((unsigned int)index) != pos )
334     {
335         // Move pre-inserted empty bitmap into correct position
336         // (usually happens when combo box has wxCB_SORT style)
337         wxBitmap* bmp = static_cast<wxBitmap*>(m_bitmaps[pos]);
338         m_bitmaps.RemoveAt(pos);
339         m_bitmaps.Insert(bmp, index);
340     }
341 
342     return index;
343 }
344 
OnAddBitmap(const wxBitmap & bitmap)345 bool wxBitmapComboBox::OnAddBitmap(const wxBitmap& bitmap)
346 {
347     if ( wxBitmapComboBoxBase::OnAddBitmap(bitmap) )
348     {
349         // Need to recreate control for a new measureitem call?
350         int prevItemHeight = ::SendMessage(GetHwnd(), CB_GETITEMHEIGHT, 0, 0);
351 
352         if ( prevItemHeight != MeasureItem(0) )
353             RecreateControl();
354 
355         return true;
356     }
357 
358     return false;
359 }
360 
DoClear()361 void wxBitmapComboBox::DoClear()
362 {
363     wxComboBox::DoClear();
364     wxBitmapComboBoxBase::BCBDoClear();
365 }
366 
DoDeleteOneItem(unsigned int n)367 void wxBitmapComboBox::DoDeleteOneItem(unsigned int n)
368 {
369     wxComboBox::DoDeleteOneItem(n);
370     wxBitmapComboBoxBase::BCBDoDeleteOneItem(n);
371 }
372 
373 // ----------------------------------------------------------------------------
374 // wxBitmapComboBox event handlers and such
375 // ----------------------------------------------------------------------------
376 
OnSize(wxSizeEvent & event)377 void wxBitmapComboBox::OnSize(wxSizeEvent& event)
378 {
379     // Prevent infinite looping
380     if ( !m_inResize )
381     {
382         m_inResize = true;
383         DetermineIndent();
384         m_inResize = false;
385     }
386 
387     event.Skip();
388 }
389 
390 // ----------------------------------------------------------------------------
391 // wxBitmapComboBox miscellaneous
392 // ----------------------------------------------------------------------------
393 
SetFont(const wxFont & font)394 bool wxBitmapComboBox::SetFont(const wxFont& font)
395 {
396     bool res = wxComboBox::SetFont(font);
397     UpdateInternals();
398     return res;
399 }
400 
401 // ----------------------------------------------------------------------------
402 // wxBitmapComboBox item drawing and measuring
403 // ----------------------------------------------------------------------------
404 
MSWOnDraw(WXDRAWITEMSTRUCT * item)405 bool wxBitmapComboBox::MSWOnDraw(WXDRAWITEMSTRUCT *item)
406 {
407     LPDRAWITEMSTRUCT lpDrawItem = (LPDRAWITEMSTRUCT) item;
408     int pos = lpDrawItem->itemID;
409 
410     // Draw default for item -1, which means 'focus rect only'
411     if ( pos == -1 )
412         return FALSE;
413 
414     int flags = 0;
415     if ( lpDrawItem->itemState & ODS_COMBOBOXEDIT )
416         flags |= wxODCB_PAINTING_CONTROL;
417     if ( lpDrawItem->itemState & ODS_SELECTED )
418         flags |= wxODCB_PAINTING_SELECTED;
419 
420     wxString text;
421 
422     if ( flags & wxODCB_PAINTING_CONTROL )
423     {
424         text = GetValue();
425         if ( !HasFlag(wxCB_READONLY) )
426             text.clear();
427     }
428     else
429     {
430         text = GetString(pos);
431     }
432 
433     wxPaintDCEx dc(this, lpDrawItem->hDC);
434     wxRect rect = wxRectFromRECT(lpDrawItem->rcItem);
435     wxBitmapComboBoxBase::DrawBackground(dc, rect, pos, flags);
436     wxBitmapComboBoxBase::DrawItem(dc, rect, pos, text, flags);
437 
438     // If the item has the focus, draw focus rectangle.
439     // Commented out since regular combo box doesn't
440     // seem to do it either.
441     //if ( lpDrawItem->itemState & ODS_FOCUS )
442     //    DrawFocusRect(lpDrawItem->hDC, &lpDrawItem->rcItem);
443 
444     return true;
445 }
446 
MSWOnMeasure(WXMEASUREITEMSTRUCT * item)447 bool wxBitmapComboBox::MSWOnMeasure(WXMEASUREITEMSTRUCT *item)
448 {
449     LPMEASUREITEMSTRUCT lpMeasureItem = (LPMEASUREITEMSTRUCT) item;
450     int pos = lpMeasureItem->itemID;
451 
452     // Measure edit field height if item list is not empty,
453     // otherwise leave default system value.
454     if ( m_usedImgSize.y >= 0 || pos >= 0 )
455     {
456         lpMeasureItem->itemHeight = wxBitmapComboBoxBase::MeasureItem(pos);
457     }
458 
459     return true;
460 }
461 
462 #endif // wxUSE_BITMAPCOMBOBOX
463