Fix crash when getting or setting wxComboBox value in wxUniv.
[wxWidgets.git] / src / univ / combobox.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/univ/combobox.cpp
3 // Purpose: wxComboBox implementation
4 // Author: Vadim Zeitlin
5 // Modified by:
6 // Created: 15.12.00
7 // RCS-ID: $Id$
8 // Copyright: (c) 2000 SciTech Software, Inc. (www.scitechsoft.com)
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_COMBOBOX
27
28 #ifndef WX_PRECOMP
29 #include "wx/log.h"
30
31 #include "wx/button.h"
32 #include "wx/combobox.h"
33 #include "wx/listbox.h"
34 #include "wx/textctrl.h"
35 #include "wx/bmpbuttn.h"
36
37 #include "wx/validate.h"
38 #endif
39
40 #include "wx/tooltip.h"
41 #include "wx/combo.h"
42
43 #include "wx/univ/renderer.h"
44 #include "wx/univ/inphand.h"
45 #include "wx/univ/theme.h"
46
47 // ----------------------------------------------------------------------------
48 // wxStdComboBoxInputHandler: allows the user to open/close the combo from kbd
49 // ----------------------------------------------------------------------------
50
51 class WXDLLEXPORT wxStdComboBoxInputHandler : public wxStdInputHandler
52 {
53 public:
54 wxStdComboBoxInputHandler(wxInputHandler *inphand);
55
56 virtual bool HandleKey(wxInputConsumer *consumer,
57 const wxKeyEvent& event,
58 bool pressed);
59 };
60
61 // ----------------------------------------------------------------------------
62 // wxComboListBox is a listbox modified to be used as a popup window in a
63 // combobox
64 // ----------------------------------------------------------------------------
65
66 class wxComboListBox : public wxListBox, public wxComboPopup
67 {
68 public:
69 // ctor and dtor
70 wxComboListBox();
71 virtual ~wxComboListBox();
72
73 // implement wxComboPopup methods
74 virtual bool Create(wxWindow* parent);
75 virtual void SetStringValue(const wxString& s);
76 virtual wxString GetStringValue() const;
77 virtual wxWindow *GetControl() { return this; }
78 virtual void OnPopup();
79 virtual wxSize GetAdjustedSize(int minWidth, int prefHeight, int maxHeight);
80
81 // fix virtual function hiding
82 virtual void SetSelection(int n) { DoSetSelection(n, true); }
83 void SetSelection(int n, bool select) { DoSetSelection(n, select); }
84
85 // used to process wxUniv actions
86 bool PerformAction(const wxControlAction& action,
87 long numArg,
88 const wxString& strArg);
89
90 protected:
91 // set m_clicked value from here
92 void OnLeftUp(wxMouseEvent& event);
93
94 private:
95 friend class wxComboBox; // it accesses our DoGetItemClientData()
96
97 DECLARE_EVENT_TABLE()
98 };
99
100 // ----------------------------------------------------------------------------
101 // event tables and such
102 // ----------------------------------------------------------------------------
103
104 BEGIN_EVENT_TABLE(wxComboListBox, wxListBox)
105 EVT_LEFT_UP(wxComboListBox::OnLeftUp)
106 END_EVENT_TABLE()
107
108 // ============================================================================
109 // implementation
110 // ============================================================================
111
112 // ----------------------------------------------------------------------------
113 // wxComboListBox
114 // ----------------------------------------------------------------------------
115
116 wxComboListBox::wxComboListBox() : wxListBox(), wxComboPopup()
117 {
118 }
119
120 bool wxComboListBox::Create(wxWindow* parent)
121 {
122 if ( !wxListBox::Create(parent, wxID_ANY,
123 wxDefaultPosition, wxDefaultSize,
124 0, NULL,
125 wxBORDER_SIMPLE |
126 ( m_combo->GetWindowStyle() & wxCB_SORT ? wxLB_SORT : 0 ) ) )
127 return false;
128
129 // we don't react to the mouse events outside the window at all
130 StopAutoScrolling();
131
132 return true;
133 }
134
135 wxComboListBox::~wxComboListBox()
136 {
137 }
138
139 wxString wxComboListBox::GetStringValue() const
140 {
141 return wxListBox::GetStringSelection();
142 }
143
144 void wxComboListBox::SetStringValue(const wxString& value)
145 {
146 if ( !value.empty() )
147 {
148 if (FindString(value) != wxNOT_FOUND)
149 wxListBox::SetStringSelection(value);
150 }
151 else
152 wxListBox::SetSelection(-1);
153 }
154
155 void wxComboListBox::OnPopup()
156 {
157 }
158
159 bool wxComboListBox::PerformAction(const wxControlAction& action,
160 long numArg,
161 const wxString& strArg)
162
163 {
164 if ( action == wxACTION_LISTBOX_FIND )
165 {
166 // we don't let the listbox handle this as instead of just using the
167 // single key presses, as usual, we use the text ctrl value as prefix
168 // and this is done by wxComboCtrl itself
169 return true;
170 }
171
172 return wxListBox::PerformAction(action, numArg, strArg);
173 }
174
175 void wxComboListBox::OnLeftUp(wxMouseEvent& event)
176 {
177 // we should dismiss the combo now
178 // first update the combo and close the listbox
179 Dismiss();
180 m_combo->SetValue(wxListBox::GetStringSelection());
181
182 // next let the user code have the event
183 wxCommandEvent evt(wxEVT_COMMAND_COMBOBOX_SELECTED,m_combo->GetId());
184 evt.SetInt(wxListBox::GetSelection());
185 evt.SetEventObject(m_combo);
186 m_combo->ProcessWindowEvent(evt);
187
188 event.Skip();
189 }
190
191 wxSize wxComboListBox::GetAdjustedSize(int minWidth,
192 int WXUNUSED(prefHeight),
193 int maxHeight)
194 {
195 wxSize bestSize = wxListBox::GetBestSize();
196 return wxSize(wxMax(bestSize.x,minWidth),
197 wxMin(bestSize.y,maxHeight));
198 }
199
200 // ----------------------------------------------------------------------------
201 // wxComboBox
202 // ----------------------------------------------------------------------------
203
204 void wxComboBox::Init()
205 {
206 m_lbox = NULL;
207 }
208
209 wxComboBox::wxComboBox(wxWindow *parent,
210 wxWindowID id,
211 const wxString& value,
212 const wxPoint& pos,
213 const wxSize& size,
214 const wxArrayString& choices,
215 long style,
216 const wxValidator& validator,
217 const wxString& name)
218 {
219 Init();
220
221 Create(parent, id, value, pos, size, choices, style, validator, name);
222 }
223
224 bool wxComboBox::Create(wxWindow *parent,
225 wxWindowID id,
226 const wxString& value,
227 const wxPoint& pos,
228 const wxSize& size,
229 const wxArrayString& choices,
230 long style,
231 const wxValidator& validator,
232 const wxString& name)
233 {
234 wxCArrayString chs(choices);
235
236 return Create(parent, id, value, pos, size, chs.GetCount(),
237 chs.GetStrings(), style, validator, name);
238 }
239
240 bool wxComboBox::Create(wxWindow *parent,
241 wxWindowID id,
242 const wxString& value,
243 const wxPoint& pos,
244 const wxSize& size,
245 int n,
246 const wxString choices[],
247 long style,
248 const wxValidator& validator,
249 const wxString& name)
250 {
251 if ( !wxComboCtrl::Create(parent, id, value, pos, size, style,
252 validator, name) )
253 {
254 return false;
255 }
256
257 wxComboListBox *combolbox = new wxComboListBox();
258 SetPopupControl(combolbox);
259
260 m_lbox = combolbox;
261 m_lbox->Set(n, choices);
262
263 return true;
264 }
265
266 wxComboBox::~wxComboBox()
267 {
268 }
269
270 // ----------------------------------------------------------------------------
271 // wxComboBox methods forwarded to wxTextCtrl
272 // ----------------------------------------------------------------------------
273
274 wxString wxComboBox::DoGetValue() const
275 {
276 return GetTextCtrl() ? GetTextCtrl()->GetValue() : wxString();
277 }
278
279 void wxComboBox::SetValue(const wxString& value)
280 {
281 if ( GetTextCtrl() )
282 GetTextCtrl()->SetValue(value);
283 }
284
285 void wxComboBox::WriteText(const wxString& value)
286 {
287 if ( GetTextCtrl() ) GetTextCtrl()->WriteText(value);
288 }
289
290 void wxComboBox::Copy()
291 {
292 if ( GetTextCtrl() ) GetTextCtrl()->Copy();
293 }
294
295 void wxComboBox::Cut()
296 {
297 if ( GetTextCtrl() ) GetTextCtrl()->Cut();
298 }
299
300 void wxComboBox::Paste()
301 {
302 if ( GetTextCtrl() ) GetTextCtrl()->Paste();
303 }
304
305 void wxComboBox::SetInsertionPoint(long pos)
306 {
307 if ( GetTextCtrl() ) GetTextCtrl()->SetInsertionPoint(pos);
308 }
309
310 void wxComboBox::SetInsertionPointEnd()
311 {
312 if ( GetTextCtrl() ) GetTextCtrl()->SetInsertionPointEnd();
313 }
314
315 long wxComboBox::GetInsertionPoint() const
316 {
317 if ( GetTextCtrl() )
318 return GetTextCtrl()->GetInsertionPoint();
319 return -1;
320 }
321
322 wxTextPos wxComboBox::GetLastPosition() const
323 {
324 if ( GetTextCtrl() )
325 return GetTextCtrl()->GetLastPosition();
326 return -1;
327 }
328
329 void wxComboBox::Replace(long from, long to, const wxString& value)
330 {
331 if ( GetTextCtrl() ) GetTextCtrl()->Replace(from, to, value);
332 }
333
334 void wxComboBox::Remove(long from, long to)
335 {
336 if ( GetTextCtrl() ) GetTextCtrl()->Remove(from, to);
337 }
338
339 void wxComboBox::SetSelection(long from, long to)
340 {
341 if ( GetTextCtrl() ) GetTextCtrl()->SetSelection(from, to);
342 }
343
344 void wxComboBox::GetSelection(long *from, long *to) const
345 {
346 if ( GetTextCtrl() ) GetTextCtrl()->GetSelection(from, to);
347 }
348
349 void wxComboBox::SetEditable(bool editable)
350 {
351 if ( GetTextCtrl() ) GetTextCtrl()->SetEditable(editable);
352 }
353
354 // ----------------------------------------------------------------------------
355 // wxComboBox methods forwarded to wxListBox
356 // ----------------------------------------------------------------------------
357
358 void wxComboBox::DoClear()
359 {
360 GetLBox()->Clear();
361 if ( GetTextCtrl() ) GetTextCtrl()->SetValue(wxEmptyString);
362 }
363
364 void wxComboBox::DoDeleteOneItem(unsigned int n)
365 {
366 wxCHECK_RET( IsValid(n), wxT("invalid index in wxComboBox::Delete") );
367
368 if (GetSelection() == (int)n)
369 if ( GetTextCtrl() ) GetTextCtrl()->SetValue(wxEmptyString);
370
371 GetLBox()->Delete(n);
372 }
373
374 unsigned int wxComboBox::GetCount() const
375 {
376 return GetLBox()->GetCount();
377 }
378
379 wxString wxComboBox::GetString(unsigned int n) const
380 {
381 wxCHECK_MSG( IsValid(n), wxEmptyString, wxT("invalid index in wxComboBox::GetString") );
382
383 return GetLBox()->GetString(n);
384 }
385
386 void wxComboBox::SetString(unsigned int n, const wxString& s)
387 {
388 wxCHECK_RET( IsValid(n), wxT("invalid index in wxComboBox::SetString") );
389
390 GetLBox()->SetString(n, s);
391 }
392
393 int wxComboBox::FindString(const wxString& s, bool bCase) const
394 {
395 return GetLBox()->FindString(s, bCase);
396 }
397
398 void wxComboBox::SetSelection(int n)
399 {
400 wxCHECK_RET( (n == wxNOT_FOUND || IsValid(n)), wxT("invalid index in wxComboBox::Select") );
401
402 GetLBox()->SetSelection(n);
403
404 wxString str;
405 if ( n != wxNOT_FOUND )
406 str = GetLBox()->GetString(n);
407
408 SetText(str);
409 }
410
411 int wxComboBox::GetSelection() const
412 {
413 #if 1 // FIXME:: What is the correct behaviour?
414 // if the current value isn't one of the listbox strings, return -1
415 return GetLBox()->GetSelection();
416 #else
417 // Why oh why is this done this way?
418 // It is not because the value displayed in the text can be found
419 // in the list that it is the item that is selected!
420 return FindString(if ( GetTextCtrl() ) GetTextCtrl()->GetValue());
421 #endif
422 }
423
424 wxString wxComboBox::GetStringSelection() const
425 {
426 return GetLBox()->GetStringSelection();
427 }
428
429 wxClientDataType wxComboBox::GetClientDataType() const
430 {
431 return GetLBox()->GetClientDataType();
432 }
433
434 void wxComboBox::SetClientDataType(wxClientDataType clientDataItemsType)
435 {
436 GetLBox()->SetClientDataType(clientDataItemsType);
437 }
438
439 int wxComboBox::DoInsertItems(const wxArrayStringsAdapter & items,
440 unsigned int pos,
441 void **clientData, wxClientDataType type)
442 {
443 return GetLBox()->DoInsertItems(items, pos, clientData, type);
444 }
445
446 void wxComboBox::DoSetItemClientData(unsigned int n, void* clientData)
447 {
448 GetLBox()->DoSetItemClientData(n, clientData);
449 }
450
451 void *wxComboBox::DoGetItemClientData(unsigned int n) const
452 {
453 return GetLBox()->DoGetItemClientData(n);
454 }
455
456 bool wxComboBox::IsEditable() const
457 {
458 return GetTextCtrl() != NULL && (!HasFlag(wxCB_READONLY) || GetTextCtrl()->IsEditable() );
459 }
460
461 void wxComboBox::Undo()
462 {
463 if (IsEditable())
464 if ( GetTextCtrl() ) GetTextCtrl()->Undo();
465 }
466
467 void wxComboBox::Redo()
468 {
469 if (IsEditable())
470 if ( GetTextCtrl() ) GetTextCtrl()->Redo();
471 }
472
473 void wxComboBox::SelectAll()
474 {
475 if ( GetTextCtrl() ) GetTextCtrl()->SelectAll();
476 }
477
478 bool wxComboBox::CanCopy() const
479 {
480 if (GetTextCtrl() != NULL)
481 return GetTextCtrl()->CanCopy();
482 else
483 return false;
484 }
485
486 bool wxComboBox::CanCut() const
487 {
488 if (GetTextCtrl() != NULL)
489 return GetTextCtrl()->CanCut();
490 else
491 return false;
492 }
493
494 bool wxComboBox::CanPaste() const
495 {
496 if (IsEditable())
497 return GetTextCtrl()->CanPaste();
498 else
499 return false;
500 }
501
502 bool wxComboBox::CanUndo() const
503 {
504 if (IsEditable())
505 return GetTextCtrl()->CanUndo();
506 else
507 return false;
508 }
509
510 bool wxComboBox::CanRedo() const
511 {
512 if (IsEditable())
513 return GetTextCtrl()->CanRedo();
514 else
515 return false;
516 }
517
518
519 // ----------------------------------------------------------------------------
520 // wxStdComboBoxInputHandler
521 // ----------------------------------------------------------------------------
522
523 wxStdComboBoxInputHandler::wxStdComboBoxInputHandler(wxInputHandler *inphand)
524 : wxStdInputHandler(inphand)
525 {
526 }
527
528 bool wxStdComboBoxInputHandler::HandleKey(wxInputConsumer *consumer,
529 const wxKeyEvent& event,
530 bool pressed)
531 {
532 if ( pressed )
533 {
534 wxControlAction action;
535 switch ( event.GetKeyCode() )
536 {
537 case WXK_DOWN:
538 action = wxACTION_COMBOBOX_POPUP;
539 break;
540
541 case WXK_ESCAPE:
542 action = wxACTION_COMBOBOX_DISMISS;
543 break;
544 }
545
546 if ( !action.IsEmpty() )
547 {
548 consumer->PerformAction(action);
549
550 return true;
551 }
552 }
553
554 return wxStdInputHandler::HandleKey(consumer, event, pressed);
555 }
556
557 /* static */
558 wxInputHandler *wxComboBox::GetStdInputHandler(wxInputHandler *handlerDef)
559 {
560 static wxStdComboBoxInputHandler s_handler(handlerDef);
561
562 return &s_handler;
563 }
564
565 #endif // wxUSE_COMBOBOX