1 ///////////////////////////////////////////////////////////////////////////// 
   2 // Name:        src/gtk/slider.cpp 
   4 // Author:      Robert Roebling 
   6 // Copyright:   (c) 1998 Robert Roebling 
   7 // Licence:     wxWindows licence 
   8 ///////////////////////////////////////////////////////////////////////////// 
  10 // For compilers that support precompilation, includes "wx.h". 
  11 #include "wx/wxprec.h" 
  15 #include "wx/slider.h" 
  24 //----------------------------------------------------------------------------- 
  26 //----------------------------------------------------------------------------- 
  28 extern bool g_blockEventsOnDrag
; 
  30 // ---------------------------------------------------------------------------- 
  32 // ---------------------------------------------------------------------------- 
  34 // process a scroll event 
  36 ProcessScrollEvent(wxSlider 
*win
, wxEventType evtType
) 
  38     const int orient 
= win
->HasFlag(wxSL_VERTICAL
) ? wxVERTICAL
 
  41     const int value 
= win
->GetValue(); 
  43     // if we have any "special" event (i.e. the value changed by a line or a 
  44     // page), send this specific event first 
  45     if ( evtType 
!= wxEVT_NULL 
) 
  47         wxScrollEvent 
event( evtType
, win
->GetId(), value
, orient 
); 
  48         event
.SetEventObject( win 
); 
  49         win
->HandleWindowEvent( event 
); 
  52     // but, in any case, except if we're dragging the slider (and so the change 
  53     // is not definitive), send a generic "changed" event 
  54     if ( evtType 
!= wxEVT_SCROLL_THUMBTRACK 
) 
  56         wxScrollEvent 
event(wxEVT_SCROLL_CHANGED
, win
->GetId(), value
, orient
); 
  57         event
.SetEventObject( win 
); 
  58         win
->HandleWindowEvent( event 
); 
  61     // and also generate a command event for compatibility 
  62     wxCommandEvent 
event( wxEVT_COMMAND_SLIDER_UPDATED
, win
->GetId() ); 
  63     event
.SetEventObject( win 
); 
  64     event
.SetInt( value 
); 
  65     win
->HandleWindowEvent( event 
); 
  68 static inline wxEventType 
GtkScrollTypeToWx(int scrollType
) 
  70     wxEventType eventType
; 
  73     case GTK_SCROLL_STEP_BACKWARD
: 
  74     case GTK_SCROLL_STEP_LEFT
: 
  75     case GTK_SCROLL_STEP_UP
: 
  76         eventType 
= wxEVT_SCROLL_LINEUP
; 
  78     case GTK_SCROLL_STEP_DOWN
: 
  79     case GTK_SCROLL_STEP_FORWARD
: 
  80     case GTK_SCROLL_STEP_RIGHT
: 
  81         eventType 
= wxEVT_SCROLL_LINEDOWN
; 
  83     case GTK_SCROLL_PAGE_BACKWARD
: 
  84     case GTK_SCROLL_PAGE_LEFT
: 
  85     case GTK_SCROLL_PAGE_UP
: 
  86         eventType 
= wxEVT_SCROLL_PAGEUP
; 
  88     case GTK_SCROLL_PAGE_DOWN
: 
  89     case GTK_SCROLL_PAGE_FORWARD
: 
  90     case GTK_SCROLL_PAGE_RIGHT
: 
  91         eventType 
= wxEVT_SCROLL_PAGEDOWN
; 
  93     case GTK_SCROLL_START
: 
  94         eventType 
= wxEVT_SCROLL_TOP
; 
  97         eventType 
= wxEVT_SCROLL_BOTTOM
; 
 100         eventType 
= wxEVT_SCROLL_THUMBTRACK
; 
 103         wxFAIL_MSG(wxT("Unknown GtkScrollType")); 
 104         eventType 
= wxEVT_NULL
; 
 110 // Determine if increment is the same as +/-x, allowing for some small 
 111 //   difference due to possible inexactness in floating point arithmetic 
 112 static inline bool IsScrollIncrement(double increment
, double x
) 
 114     wxASSERT(increment 
> 0); 
 115     const double tolerance 
= 1.0 / 1024; 
 116     return fabs(increment 
- fabs(x
)) < tolerance
; 
 119 //----------------------------------------------------------------------------- 
 121 //----------------------------------------------------------------------------- 
 125 gtk_value_changed(GtkRange
* range
, wxSlider
* win
) 
 127     GtkAdjustment
* adj 
= gtk_range_get_adjustment (range
); 
 128     const int pos 
