]> git.saurik.com Git - wxWidgets.git/blob - src/osx/spinctrl_osx.cpp
Make wxMac wxSpinCtrl emit killfocus events
[wxWidgets.git] / src / osx / spinctrl_osx.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/osx/carbon/spinbutt.cpp
3 // Purpose: wxSpinCtrl
4 // Author: Robert
5 // Modified by: Mark Newsam (Based on GTK file)
6 // RCS-ID: $Id$
7 // Copyright: (c) Robert Roebling
8 // Licence: wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
10
11 #include "wx/wxprec.h"
12
13 #if wxUSE_SPINCTRL
14
15 #include "wx/spinctrl.h"
16
17 #ifndef WX_PRECOMP
18 #include "wx/textctrl.h"
19 #include "wx/containr.h"
20 #endif
21
22 #include "wx/spinbutt.h"
23
24 // ----------------------------------------------------------------------------
25 // constants
26 // ----------------------------------------------------------------------------
27
28 // the focus rect around a text may have 4 pixels in each direction
29 // we handle these problems right now in an extended vis region of a window
30 static const wxCoord TEXTBORDER = 4 ;
31 // the margin between the text control and the spin
32 // HIG says 2px between text and stepper control,
33 // but a value of 3 leads to the same look as the
34 // spin controls in Apple's apps
35 static const wxCoord MARGIN = 3;
36
37 // ----------------------------------------------------------------------------
38 // wxSpinCtrlText: text control used by spin control
39 // ----------------------------------------------------------------------------
40
41 class wxSpinCtrlText : public wxTextCtrl
42 {
43 public:
44 wxSpinCtrlText(wxSpinCtrl *spin, const wxString& value)
45 : wxTextCtrl(spin , wxID_ANY, value, wxDefaultPosition, wxSize(40, wxDefaultCoord))
46 {
47 m_spin = spin;
48
49 // remove the default minsize, the spinctrl will have one instead
50 SetMinSize(wxDefaultSize);
51 }
52
53 bool ProcessEvent(wxEvent &event)
54 {
55 // Hand button down events to wxSpinCtrl. Doesn't work.
56 if (event.GetEventType() == wxEVT_LEFT_DOWN && m_spin->ProcessEvent( event ))
57 return true;
58
59 return wxTextCtrl::ProcessEvent( event );
60 }
61
62 protected:
63 void OnKillFocus(wxFocusEvent& event)
64 {
65 long l;
66 if ( !GetValue().ToLong(&l) )
67 {
68 // not a number at all
69 return;
70 }
71
72 // is within range
73 if (l < m_spin->GetMin())
74 l = m_spin->GetMin();
75 if (l > m_spin->GetMax())
76 l = m_spin->GetMax();
77
78 // Update text control
79 wxString str;
80 str.Printf( wxT("%d"), (int)l );
81 if (str != GetValue())
82 SetValue( str );
83
84 if (l != m_spin->m_oldValue)
85 {
86 // set value in spin button
87 // does that trigger an event?
88 m_spin->m_btn->SetValue( l );
89
90 // if not
91 wxCommandEvent cevent(wxEVT_COMMAND_SPINCTRL_UPDATED, m_spin->GetId());
92 cevent.SetEventObject(m_spin);
93 cevent.SetInt(l);
94 m_spin->HandleWindowEvent(cevent);
95
96 m_spin->m_oldValue = l;
97 }
98
99 event.SetEventObject( GetParent() );
100 GetParent()->HandleWindowEvent(event);
101 }
102
103 void OnTextChange(wxCommandEvent& event)
104 {
105 int val;
106 if ( m_spin->GetTextValue(&val) )
107 {
108 m_spin->GetSpinButton()->SetValue(val);
109
110 // If we're already processing a text update from m_spin,
111 // don't send it again, since we could end up recursing
112 // infinitely.
113 if (event.GetId() == m_spin->GetId())
114 {
115 event.Skip();
116 return;
117 }
118
119 // Send event that the text was manually changed
120 wxCommandEvent event(wxEVT_COMMAND_TEXT_UPDATED, m_spin->GetId());
121 event.SetEventObject(m_spin);
122 event.SetString(m_spin->GetText()->GetValue());
123 event.SetInt(val);
124
125 m_spin->HandleWindowEvent(event);
126 }
127
128 event.Skip();
129 }
130
131 private:
132 wxSpinCtrl *m_spin;
133
134 DECLARE_EVENT_TABLE()
135 };
136
137 BEGIN_EVENT_TABLE(wxSpinCtrlText, wxTextCtrl)
138 EVT_TEXT(wxID_ANY, wxSpinCtrlText::OnTextChange)
139 EVT_KILL_FOCUS( wxSpinCtrlText::OnKillFocus)
140 END_EVENT_TABLE()
141
142 // ----------------------------------------------------------------------------
143 // wxSpinCtrlButton: spin button used by spin control
144 // ----------------------------------------------------------------------------
145
146 class wxSpinCtrlButton : public wxSpinButton
147 {
148 public:
149 wxSpinCtrlButton(wxSpinCtrl *spin, int style)
150 : wxSpinButton(spin )
151 {
152 m_spin = spin;
153 SetWindowStyle(style | wxSP_VERTICAL);
154
155 // TODO: The spin button gets truncated a little bit due to size
156 // differences so change it's default size a bit. SMALL still gets a
157 // bit truncated, but MINI seems to be too small... Readdress this
158 // when the textctrl issues are all sorted out.
159 //SetWindowVariant(wxWINDOW_VARIANT_SMALL);
160
161 // remove the default minsize, the spinctrl will have one instead
162 SetMinSize(wxDefaultSize);
163 }
164
165 protected:
166 void OnSpinButton(wxSpinEvent& eventSpin)
167 {
168 int pos = eventSpin.GetPosition();
169 m_spin->SetTextValue(pos);
170
171 wxCommandEvent event(wxEVT_COMMAND_SPINCTRL_UPDATED, m_spin->GetId());
172 event.SetEventObject(m_spin);
173 event.SetInt(pos);
174
175 m_spin->HandleWindowEvent(event);
176
177 m_spin->m_oldValue = pos;
178 }
179
180 private:
181 wxSpinCtrl *m_spin;
182
183 DECLARE_EVENT_TABLE()
184 };
185
186 BEGIN_EVENT_TABLE(wxSpinCtrlButton, wxSpinButton)
187 EVT_SPIN(wxID_ANY, wxSpinCtrlButton::OnSpinButton)
188 END_EVENT_TABLE()
189
190 IMPLEMENT_DYNAMIC_CLASS(wxSpinCtrl, wxControl)
191
192 BEGIN_EVENT_TABLE(wxSpinCtrl, wxControl)
193 WX_EVENT_TABLE_CONTROL_CONTAINER(wxSpinCtrl)
194 END_EVENT_TABLE()
195
196 WX_DELEGATE_TO_CONTROL_CONTAINER(wxSpinCtrl, wxControl)
197
198
199 // ============================================================================
200 // implementation
201 // ============================================================================
202
203 // ----------------------------------------------------------------------------
204 // wxSpinCtrl creation
205 // ----------------------------------------------------------------------------
206
207 void wxSpinCtrl::Init()
208 {
209 m_text = NULL;
210 m_btn = NULL;
211 WX_INIT_CONTROL_CONTAINER();
212 }
213
214 bool wxSpinCtrl::Create(wxWindow *parent,
215 wxWindowID id,
216 const wxString& value,
217 const wxPoint& pos,
218 const wxSize& size,
219 long style,
220 int min,
221 int max,
222 int initial,
223 const wxString& name)
224 {
225 m_macIsUserPane = true;
226 if ( !wxControl::Create(parent, id, pos, size, style,
227 wxDefaultValidator, name) )
228 {
229 return false;
230 }
231
232 // the string value overrides the numeric one (for backwards compatibility
233 // reasons and also because it is simpler to satisfy the string value which
234 // comes much sooner in the list of arguments and leave the initial
235 // parameter unspecified)
236 if ( !value.empty() )
237 {
238 long l;
239 if ( value.ToLong(&l) )
240 initial = l;
241 }
242
243 wxSize csize = size ;
244 m_text = new wxSpinCtrlText(this, value);
245 m_btn = new wxSpinCtrlButton(this, style);
246
247 m_btn->SetRange(min, max);
248 m_btn->SetValue(initial);
249 // make it different
250 m_oldValue = GetMin()-1;
251
252 if ( size.x == wxDefaultCoord ){
253 csize.x = m_text->GetSize().x + MARGIN + m_btn->GetSize().x ;
254 }
255
256 if ( size.y == wxDefaultCoord ) {
257 csize.y = m_text->GetSize().y + 2 * TEXTBORDER ; //allow for text border highlights
258 if ( m_btn->GetSize().y > csize.y )
259 csize.y = m_btn->GetSize().y ;
260 }
261
262 //SetSize(csize);
263
264 //MacPostControlCreate(pos, csize);
265 SetInitialSize(csize);
266
267 return true;
268 }
269
270 wxSpinCtrl::~wxSpinCtrl()
271 {
272 // delete the controls now, don't leave them alive even though they would
273 // still be eventually deleted by our parent - but it will be too late, the
274 // user code expects them to be gone now
275 delete m_text;
276 m_text = NULL ;
277 delete m_btn;
278 m_btn = NULL ;
279 }
280
281 // ----------------------------------------------------------------------------
282 // geometry
283 // ----------------------------------------------------------------------------
284
285 wxSize wxSpinCtrl::DoGetBestSize() const
286 {
287 if (!m_btn || !m_text)
288 return GetSize();
289
290 wxSize sizeBtn = m_btn->GetBestSize(),
291 sizeText = m_text->GetBestSize();
292
293 sizeText.y += 2 * TEXTBORDER ;
294 sizeText.x += 2 * TEXTBORDER ;
295
296 int height;
297 if (sizeText.y > sizeBtn.y)
298 height = sizeText.y;
299 else
300 height = sizeBtn.y;
301
302 return wxSize(sizeBtn.x + sizeText.x + MARGIN, height );
303 }
304
305 void wxSpinCtrl::DoMoveWindow(int x, int y, int width, int height)
306 {
307 // position the subcontrols inside the client area
308 wxSize sizeBtn = m_btn->GetSize();
309 wxSize sizeText = m_text->GetSize();
310
311 wxControl::DoMoveWindow(x, y, width, height);
312
313 wxCoord wText = width - sizeBtn.x - MARGIN - 2 * TEXTBORDER;
314
315 m_text->SetSize(TEXTBORDER, (height - sizeText.y) / 2, wText, -1);
316 m_btn->SetSize(0 + wText + MARGIN + TEXTBORDER , (height - sizeBtn.y) / 2 , -1, -1 );
317 }
318
319 // ----------------------------------------------------------------------------
320 // operations forwarded to the subcontrols
321 // ----------------------------------------------------------------------------
322
323 bool wxSpinCtrl::Enable(bool enable)
324 {
325 if ( !wxControl::Enable(enable) )
326 return false;
327 return true;
328 }
329
330 bool wxSpinCtrl::Show(bool show)
331 {
332 if ( !wxControl::Show(show) )
333 return false;
334 return true;
335 }
336
337 // ----------------------------------------------------------------------------
338 // value and range access
339 // ----------------------------------------------------------------------------
340
341 bool wxSpinCtrl::GetTextValue(int *val) const
342 {
343 long l;
344 if ( !m_text->GetValue().ToLong(&l) )
345 {
346 // not a number at all
347 return false;
348 }
349
350 if ( l < GetMin() || l > GetMax() )
351 {
352 // out of range
353 return false;
354 }
355
356 *val = l;
357
358 return true;
359 }
360
361 int wxSpinCtrl::GetValue() const
362 {
363 return m_btn ? m_btn->GetValue() : 0;
364 }
365
366 int wxSpinCtrl::GetMin() const
367 {
368 return m_btn ? m_btn->GetMin() : 0;
369 }
370
371 int wxSpinCtrl::GetMax() const
372 {
373 return m_btn ? m_btn->GetMax() : 0;
374 }
375
376 // ----------------------------------------------------------------------------
377 // changing value and range
378 // ----------------------------------------------------------------------------
379
380 void wxSpinCtrl::SetTextValue(int val)
381 {
382 wxCHECK_RET( m_text, _T("invalid call to wxSpinCtrl::SetTextValue") );
383
384 m_text->SetValue(wxString::Format(_T("%d"), val));
385
386 // select all text
387 m_text->SetSelection(0, -1);
388
389 // and give focus to the control!
390 // m_text->SetFocus(); Why???? TODO.
391 }
392
393 void wxSpinCtrl::SetValue(int val)
394 {
395 wxCHECK_RET( m_btn, _T("invalid call to wxSpinCtrl::SetValue") );
396
397 SetTextValue(val);
398
399 m_btn->SetValue(val);
400 m_oldValue = val;
401 }
402
403 void wxSpinCtrl::SetValue(const wxString& text)
404 {
405 wxCHECK_RET( m_text, _T("invalid call to wxSpinCtrl::SetValue") );
406
407 long val;
408 if ( text.ToLong(&val) && ((val > INT_MIN) && (val < INT_MAX)) )
409 {
410 SetValue((int)val);
411 }
412 else // not a number at all or out of range
413 {
414 m_text->SetValue(text);
415 m_text->SetSelection(0, -1);
416 }
417 }
418
419 void wxSpinCtrl::SetRange(int min, int max)
420 {
421 wxCHECK_RET( m_btn, _T("invalid call to wxSpinCtrl::SetRange") );
422
423 m_btn->SetRange(min, max);
424 }
425
426 void wxSpinCtrl::SetSelection(long from, long to)
427 {
428 // if from and to are both -1, it means (in wxWidgets) that all text should
429 // be selected
430 if ( (from == -1) && (to == -1) )
431 {
432 from = 0;
433 }
434 m_text->SetSelection(from, to);
435 }
436
437 #endif // wxUSE_SPINCTRL