Try to better revert to the original string value in wxBitmapComboBox::RecreateControl()
[wxWidgets.git] / src / msw / bmpcbox.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/msw/bmpcboxg.cpp
3 // Purpose: wxBitmapComboBox
4 // Author: Jaakko Salli
5 // Created: 2008-04-06
6 // RCS-ID: $Id$
7 // Copyright: (c) 2008 Jaakko Salli
8 // Licence: wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
10
11 // ============================================================================
12 // declarations
13 // ============================================================================
14
15 // ----------------------------------------------------------------------------
16 // headers
17 // ----------------------------------------------------------------------------
18
19 #include "wx/wxprec.h"
20
21 #ifdef __BORLANDC__
22 #pragma hdrstop
23 #endif
24
25 #if wxUSE_BITMAPCOMBOBOX
26
27 #include "wx/bmpcbox.h"
28
29 #ifndef WX_PRECOMP
30 #include "wx/log.h"
31 #endif
32
33 #include "wx/settings.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
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
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
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
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
119 WXDWORD wxBitmapComboBox::MSWGetStyle(long style, WXDWORD *exstyle) const
120 {
121 return wxComboBox::MSWGetStyle(style, exstyle) | CBS_OWNERDRAWFIXED | CBS_HASSTRINGS;
122 }
123
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 wxArrayString strings = GetStrings();
136
137 wxComboBox::DoClear();
138
139 HWND hwnd = GetHwnd();
140 DissociateHandle();
141 ::DestroyWindow(hwnd);
142
143 if ( !MSWCreateControl(wxT("COMBOBOX"), wxEmptyString, pos, size) )
144 return;
145
146 // initialize the controls contents
147 for ( unsigned int i = 0; i < strings.size(); i++ )
148 {
149 wxComboBox::Append(strings[i]);
150 }
151
152 // and make sure it has the same attributes as before
153 if ( m_hasFont )
154 {
155 // calling SetFont(m_font) would do nothing as the code would
156 // notice that the font didn't change, so force it to believe
157 // that it did
158 wxFont font = m_font;
159 m_font = wxNullFont;
160 SetFont(font);
161 }
162
163 if ( m_hasFgCol )
164 {
165 wxColour colFg = m_foregroundColour;
166 m_foregroundColour = wxNullColour;
167 SetForegroundColour(colFg);
168 }
169
170 if ( m_hasBgCol )
171 {
172 wxColour colBg = m_backgroundColour;
173 m_backgroundColour = wxNullColour;
174 SetBackgroundColour(colBg);
175 }
176 else
177 {
178 SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
179 }
180
181 ::SendMessage(GetHwnd(), CB_SETITEMHEIGHT, 0, MeasureItem(0));
182
183 // Revert the old string value
184 ChangeValue(value);
185 }
186
187 wxBitmapComboBox::~wxBitmapComboBox()
188 {
189 Clear();
190 }
191
192 wxSize wxBitmapComboBox::DoGetBestSize() const
193 {
194 wxSize best = wxComboBox::DoGetBestSize();
195 wxSize bitmapSize = GetBitmapSize();
196
197 wxCoord useHeightBitmap = EDIT_HEIGHT_FROM_CHAR_HEIGHT(bitmapSize.y);
198 if ( best.y < useHeightBitmap )
199 {
200 best.y = useHeightBitmap;
201 CacheBestSize(best);
202 }
203 return best;
204 }
205
206 // ----------------------------------------------------------------------------
207 // Item manipulation
208 // ----------------------------------------------------------------------------
209
210 void wxBitmapComboBox::SetItemBitmap(unsigned int n, const wxBitmap& bitmap)
211 {
212 OnAddBitmap(bitmap);
213 DoSetItemBitmap(n, bitmap);
214
215 if ( (int)n == GetSelection() )
216 Refresh();
217 }
218
219 int wxBitmapComboBox::Append(const wxString& item, const wxBitmap& bitmap)
220 {
221 OnAddBitmap(bitmap);
222 const int n = wxComboBox::Append(item);
223 if ( n != wxNOT_FOUND )
224 DoSetItemBitmap(n, bitmap);
225 return n;
226 }
227
228 int wxBitmapComboBox::Append(const wxString& item, const wxBitmap& bitmap,
229 void *clientData)
230 {
231 OnAddBitmap(bitmap);
232 const int n = wxComboBox::Append(item, clientData);
233 if ( n != wxNOT_FOUND )
234 DoSetItemBitmap(n, bitmap);
235 return n;
236 }
237
238 int wxBitmapComboBox::Append(const wxString& item, const wxBitmap& bitmap,
239 wxClientData *clientData)
240 {
241 OnAddBitmap(bitmap);
242 const int n = wxComboBox::Append(item, clientData);
243 if ( n != wxNOT_FOUND )
244 DoSetItemBitmap(n, bitmap);
245 return n;
246 }
247
248 int wxBitmapComboBox::Insert(const wxString& item,
249 const wxBitmap& bitmap,
250 unsigned int pos)
251 {
252 OnAddBitmap(bitmap);
253 const int n = wxComboBox::Insert(item, pos);
254 if ( n != wxNOT_FOUND )
255 DoSetItemBitmap(n, bitmap);
256 return n;
257 }
258
259 int wxBitmapComboBox::Insert(const wxString& item, const wxBitmap& bitmap,
260 unsigned int pos, wxClientData *clientData)
261 {
262 OnAddBitmap(bitmap);
263 const int n = wxComboBox::Insert(item, pos, clientData);
264 if ( n != wxNOT_FOUND )
265 DoSetItemBitmap(n, bitmap);
266 return n;
267 }
268
269 int wxBitmapComboBox::DoInsertItems(const wxArrayStringsAdapter & items,
270 unsigned int pos,
271 void **clientData, wxClientDataType type)
272 {
273 const unsigned int numItems = items.GetCount();
274 const unsigned int countNew = GetCount() + numItems;
275
276 wxASSERT( numItems == 1 || !HasFlag(wxCB_SORT) ); // Sanity check
277
278 m_bitmaps.Alloc(countNew);
279
280 for ( unsigned int i = 0; i < numItems; i++ )
281 {
282 m_bitmaps.Insert(new wxBitmap(wxNullBitmap), pos + i);
283 }
284
285 const int index = wxComboBox::DoInsertItems(items, pos,
286 clientData, type);
287
288 if ( index == wxNOT_FOUND )
289 {
290 for ( int i = numItems-1; i >= 0; i-- )
291 BCBDoDeleteOneItem(pos + i);
292 }
293 else if ( ((unsigned int)index) != pos )
294 {
295 // Move pre-inserted empty bitmap into correct position
296 // (usually happens when combo box has wxCB_SORT style)
297 wxBitmap* bmp = static_cast<wxBitmap*>(m_bitmaps[pos]);
298 m_bitmaps.RemoveAt(pos);
299 m_bitmaps.Insert(bmp, index);
300 }
301
302 return index;
303 }
304
305 bool wxBitmapComboBox::OnAddBitmap(const wxBitmap& bitmap)
306 {
307 if ( wxBitmapComboBoxBase::OnAddBitmap(bitmap) )
308 {
309 // Need to recreate control for a new measureitem call?
310 int prevItemHeight = ::SendMessage(GetHwnd(), CB_GETITEMHEIGHT, 0, 0);
311
312 if ( prevItemHeight != MeasureItem(0) )
313 RecreateControl();
314
315 return true;
316 }
317
318 return false;
319 }
320
321 void wxBitmapComboBox::DoClear()
322 {
323 wxComboBox::DoClear();
324 wxBitmapComboBoxBase::BCBDoClear();
325 }
326
327 void wxBitmapComboBox::DoDeleteOneItem(unsigned int n)
328 {
329 wxComboBox::DoDeleteOneItem(n);
330 wxBitmapComboBoxBase::BCBDoDeleteOneItem(n);
331 }
332
333 // ----------------------------------------------------------------------------
334 // wxBitmapComboBox event handlers and such
335 // ----------------------------------------------------------------------------
336
337 void wxBitmapComboBox::OnSize(wxSizeEvent& event)
338 {
339 // Prevent infinite looping
340 if ( !m_inResize )
341 {
342 m_inResize = true;
343 DetermineIndent();
344 m_inResize = false;
345 }
346
347 event.Skip();
348 }
349
350 // ----------------------------------------------------------------------------
351 // wxBitmapComboBox miscellaneous
352 // ----------------------------------------------------------------------------
353
354 bool wxBitmapComboBox::SetFont(const wxFont& font)
355 {
356 bool res = wxComboBox::SetFont(font);
357 UpdateInternals();
358 return res;
359 }
360
361 // ----------------------------------------------------------------------------
362 // wxBitmapComboBox item drawing and measuring
363 // ----------------------------------------------------------------------------
364
365 bool wxBitmapComboBox::MSWOnDraw(WXDRAWITEMSTRUCT *item)
366 {
367 LPDRAWITEMSTRUCT lpDrawItem = (LPDRAWITEMSTRUCT) item;
368 int pos = lpDrawItem->itemID;
369
370 // Draw default for item -1, which means 'focus rect only'
371 if ( pos == -1 )
372 return FALSE;
373
374 int flags = 0;
375 if ( lpDrawItem->itemState & ODS_COMBOBOXEDIT )
376 flags |= wxODCB_PAINTING_CONTROL;
377 if ( lpDrawItem->itemState & ODS_SELECTED )
378 flags |= wxODCB_PAINTING_SELECTED;
379
380 wxString text;
381
382 if ( flags & wxODCB_PAINTING_CONTROL )
383 {
384 text = GetValue();
385 if ( !HasFlag(wxCB_READONLY) )
386 text.clear();
387 }
388 else
389 {
390 text = GetString(pos);
391 }
392
393 wxPaintDCEx dc(this, lpDrawItem->hDC);
394 wxRect rect = wxRectFromRECT(lpDrawItem->rcItem);
395 wxBitmapComboBoxBase::DrawBackground(dc, rect, pos, flags);
396 wxBitmapComboBoxBase::DrawItem(dc, rect, pos, text, flags);
397
398 // If the item has the focus, draw focus rectangle.
399 // Commented out since regular combo box doesn't
400 // seem to do it either.
401 //if ( lpDrawItem->itemState & ODS_FOCUS )
402 // DrawFocusRect(lpDrawItem->hDC, &lpDrawItem->rcItem);
403
404 return TRUE;
405 }
406
407 bool wxBitmapComboBox::MSWOnMeasure(WXMEASUREITEMSTRUCT *item)
408 {
409 LPMEASUREITEMSTRUCT lpMeasureItem = (LPMEASUREITEMSTRUCT) item;
410 int pos = lpMeasureItem->itemID;
411
412 lpMeasureItem->itemHeight = wxBitmapComboBoxBase::MeasureItem(pos);
413
414 return TRUE;
415 }
416
417 #endif // wxUSE_BITMAPCOMBOBOX