= wxRound(adj
->value
); 
 129     const double oldPos 
= win
->m_pos
; 
 130     win
->m_pos 
= adj
->value
; 
 132     if (!win
->m_hasVMT 
|| g_blockEventsOnDrag
) 
 135     if (win
->GTKEventsDisabled()) 
 137         win
->m_scrollEventType 
= GTK_SCROLL_NONE
; 
 141     wxEventType eventType 
= wxEVT_NULL
; 
 142     if (win
->m_isScrolling
) 
 144         eventType 
= wxEVT_SCROLL_THUMBTRACK
; 
 146     else if (win
->m_scrollEventType 
!= GTK_SCROLL_NONE
) 
 148         // Scroll event from "move-slider" (keyboard) 
 149         eventType 
= GtkScrollTypeToWx(win
->m_scrollEventType
); 
 151     else if (win
->m_mouseButtonDown
) 
 153         // Difference from last change event 
 154         const double diff 
= adj
->value 
- oldPos
; 
 155         const bool isDown 
= diff 
> 0; 
 157         if (IsScrollIncrement(adj
->page_increment
, diff
)) 
 159             eventType 
= isDown 
? wxEVT_SCROLL_PAGEDOWN 
: wxEVT_SCROLL_PAGEUP
; 
 161         else if (wxIsSameDouble(adj
->value
, 0)) 
 163             eventType 
= wxEVT_SCROLL_PAGEUP
; 
 165         else if (wxIsSameDouble(adj
->value
, adj
->upper
)) 
 167             eventType 
= wxEVT_SCROLL_PAGEDOWN
; 
 171             // Assume track event 
 172             eventType 
= wxEVT_SCROLL_THUMBTRACK
; 
 173             // Remember that we're tracking 
 174             win
->m_isScrolling 
= true; 
 178     win
->m_scrollEventType 
= GTK_SCROLL_NONE
; 
 180     // If integral position has changed 
 181     if (wxRound(oldPos
) != pos
) 
 183         ProcessScrollEvent(win
, eventType
); 
 184         win
->m_needThumbRelease 
= eventType 
== wxEVT_SCROLL_THUMBTRACK
; 
 189 //----------------------------------------------------------------------------- 
 190 // "move_slider" (keyboard event) 
 191 //----------------------------------------------------------------------------- 
 195 gtk_move_slider(GtkRange
*, GtkScrollType scrollType
, wxSlider
* win
) 
 197     // Save keyboard scroll type for "value_changed" handler 
 198     win
->m_scrollEventType 
= scrollType
; 
 202 //----------------------------------------------------------------------------- 
 203 // "button_press_event" 
 204 //----------------------------------------------------------------------------- 
 208 gtk_button_press_event(GtkWidget
*, GdkEventButton
*, wxSlider
* win
) 
 210     win
->m_mouseButtonDown 
= true; 
 216 //----------------------------------------------------------------------------- 
 218 //----------------------------------------------------------------------------- 
 222 gtk_event_after(GtkRange
* range
, GdkEvent
* event
, wxSlider
* win
) 
 224     if (event
->type 
== GDK_BUTTON_RELEASE
) 
 226         g_signal_handlers_block_by_func(range
, (gpointer
) gtk_event_after
, win
); 
 228         if (win
->m_needThumbRelease
) 
 230             win
->m_needThumbRelease 
= false; 
 231             ProcessScrollEvent(win
, wxEVT_SCROLL_THUMBRELEASE
); 
 233         // Keep slider at an integral position 
 234         win
->GTKDisableEvents(); 
 235         gtk_range_set_value(GTK_RANGE (win
->m_scale
), win
->GetValue()); 
 236         win
->GTKEnableEvents(); 
 241 //----------------------------------------------------------------------------- 
 242 // "button_release_event" 
 243 //----------------------------------------------------------------------------- 
 247 gtk_button_release_event(GtkRange
* range
, GdkEventButton
*, wxSlider
* win
) 
 249     win
->m_mouseButtonDown 
= false; 
 250     if (win
->m_isScrolling
) 
 252         win
->m_isScrolling 
= false; 
 253         g_signal_handlers_unblock_by_func(range
, (gpointer
) gtk_event_after
, win
); 
 259 //----------------------------------------------------------------------------- 
 261 //----------------------------------------------------------------------------- 
 264 static gchar
* gtk_format_value(GtkScale
*, double value
, void*) 
 266     // Format value as nearest integer 
 267     return g_strdup_printf("%d", wxRound(value
)); 
 271 //----------------------------------------------------------------------------- 
 273 //----------------------------------------------------------------------------- 
 275 IMPLEMENT_DYNAMIC_CLASS(wxSlider
,wxControl
) 
 280     m_scrollEventType 
