1///////////////////////////////////////////////////////////////////////////// 2// Name: slider.cpp 3// Purpose: wxSlider 4// Author: Stefan Csomor 5// Modified by: 6// Created: 1998-01-01 7// RCS-ID: $Id: slider.cpp 49543 2007-10-31 01:57:13Z KO $ 8// Copyright: (c) Stefan Csomor 9// Licence: wxWindows licence 10///////////////////////////////////////////////////////////////////////////// 11 12#include "wx/wxprec.h" 13 14#if wxUSE_SLIDER 15 16#include "wx/slider.h" 17#include "wx/mac/uma.h" 18 19IMPLEMENT_DYNAMIC_CLASS(wxSlider, wxControl) 20 21BEGIN_EVENT_TABLE(wxSlider, wxControl) 22END_EVENT_TABLE() 23 24 // The dimensions of the different styles of sliders (from Aqua document) 25#define wxSLIDER_DIMENSIONACROSS_WITHTICKMARKS 24 26#define wxSLIDER_DIMENSIONACROSS_ARROW 18 27 28// Distance between slider and text 29#define wxSLIDER_BORDERTEXT 5 30 31// NB: The default orientation for a slider is horizontal; however, if the user specifies 32// some slider styles but doesn't specify the orientation we have to assume he wants a 33// horizontal one. Therefore in this file when testing for the slider's orientation 34// vertical is tested for if this is not set then we use the horizontal one 35// e.g., if (GetWindowStyle() & wxSL_VERTICAL) {} else { horizontal case }. 36 37wxSlider::wxSlider() 38{ 39 m_pageSize = 1; 40 m_lineSize = 1; 41 m_rangeMax = 0; 42 m_rangeMin = 0; 43 m_tickFreq = 0; 44 45 m_macMinimumStatic = NULL; 46 m_macMaximumStatic = NULL; 47 m_macValueStatic = NULL; 48} 49 50bool wxSlider::Create(wxWindow *parent, 51 wxWindowID id, 52 int value, int minValue, int maxValue, 53 const wxPoint& pos, 54 const wxSize& size, long style, 55 const wxValidator& validator, 56 const wxString& name) 57{ 58 m_macIsUserPane = false; 59 60 m_macMinimumStatic = NULL; 61 m_macMaximumStatic = NULL; 62 m_macValueStatic = NULL; 63 64 m_lineSize = 1; 65 m_tickFreq = 0; 66 67 m_rangeMax = maxValue; 68 m_rangeMin = minValue; 69 70 m_pageSize = (int)((maxValue - minValue) / 10); 71 72 // our styles are redundant: wxSL_LEFT/RIGHT imply wxSL_VERTICAL and 73 // wxSL_TOP/BOTTOM imply wxSL_HORIZONTAL, but for backwards compatibility 74 // reasons we can't really change it, instead try to infer the orientation 75 // from the flags given to us here 76 switch ( style & (wxSL_LEFT | wxSL_RIGHT | wxSL_TOP | wxSL_BOTTOM) ) 77 { 78 case wxSL_LEFT: 79 case wxSL_RIGHT: 80 style |= wxSL_VERTICAL; 81 break; 82 83 case wxSL_TOP: 84 case wxSL_BOTTOM: 85 style |= wxSL_HORIZONTAL; 86 break; 87 88 case 0: 89 default: 90 // no specific direction, do we have at least the orientation? 91 if ( !(style & (wxSL_HORIZONTAL | wxSL_VERTICAL)) ) 92 // no: choose default 93 style |= wxSL_BOTTOM | wxSL_HORIZONTAL; 94 break; 95 } 96 97 wxASSERT_MSG( !(style & wxSL_VERTICAL) || !(style & wxSL_HORIZONTAL), 98 wxT("incompatible slider direction and orientation") ); 99 100 if ( !wxControl::Create(parent, id, pos, size, style, validator, name) ) 101 return false; 102 103 Rect bounds = wxMacGetBoundsForControl( this , pos , size ); 104 105 // NB: (RN) Ticks here are sometimes off in the GUI if there 106 // are not as many tick marks as there are values 107 // 108 int tickMarks = 0; 109 if ( style & wxSL_AUTOTICKS ) 110 tickMarks = (maxValue - minValue) + 1; // +1 for the 0 value 111 112 // keep the number of tickmarks from becoming unwieldly, therefore below it is ok to cast 113 // it to a UInt16 114 while (tickMarks > 20) 115 tickMarks /= 5; 116 117 m_peer = new wxMacControl( this ); 118 OSStatus err = CreateSliderControl( 119 MAC_WXHWND(parent->MacGetTopLevelWindowRef()), &bounds, 120 value, minValue, maxValue, 121 kControlSliderPointsDownOrRight, 122 (UInt16) tickMarks, true /* liveTracking */, 123 GetwxMacLiveScrollbarActionProc(), 124 m_peer->GetControlRefAddr() ); 125 verify_noerr( err ); 126 127 if (style & wxSL_VERTICAL) 128 // Forces SetSize to use the proper width 129 SetSizeHints(10, -1, 10, -1); 130 else 131 // Forces SetSize to use the proper height 132 SetSizeHints(-1, 10, -1, 10); 133 134 // NB: SetSizeHints is overloaded by wxSlider and will substitute 10 with the 135 // proper dimensions, it also means other people cannot bugger the slider with 136 // other values 137 138 if (style & wxSL_LABELS) 139 { 140 m_macMinimumStatic = new wxStaticText( parent, wxID_ANY, wxEmptyString ); 141 m_macMaximumStatic = new wxStaticText( parent, wxID_ANY, wxEmptyString ); 142 m_macValueStatic = new wxStaticText( parent, wxID_ANY, wxEmptyString ); 143 } 144 145 SetRange(minValue, maxValue); 146 SetValue(value); 147 148 MacPostControlCreate(pos, size); 149 150 return true; 151} 152 153wxSlider::~wxSlider() 154{ 155 // this is a special case, as we had to add windows as siblings we are 156 // responsible for their disposal, but only if we are not part of a DestroyAllChildren 157 if ( m_parent && !m_parent->IsBeingDeleted() ) 158 { 159 delete m_macMinimumStatic; 160 delete m_macMaximumStatic; 161 delete m_macValueStatic; 162 } 163} 164 165int wxSlider::GetValue() const 166{ 167 // We may need to invert the value returned by the widget 168 return ValueInvertOrNot( m_peer->GetValue() ) ; 169} 170 171void wxSlider::SetValue(int value) 172{ 173 if ( m_macValueStatic ) 174 { 175 wxString valuestring; 176 valuestring.Printf( wxT("%d"), value ); 177 m_macValueStatic->SetLabel( valuestring ); 178 } 179 180 // We only invert for the setting of the actual native widget 181 m_peer->SetValue( ValueInvertOrNot( value ) ); 182} 183 184void wxSlider::SetRange(int minValue, int maxValue) 185{ 186 wxString value; 187 188 m_rangeMin = minValue; 189 m_rangeMax = maxValue; 190 191 m_peer->SetMinimum( m_rangeMin ); 192 m_peer->SetMaximum( m_rangeMax ); 193 194 if (m_macMinimumStatic) 195 { 196 value.Printf( wxT("%d"), ValueInvertOrNot( m_rangeMin ) ); 197 m_macMinimumStatic->SetLabel( value ); 198 } 199 200 if (m_macMaximumStatic) 201 { 202 value.Printf( wxT("%d"), ValueInvertOrNot( m_rangeMax ) ); 203 m_macMaximumStatic->SetLabel( value ); 204 } 205 206 // If the range is out of bounds, set it to a 207 // value that is within bounds 208 // RN: Testing reveals OSX does its own 209 // bounding, perhaps this isn't needed? 210 int currentValue = GetValue(); 211 212 if(currentValue < m_rangeMin) 213 SetValue(m_rangeMin); 214 else if(currentValue > m_rangeMax) 215 SetValue(m_rangeMax); 216} 217 218// For trackbars only 219void wxSlider::SetTickFreq(int n, int pos) 220{ 221 // TODO 222 m_tickFreq = n; 223} 224 225void wxSlider::SetPageSize(int pageSize) 226{ 227 // TODO 228 m_pageSize = pageSize; 229} 230 231int wxSlider::GetPageSize() const 232{ 233 return m_pageSize; 234} 235 236void wxSlider::ClearSel() 237{ 238 // TODO 239} 240 241void wxSlider::ClearTicks() 242{ 243 // TODO 244} 245 246void wxSlider::SetLineSize(int lineSize) 247{ 248 m_lineSize = lineSize; 249 // TODO 250} 251 252int wxSlider::GetLineSize() const 253{ 254 // TODO 255 return m_lineSize; 256} 257 258int wxSlider::GetSelEnd() const 259{ 260 // TODO 261 return 0; 262} 263 264int wxSlider::GetSelStart() const 265{ 266 // TODO 267 return 0; 268} 269 270void wxSlider::SetSelection(int minPos, int maxPos) 271{ 272 // TODO 273} 274 275void wxSlider::SetThumbLength(int len) 276{ 277 // TODO 278} 279 280int wxSlider::GetThumbLength() const 281{ 282 // TODO 283 return 0; 284} 285 286void wxSlider::SetTick(int tickPos) 287{ 288 // TODO 289} 290 291void wxSlider::Command(wxCommandEvent &event) 292{ 293 SetValue(event.GetInt()); 294 ProcessCommand(event); 295} 296 297void wxSlider::MacHandleControlClick( WXWidget control, wxInt16 controlpart, bool mouseStillDown ) 298{ 299 // Whatever the native value is, we may need to invert it for calling 300 // SetValue and putting the possibly inverted value in the event 301 int value = ValueInvertOrNot( m_peer->GetValue() ); 302 303 SetValue( value ); 304 305 wxScrollEvent event( wxEVT_SCROLL_THUMBTRACK, m_windowId ); 306 event.SetPosition( value ); 307 event.SetEventObject( this ); 308 GetEventHandler()->ProcessEvent( event ); 309 310 wxCommandEvent cevent( wxEVT_COMMAND_SLIDER_UPDATED, m_windowId ); 311 cevent.SetInt( value ); 312 cevent.SetEventObject( this ); 313 GetEventHandler()->ProcessEvent( cevent ); 314} 315 316wxInt32 wxSlider::MacControlHit( WXEVENTHANDLERREF handler , WXEVENTREF mevent ) 317{ 318 // Whatever the native value is, we may need to invert it for calling 319 // SetValue and putting the possibly inverted value in the event 320 int value = ValueInvertOrNot( m_peer->GetValue() ) ; 321 322 SetValue( value ) ; 323 324 wxScrollEvent event( wxEVT_SCROLL_THUMBRELEASE, m_windowId ); 325 event.SetPosition( value ); 326 event.SetEventObject( this ); 327 GetEventHandler()->ProcessEvent( event ); 328 329 wxCommandEvent cevent( wxEVT_COMMAND_SLIDER_UPDATED, m_windowId ); 330 cevent.SetInt( value ); 331 cevent.SetEventObject( this ); 332 333 GetEventHandler()->ProcessEvent( cevent ); 334 335 return noErr; 336} 337 338// This is overloaded in wxSlider so that the proper width/height will always be used 339// for the slider different values would cause redrawing and mouse detection problems 340// 341void wxSlider::DoSetSizeHints( int minW, int minH, 342 int maxW, int maxH, 343 int incW, int incH ) 344{ 345 wxSize size = GetBestSize(); 346 347 if (GetWindowStyle() & wxSL_VERTICAL) 348 { 349 SetMinSize( wxSize(size.x,minH) ); 350 SetMaxSize( wxSize(size.x,maxH) ); 351 } 352 else 353 { 354 SetMinSize( wxSize(minW,size.y) ); 355 SetMaxSize( wxSize(maxW,size.y) ); 356 } 357} 358 359wxSize wxSlider::DoGetBestSize() const 360{ 361 wxSize size; 362 int textwidth, textheight; 363 int mintwidth, mintheight; 364 int maxtwidth, maxtheight; 365 366 textwidth = textheight = 0; 367 mintwidth = mintheight = 0; 368 maxtwidth = maxtheight = 0; 369 370 if (GetWindowStyle() & wxSL_LABELS) 371 { 372 wxString text; 373 374 // Get maximum text label width and height 375 text.Printf( wxT("%d"), ValueInvertOrNot( m_rangeMin ) ); 376 GetTextExtent(text, &mintwidth, &mintheight); 377 text.Printf( wxT("%d"), ValueInvertOrNot( m_rangeMax ) ); 378 GetTextExtent(text, &maxtwidth, &maxtheight); 379 380 if (maxtheight > mintheight) 381 textheight = maxtheight; 382 else 383 textheight = mintheight; 384 385 if (maxtwidth > mintwidth) 386 textwidth = maxtwidth; 387 else 388 textwidth = mintwidth; 389 } 390 391 if (GetWindowStyle() & wxSL_VERTICAL) 392 { 393 size.y = 150; 394 395 if (GetWindowStyle() & wxSL_AUTOTICKS) 396 size.x = wxSLIDER_DIMENSIONACROSS_WITHTICKMARKS; 397 else 398 size.x = wxSLIDER_DIMENSIONACROSS_ARROW; 399 400 if (GetWindowStyle() & wxSL_LABELS) 401 size.x += textwidth + wxSLIDER_BORDERTEXT; 402 } 403 else 404 { 405 size.x = 150; 406 407 if (GetWindowStyle() & wxSL_AUTOTICKS) 408 size.y = wxSLIDER_DIMENSIONACROSS_WITHTICKMARKS; 409 else 410 size.y = wxSLIDER_DIMENSIONACROSS_ARROW; 411 412 if (GetWindowStyle() & wxSL_LABELS) 413 { 414 size.y += textheight + wxSLIDER_BORDERTEXT; 415 size.x += (mintwidth / 2) + (maxtwidth / 2); 416 } 417 } 418 419 return size; 420} 421 422void wxSlider::DoSetSize(int x, int y, int w, int h, int sizeFlags) 423{ 424 int yborder = 0; 425 int minValWidth, maxValWidth, textheight; 426 int sliderBreadth; 427 int width = w; 428 429 if (GetWindowStyle() & wxSL_LABELS) 430 { 431 wxString text; 432 int ht, valValWidth; 433 434 // Get maximum text label width and height 435 text.Printf(wxT("%d"), ValueInvertOrNot( m_rangeMin ) ); 436 GetTextExtent(text, &minValWidth, &textheight); 437 text.Printf(wxT("%d"), ValueInvertOrNot( m_rangeMax ) ); 438 GetTextExtent(text, &maxValWidth, &ht); 439 440 if (ht > textheight) 441 textheight = ht; 442 443 if (GetWindowStyle() & wxSL_HORIZONTAL) 444 { 445 if ( m_macMinimumStatic ) 446 { 447 w -= minValWidth / 2; 448 x += minValWidth / 2; 449 } 450 451 if ( m_macMaximumStatic ) 452 w -= maxValWidth / 2; 453 } 454 455 // Labels have this control's parent as their parent 456 // so if this control is not at 0,0 relative to the parent 457 // the labels need to know the position of this control 458 // relative to its parent in order to size properly, so 459 // move the control first so we can use GetPosition() 460 wxControl::DoSetSize( x, y, w, h, sizeFlags ); 461 462 if (GetWindowStyle() & wxSL_VERTICAL) 463 // If vertical, use current value 464 text.Printf(wxT("%d"), (int)m_peer->GetValue()); 465 else 466 // Use max so that the current value doesn't drift as centering would need to change 467 text.Printf(wxT("%d"), m_rangeMax); 468 469 GetTextExtent(text, &valValWidth, &ht); 470 471 yborder = textheight + wxSLIDER_BORDERTEXT; 472 473 // Get slider breadth 474 if (GetWindowStyle() & wxSL_AUTOTICKS) 475 sliderBreadth = wxSLIDER_DIMENSIONACROSS_WITHTICKMARKS; 476 else 477 sliderBreadth = wxSLIDER_DIMENSIONACROSS_ARROW; 478 479 if (GetWindowStyle() & wxSL_VERTICAL) 480 { 481 h = h - yborder; 482 483 if ( m_macMinimumStatic ) 484 m_macMinimumStatic->Move(GetPosition().x + sliderBreadth + wxSLIDER_BORDERTEXT, GetPosition().y + h - yborder); 485 if ( m_macMaximumStatic ) 486 m_macMaximumStatic->Move(GetPosition().x + sliderBreadth + wxSLIDER_BORDERTEXT, GetPosition().y + 0); 487 if ( m_macValueStatic ) 488 m_macValueStatic->Move(GetPosition().x + sliderBreadth + wxSLIDER_BORDERTEXT, GetPosition().y + (h / 2) - (ht / 2)); 489 } 490 else 491 { 492 if ( m_macMinimumStatic ) 493 m_macMinimumStatic->Move(GetPosition().x, GetPosition().y + sliderBreadth + wxSLIDER_BORDERTEXT); 494 if ( m_macMaximumStatic ) 495 m_macMaximumStatic->Move(GetPosition().x + w - maxValWidth, GetPosition().y + sliderBreadth + wxSLIDER_BORDERTEXT); 496 if ( m_macValueStatic ) 497 m_macValueStatic->Move(GetPosition().x + (w / 2) - (valValWidth / 2), GetPosition().y + sliderBreadth + wxSLIDER_BORDERTEXT); 498 } 499 } 500 501 // yet another hack since this is a composite control 502 // when wxSlider has it's size hardcoded, we're not allowed to 503 // change the size. But when the control has labels, we DO need 504 // to resize the internal Mac control to accommodate the text labels. 505 // We need to trick the wxWidgets resize mechanism so that we can 506 // resize the slider part of the control ONLY. 507 508 // TODO: Can all of this code go in the conditional wxSL_LABELS block? 509 510 int minWidth = m_minWidth; 511 512 if (GetWindowStyle() & wxSL_LABELS) 513 { 514 // make sure we don't allow the entire control to be resized accidently 515 if (width == GetSize().x) 516 m_minWidth = -1; 517 } 518 519 // If the control has labels, we still need to call this again because 520 // the labels alter the control's w and h values. 521 wxControl::DoSetSize( x, y, w, h, sizeFlags ); 522 523 m_minWidth = minWidth; 524} 525 526void wxSlider::DoMoveWindow(int x, int y, int width, int height) 527{ 528 wxControl::DoMoveWindow( x, y, width, height ); 529} 530 531// Common processing to invert slider values based on wxSL_INVERSE 532int wxSlider::ValueInvertOrNot(int value) const 533{ 534 int result = 0; 535 536 if (m_windowStyle & wxSL_VERTICAL) 537 { 538 // The reason for the backwards logic is that Mac's vertical sliders are 539 // inverted compared to Windows and GTK, hence we want inversion to be the 540 // default, and if wxSL_INVERSE is set, then we do not invert (use native) 541 if (m_windowStyle & wxSL_INVERSE) 542 result = value; 543 else 544 result = (m_rangeMax + m_rangeMin) - value; 545 } 546 else // normal logic applies to HORIZONTAL sliders 547 { 548 result = wxSliderBase::ValueInvertOrNot(value); 549 } 550 551 return result; 552} 553 554#endif // wxUSE_SLIDER 555