]> git.saurik.com Git - wxWidgets.git/blame - src/osx/spinctrl_osx.cpp
rewrite using type safe wxHashMap instead of wxObject-based wxHashTable
[wxWidgets.git] / src / osx / spinctrl_osx.cpp
CommitLineData
489468fe 1/////////////////////////////////////////////////////////////////////////////
524c47aa 2// Name: src/osx/carbon/spinbutt.cpp
489468fe
SC
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
30static 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
35static const wxCoord MARGIN = 3;
36
37// ----------------------------------------------------------------------------
38// wxSpinCtrlText: text control used by spin control
39// ----------------------------------------------------------------------------
40
41class wxSpinCtrlText : public wxTextCtrl
42{
43public:
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
62protected:
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
128private:
129 wxSpinCtrl *m_spin;
130
131 DECLARE_EVENT_TABLE()
132};
133
134BEGIN_EVENT_TABLE(wxSpinCtrlText, wxTextCtrl)
135 EVT_TEXT(wxID_ANY, wxSpinCtrlText::OnTextChange)
136 EVT_KILL_FOCUS( wxSpinCtrlText::OnKillFocus)
137END_EVENT_TABLE()
138
139// ----------------------------------------------------------------------------
140// wxSpinCtrlButton: spin button used by spin control
141// ----------------------------------------------------------------------------
142
143class wxSpinCtrlButton : public wxSpinButton
144{
145public:
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
162protected:
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
177private:
178 wxSpinCtrl *m_spin;
179
180 DECLARE_EVENT_TABLE()
181};
182
183BEGIN_EVENT_TABLE(wxSpinCtrlButton, wxSpinButton)
184 EVT_SPIN(wxID_ANY, wxSpinCtrlButton::OnSpinButton)
185END_EVENT_TABLE()
186
187IMPLEMENT_DYNAMIC_CLASS(wxSpinCtrl, wxControl)
188
189BEGIN_EVENT_TABLE(wxSpinCtrl, wxControl)
190 WX_EVENT_TABLE_CONTROL_CONTAINER(wxSpinCtrl)
191END_EVENT_TABLE()
192
193WX_DELEGATE_TO_CONTROL_CONTAINER(wxSpinCtrl, wxControl)
194
195
196// ============================================================================
197// implementation
198// ============================================================================
199
200// ----------------------------------------------------------------------------
201// wxSpinCtrl creation
202// ----------------------------------------------------------------------------
203
204void wxSpinCtrl::Init()
205{
206 m_text = NULL;
207 m_btn = NULL;
208 WX_INIT_CONTROL_CONTAINER();
209}
210
211bool 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
267wxSpinCtrl::~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
282wxSize 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
302void 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
320bool wxSpinCtrl::Enable(bool enable)
321{
322 if ( !wxControl::Enable(enable) )
323 return false;
324 return true;
325}
326
327bool 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
338bool 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
358int wxSpinCtrl::GetValue() const
359{
360 return m_btn ? m_btn->GetValue() : 0;
361}
362
363int wxSpinCtrl::GetMin() const
364{
365 return m_btn ? m_btn->GetMin() : 0;
366}
367
368int wxSpinCtrl::GetMax() const
369{
370 return m_btn ? m_btn->GetMax() : 0;
371}
372
373// ----------------------------------------------------------------------------
374// changing value and range
375// ----------------------------------------------------------------------------
376
377void 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
390void 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
400void 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
416void 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
423void 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