= GTK_SCROLL_NONE
; 
 281     m_needThumbRelease 
= false; 
 282     m_blockScrollEvent 
= false; 
 285 bool wxSlider::Create(wxWindow 
*parent
, 
 293                       const wxValidator
& validator
, 
 294                       const wxString
& name
) 
 297     m_scrollEventType 
= GTK_SCROLL_NONE
; 
 299     if (!PreCreation( parent
, pos
, size 
) || 
 300         !CreateBase( parent
, id
, pos
, size
, style
, validator
, name 
)) 
 302         wxFAIL_MSG( wxT("wxSlider creation failed") ); 
 307     if (style 
& wxSL_VERTICAL
) 
 308         m_scale 
= gtk_vscale_new( NULL 
); 
 310         m_scale 
= gtk_hscale_new( NULL 
); 
 311     g_object_ref(m_scale
); 
 313     if (style 
& wxSL_MIN_MAX_LABELS
) 
 315         gtk_widget_show( m_scale 
); 
 317         if (style 
& wxSL_VERTICAL
) 
 318             m_widget 
= gtk_hbox_new(false, 0); 
 320             m_widget 
= gtk_vbox_new(false, 0); 
 321         g_object_ref(m_widget
); 
 322         gtk_widget_show( m_widget 
); 
 323         gtk_container_add( GTK_CONTAINER(m_widget
), m_scale 
); 
 326         if (style 
& wxSL_VERTICAL
) 
 327             box 
= gtk_vbox_new(false,0); 
 329             box 
= gtk_hbox_new(false,0); 
 331         gtk_widget_show(box
); 
 332         gtk_container_add( GTK_CONTAINER(m_widget
), box 
); 
 334         m_minLabel 
= gtk_label_new(NULL
); 
 335         g_object_ref(m_minLabel
); 
 336         gtk_widget_show( m_minLabel 
); 
 337         gtk_container_add( GTK_CONTAINER(box
), m_minLabel 
); 
 338         gtk_box_set_child_packing( GTK_BOX(box
), m_minLabel
, FALSE
, FALSE
, 0, GTK_PACK_START 
); 
 340         // expanding empty space between the min/max labels 
 341         GtkWidget 
*space 
= gtk_label_new(NULL
); 
 343         gtk_widget_show( space 
); 
 344         gtk_container_add( GTK_CONTAINER(box
), space 
); 
 345         gtk_box_set_child_packing( GTK_BOX(box
), space
, TRUE
, FALSE
, 0, GTK_PACK_START 
); 
 347         m_maxLabel 
= gtk_label_new(NULL
); 
 348         g_object_ref(m_maxLabel
); 
 349         gtk_widget_show( m_maxLabel 
); 
 350         gtk_container_add( GTK_CONTAINER(box
), m_maxLabel 
); 
 351         gtk_box_set_child_packing( GTK_BOX(box
), m_maxLabel
, FALSE
, FALSE
, 0, GTK_PACK_END 
); 
 360     const bool showValueLabel 
= (style 
& wxSL_VALUE_LABEL
) != 0; 
 361     gtk_scale_set_draw_value(GTK_SCALE (m_scale
), showValueLabel 
); 
 362     if ( showValueLabel 
) 
 364         // position the label appropriately: notice that wxSL_DIRECTION flags 
 365         // specify the position of the ticks, not label, under MSW and so the 
 366         // label is on the opposite side 
 367         GtkPositionType posLabel
; 
 368         if ( style 
& wxSL_VERTICAL 
) 
 370             if ( style 
& wxSL_LEFT 
) 
 371                 posLabel 
= GTK_POS_RIGHT
; 
 372             else // if ( style & wxSL_RIGHT ) -- this is also the default 
 373                 posLabel 
= GTK_POS_LEFT
; 
 375         else // horizontal slider 
 377             if ( style 
& wxSL_TOP 
) 
 378                 posLabel 
= GTK_POS_BOTTOM
; 
 379             else // if ( style & wxSL_BOTTOM) -- this is again the default 
 380                 posLabel 
