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