]> git.saurik.com Git - wxWidgets.git/blob - src/mac/carbon/spinctrl.cpp
Bring wxSpinCtrl closer to HIG
[wxWidgets.git] / src / mac / carbon / spinctrl.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/mac/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& WXUNUSED(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 event(wxEVT_COMMAND_SPINCTRL_UPDATED, m_spin->GetId());
92 event.SetEventObject(m_spin);
93 event.SetInt(l);
94 m_spin->HandleWindowEvent(event);
95
96 m_spin->m_oldValue = l;
97 }
98 }
99
100 void OnTextChange(wxCommandEvent& event)
101 {
102 int val;
103 if ( m_spin->GetTextValue(&val) )
104 {
105 m_spin->GetSpinButton()->SetValue(val);
106
107 // If we're already processing a text update from m_spin,
108 // don't send it again, since we could end up recursing
109 // infinitely.
110 if (event.GetId() == m_spin->GetId())
111 {
112 event.Skip();
113 return;
114 }
115
116 // Send event that the text was manually changed
117 wxCommandEvent event(wxEVT_COMMAND_TEXT_UPDATED, m_spin->GetId());
118 event.SetEventObject(m_spin);
119 event.SetString(m_spin->GetText()->GetValue());
120 event.SetInt(val);
121
122 m_spin->HandleWindowEvent(event);
123 }
124
125 event.Skip();
126 }
127
128 private:
129 wxSpinCtrl *m_spin;
130
131 DECLARE_EVENT_TABLE()
132 };
133
134 BEGIN_EVENT_TABLE(wxSpinCtrlText, wxTextCtrl)
135 EVT_TEXT(wxID_ANY, wxSpinCtrlText::OnTextChange)
136 EVT_KILL_FOCUS( wxSpinCtrlText::OnKillFocus)
137 END_EVENT_TABLE()
138
139 // ----------------------------------------------------------------------------
140 // wxSpinCtrlButton: spin button used by spin control
141 // ----------------------------------------------------------------------------
142
143 class wxSpinCtrlButton : public wxSpinButton
144 {
145 public:
146 wxSpinCtrlButton(wxSpinCtrl *spin, int style)
147 : wxSpinButton(spin )
148 {
149 m_spin = spin;
150 SetWindowStyle(style | wxSP_VERTICAL);
151
152 // TODO: The spin button gets truncated a little bit due to size
153 // differences so change it's default size a bit. SMALL still gets a
154 // bit truncated, but MINI seems to be too small... Readdress this
155 // when the textctrl issues are all sorted out.
156 //SetWindowVariant(wxWINDOW_VARIANT_SMALL);
157
158 // remove the default minsize, the spinctrl will have one instead
159 SetMinSize(wxDefaultSize);
160 }
161
162 protected:
163 void OnSpinButton(wxSpinEvent& eventSpin)
164 {
165 int pos = eventSpin.GetPosition();
166 m_spin->SetTextValue(pos);
167
168 wxCommandEvent event(wxEVT_COMMAND_SPINCTRL_UPDATED, m_spin->GetId());
169 event.SetEventObject(m_spin);
170 event.SetInt(pos);
171
172 m_spin->HandleWindowEvent(event);
173
174 m_spin->m_oldValue = pos;
175 }
176
177 private:
178 wxSpinCtrl *m_spin;
179
180 DECLARE_EVENT_TABLE()
181 };
182
183 BEGIN_EVENT_TABLE(wxSpinCtrlButton, wxSpinButton)
184 EVT_SPIN(wxID_ANY, wxSpinCtrlButton::OnSpinButton)
185 END_EVENT_TABLE()
186
187 IMPLEMENT_DYNAMIC_CLASS(wxSpinCtrl, wxControl)
188
189 BEGIN_EVENT_TABLE(wxSpinCtrl, wxControl)
190 WX_EVENT_TABLE_CONTROL_CONTAINER(wxSpinCtrl)
191 END_EVENT_TABLE()
192
193 WX_DELEGATE_TO_CONTROL_CONTAINER(wxSpinCtrl, wxControl)
194
195
196 // ============================================================================
197 // implementation
198 // ============================================================================
199
200 // ----------------------------------------------------------------------------
201 // wxSpinCtrl creation
202 // ----------------------------------------------------------------------------
203
204 void wxSpinCtrl::Init()
205 {
206 m_text = NULL;
207 m_btn = NULL;
208 WX_INIT_CONTROL_CONTAINER();
209 }
210
211 bool wxSpinCtrl::Create(wxWindow *parent,
212 wxWindowID id,
213 const wxString& value,
214 const wxPoint& pos,
215 const wxSize& size,
216 long style,
217 int min,
218 int max,
219 int initial,
220 const wxString& name)
221 {
222 m_macIsUserPane = true;
223 if ( !wxControl::Create(parent, id, pos, size, style,
224 wxDefaultValidator, name) )
225 {
226 return false;
227 }
228
229 // the string value overrides the numeric one (for backwards compatibility
230 // reasons and also because it is simpler to satisfy the string value which
231 // comes much sooner in the list of arguments and leave the initial
232 // parameter unspecified)
233 if ( !value.empty() )
234 {
235 long l;
236 if ( value.ToLong(&l) )
237 initial = l;
238 }
239
240 wxSize csize = size ;
241 m_text = new wxSpinCtrlText(this, value);
242 m_btn = new wxSpinCtrlButton(this, style);
243
244 m_btn->SetRange(min, max);
245 m_btn->SetValue(initial);
246 // make it different
247 m_oldValue = GetMin()-1;
248
249 if ( size.x == wxDefaultCoord ){
250 csize.x = m_text->GetSize().x + MARGIN + m_btn->GetSize().x ;
251 }
252
253 if ( size.y == wxDefaultCoord ) {
254 csize.y = m_text->GetSize().y + 2 * TEXTBORDER ; //allow for text border highlights
255 if ( m_btn->GetSize().y > csize.y )
256 csize.y = m_btn->GetSize().y ;
257 }
258
259 //SetSize(csize);
260
261 //MacPostControlCreate(pos, csize);
262 SetInitialSize(csize);
263
264 return true;
265 }
266
267 wxSpinCtrl::~wxSpinCtrl()
268 {
269 // delete the controls now, don't leave them alive even though they would
270 // still be eventually deleted by our parent - but it will be too late, the
271 // user code expects them to be gone now
272 delete m_text;
273 m_text = NULL ;
274 delete m_btn;
275 m_btn = NULL ;
276 }
277
278 // ----------------------------------------------------------------------------
279 // geometry
280 // ----------------------------------------------------------------------------
281
282 wxSize wxSpinCtrl::DoGetBestSize() const
283 {
284 if (!m_btn || !m_text)
285 return GetSize();
286
287 wxSize sizeBtn = m_btn->GetBestSize(),
288 sizeText = m_text->GetBestSize();
289
290 sizeText.y += 2 * TEXTBORDER ;
291 sizeText.x += 2 * TEXTBORDER ;
292
293 int height;
294 if (sizeText.y > sizeBtn.y)
295 height = sizeText.y;
296 else
297 height = sizeBtn.y;
298
299 return wxSize(sizeBtn.x + sizeText.x + MARGIN, height );
300 }
301
302 void wxSpinCtrl::DoMoveWindow(int x, int y, int width, int height)
303 {
304 // position the subcontrols inside the client area
305 wxSize sizeBtn = m_btn->GetSize();
306 wxSize sizeText = m_text->GetSize();
307
308 wxControl::DoMoveWindow(x, y, width, height);
309
310 wxCoord wText = width - sizeBtn.x - MARGIN - 2 * TEXTBORDER;
311
312 m_text->SetSize(TEXTBORDER, (height - sizeText.y) / 2, wText, -1);
313 m_btn->SetSize(0 + wText + MARGIN + TEXTBORDER , (height - sizeBtn.y) / 2 , -1, -1 );
314 }
315
316 // ----------------------------------------------------------------------------
317 // operations forwarded to the subcontrols
318 // ----------------------------------------------------------------------------
319
320 bool wxSpinCtrl::Enable(bool enable)
321 {
322 if ( !wxControl::Enable(enable) )
323 return false;
324 return true;
325 }
326
327 bool wxSpinCtrl::Show(bool show)
328 {
329 if ( !wxControl::Show(show) )
330 return false;
331 return true;
332 }
333
334 // ----------------------------------------------------------------------------
335 // value and range access
336 // ----------------------------------------------------------------------------
337
338 bool wxSpinCtrl::GetTextValue(int *val) const
339 {
340 long l;
341 if ( !m_text->GetValue().ToLong(&l) )
342 {
343 // not a number at all
344 return false;
345 }
346
347 if ( l < GetMin() || l > GetMax() )
348 {
349 // out of range
350 return false;
351 }
352
353 *val = l;
354
355 return true;
356 }
357
358 int wxSpinCtrl::GetValue() const
359 {
360 return m_btn ? m_btn->GetValue() : 0;
361 }
362
363 int wxSpinCtrl::GetMin() const
364 {
365 return m_btn ? m_btn->GetMin() : 0;
366 }
367
368 int wxSpinCtrl::GetMax() const
369 {
370 return m_btn ? m_btn->GetMax() : 0;
371 }
372
373 // ----------------------------------------------------------------------------
374 // changing value and range
375 // ----------------------------------------------------------------------------
376
377 void wxSpinCtrl::SetTextValue(int val)
378 {
379 wxCHECK_RET( m_text, _T("invalid call to wxSpinCtrl::SetTextValue") );
380
381 m_text->SetValue(wxString::Format(_T("%d"), val));
382
383 // select all text
384 m_text->SetSelection(0, -1);
385
386 // and give focus to the control!
387 // m_text->SetFocus(); Why???? TODO.
388 }
389
390 void wxSpinCtrl::SetValue(int val)
391 {
392 wxCHECK_RET( m_btn, _T("invalid call to wxSpinCtrl::SetValue") );
393
394 SetTextValue(val);
395
396 m_btn->SetValue(val);
397 m_oldValue = val;
398 }
399
400 void wxSpinCtrl::SetValue(const wxString& text)
401 {
402 wxCHECK_RET( m_text, _T("invalid call to wxSpinCtrl::SetValue") );
403
404 long val;
405 if ( text.ToLong(&val) && ((val > INT_MIN) && (val < INT_MAX)) )
406 {
407 SetValue((int)val);
408 }
409 else // not a number at all or out of range
410 {
411 m_text->SetValue(text);
412 m_text->SetSelection(0, -1);
413 }
414 }
415
416 void wxSpinCtrl::SetRange(int min, int max)
417 {
418 wxCHECK_RET( m_btn, _T("invalid call to wxSpinCtrl::SetRange") );
419
420 m_btn->SetRange(min, max);
421 }
422
423 void wxSpinCtrl::SetSelection(long from, long to)
424 {
425 // if from and to are both -1, it means (in wxWidgets) that all text should
426 // be selected
427 if ( (from == -1) && (to == -1) )
428 {
429 from = 0;
430 }
431 m_text->SetSelection(from, to);
432 }
433
434 #endif // wxUSE_SPINCTRL