= GTK_POS_TOP
; 
 383         gtk_scale_set_value_pos( GTK_SCALE(m_scale
), posLabel 
); 
 386     // Keep full precision in position value 
 387     gtk_scale_set_digits(GTK_SCALE (m_scale
), -1); 
 389     if (style 
& wxSL_INVERSE
) 
 390         gtk_range_set_inverted( GTK_RANGE(m_scale
), TRUE 
); 
 392     g_signal_connect(m_scale
, "button_press_event", G_CALLBACK(gtk_button_press_event
), this); 
 393     g_signal_connect(m_scale
, "button_release_event", G_CALLBACK(gtk_button_release_event
), this); 
 394     g_signal_connect(m_scale
, "move_slider", G_CALLBACK(gtk_move_slider
), this); 
 395     g_signal_connect(m_scale
, "format_value", G_CALLBACK(gtk_format_value
), NULL
); 
 396     g_signal_connect(m_scale
, "value_changed", G_CALLBACK(gtk_value_changed
), this); 
 397     gulong handler_id 
= g_signal_connect(m_scale
, "event_after", G_CALLBACK(gtk_event_after
), this); 
 398     g_signal_handler_block(m_scale
, handler_id
); 
 400     SetRange( minValue
, maxValue 
); 
 402     // don't call the public SetValue() as it won't do anything unless the 
 403     // value really changed 
 404     GTKSetValue( value 
); 
 406     m_parent
->DoAddChild( this ); 
 413 void wxSlider::GTKDisableEvents() 
 415     m_blockScrollEvent 
= true; 
 418 void wxSlider::GTKEnableEvents() 
 420     m_blockScrollEvent 
= false; 
 423 bool wxSlider::GTKEventsDisabled() const 
 425    return m_blockScrollEvent
; 
 428 int wxSlider::GetValue() const 
 430     return wxRound(m_pos
); 
 433 void wxSlider::SetValue( int value 
) 
 435     if (GetValue() != value
) 
 439 void wxSlider::GTKSetValue(int value
) 
 442     gtk_range_set_value(GTK_RANGE (m_scale
), value
); 
 446 void wxSlider::SetRange( int minValue
, int maxValue 
) 
 449     if (minValue 
== maxValue
) 
 451     gtk_range_set_range(GTK_RANGE (m_scale
), minValue
, maxValue
); 
 452     gtk_range_set_increments(GTK_RANGE (m_scale
), 1, (maxValue 
- minValue 
+ 9) / 10); 
 455     if (HasFlag(wxSL_MIN_MAX_LABELS
)) 
 459         str
.Printf( "%d", minValue 
); 
 460         if (HasFlag(wxSL_INVERSE
)) 
 461             gtk_label_set_text( GTK_LABEL(m_maxLabel
), str
.utf8_str() ); 
 463             gtk_label_set_text( GTK_LABEL(m_minLabel
), str
.utf8_str() ); 
 465         str
.Printf( "%d", maxValue 
); 
 466         if (HasFlag(wxSL_INVERSE
)) 
 467             gtk_label_set_text( GTK_LABEL(m_minLabel
), str
.utf8_str() ); 
 469             gtk_label_set_text( GTK_LABEL(m_maxLabel
), str
.utf8_str() ); 
 474 int wxSlider::GetMin() const 
 476     return int(gtk_range_get_adjustment (GTK_RANGE (m_scale
))->lower
); 
 479 int wxSlider::GetMax() const 
 481     return int(gtk_range_get_adjustment (GTK_RANGE (m_scale
))->upper
); 
 484 void wxSlider::SetPageSize( int pageSize 
) 
 487     gtk_range_set_increments(GTK_RANGE (m_scale
), GetLineSize(), pageSize
); 
 491 int wxSlider::GetPageSize() const 
 493     return int(gtk_range_get_adjustment (GTK_RANGE (m_scale
))->page_increment
); 
 496 // GTK does not support changing the size of the slider 
 497 void wxSlider::SetThumbLength(int) 
 501 int wxSlider::GetThumbLength() const 
 506 void wxSlider::SetLineSize( int lineSize 
) 
 509     gtk_range_set_increments(GTK_RANGE (m_scale
), lineSize
, GetPageSize()); 
 513 int wxSlider::GetLineSize() const 
 515     return int(gtk_range_get_adjustment (GTK_RANGE (m_scale
))->step_increment
); 
 518 GdkWindow 
*wxSlider::GTKGetWindow(wxArrayGdkWindows
& WXUNUSED(windows
)) const 
 520     return GTK_RANGE(m_scale
)->event_window
; 
 525 wxSlider::GetClassDefaultAttributes(wxWindowVariant 
WXUNUSED(variant
)) 
 527     return GetDefaultAttributesFromGTKWidget(gtk_vscale_new
); 
 530 #endif // wxUSE_SLIDER