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"
23 #include "wx/gtk/private/gtk2-compat.h"
25 //-----------------------------------------------------------------------------
27 //-----------------------------------------------------------------------------
29 extern bool g_blockEventsOnDrag
;
31 // ----------------------------------------------------------------------------
33 // ----------------------------------------------------------------------------
35 // process a scroll event
37 ProcessScrollEvent(wxSlider
*win
, wxEventType evtType
)
39 const int orient
= win
->HasFlag(wxSL_VERTICAL
) ? wxVERTICAL
42 const int value
= win
->GetValue();
44 // if we have any "special" event (i.e. the value changed by a line or a
45 // page), send this specific event first
46 if ( evtType
!= wxEVT_NULL
)
48 wxScrollEvent
event( evtType
, win
->GetId(), value
, orient
);
49 event
.SetEventObject( win
);
50 win
->HandleWindowEvent( event
);
53 // but, in any case, except if we're dragging the slider (and so the change
54 // is not definitive), send a generic "changed" event
55 if ( evtType
!= wxEVT_SCROLL_THUMBTRACK
)
57 wxScrollEvent
event(wxEVT_SCROLL_CHANGED
, win
->GetId(), value
, orient
);
58 event
.SetEventObject( win
);
59 win
->HandleWindowEvent( event
);
62 // and also generate a command event for compatibility
63 wxCommandEvent
event( wxEVT_COMMAND_SLIDER_UPDATED
, win
->GetId() );
64 event
.SetEventObject( win
);
65 event
.SetInt( value
);
66 win
->HandleWindowEvent( event
);
69 static inline wxEventType
GtkScrollTypeToWx(int scrollType
)
71 wxEventType eventType
;
74 case GTK_SCROLL_STEP_BACKWARD
:
75 case GTK_SCROLL_STEP_LEFT
:
76 case GTK_SCROLL_STEP_UP
:
77 eventType
= wxEVT_SCROLL_LINEUP
;
79 case GTK_SCROLL_STEP_DOWN
:
80 case GTK_SCROLL_STEP_FORWARD
:
81 case GTK_SCROLL_STEP_RIGHT
:
82 eventType
= wxEVT_SCROLL_LINEDOWN
;
84 case GTK_SCROLL_PAGE_BACKWARD
:
85 case GTK_SCROLL_PAGE_LEFT
:
86 case GTK_SCROLL_PAGE_UP
:
87 eventType
= wxEVT_SCROLL_PAGEUP
;
89 case GTK_SCROLL_PAGE_DOWN
:
90 case GTK_SCROLL_PAGE_FORWARD
:
91 case GTK_SCROLL_PAGE_RIGHT
:
92 eventType
= wxEVT_SCROLL_PAGEDOWN
;
94 case GTK_SCROLL_START
:
95 eventType
= wxEVT_SCROLL_TOP
;
98 eventType
= wxEVT_SCROLL_BOTTOM
;
100 case GTK_SCROLL_JUMP
:
101 eventType
= wxEVT_SCROLL_THUMBTRACK
;
104 wxFAIL_MSG(wxT("Unknown GtkScrollType"));
105 eventType
= wxEVT_NULL
;
111 // Determine if increment is the same as +/-x, allowing for some small
112 // difference due to possible inexactness in floating point arithmetic
113 static inline bool IsScrollIncrement(double increment
, double x
)
115 wxASSERT(increment
> 0);
116 const double tolerance
= 1.0 / 1024;
117 return fabs(increment
- fabs(x
)) < tolerance
;
120 //-----------------------------------------------------------------------------
122 //-----------------------------------------------------------------------------
126 gtk_value_changed(GtkRange
* range
, wxSlider
* win
)
128 const double value
= gtk_range_get_value(range
);
129 const double oldPos
= win
->m_pos
;
132 if (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
= value
- oldPos
;
155 const bool isDown
= diff
> 0;
157 GtkAdjustment
* adj
= gtk_range_get_adjustment(range
);
158 if (IsScrollIncrement(gtk_adjustment_get_page_increment(adj
), diff
))
160 eventType
= isDown
? wxEVT_SCROLL_PAGEDOWN
: wxEVT_SCROLL_PAGEUP
;
162 else if (wxIsSameDouble(value
, 0))
164 eventType
= wxEVT_SCROLL_PAGEUP
;
166 else if (wxIsSameDouble(value
, gtk_adjustment_get_upper(adj
)))
168 eventType
= wxEVT_SCROLL_PAGEDOWN
;
172 // Assume track event
173 eventType
= wxEVT_SCROLL_THUMBTRACK
;
174 // Remember that we're tracking
175 win
->m_isScrolling
= true;
179 win
->m_scrollEventType
= GTK_SCROLL_NONE
;
181 // If integral position has changed
182 if (wxRound(oldPos
) != wxRound(value
))
184 ProcessScrollEvent(win
, eventType
);
185 win
->m_needThumbRelease
= eventType
== wxEVT_SCROLL_THUMBTRACK
;
190 //-----------------------------------------------------------------------------
191 // "move_slider" (keyboard event)
192 //-----------------------------------------------------------------------------
196 gtk_move_slider(GtkRange
*, GtkScrollType scrollType
, wxSlider
* win
)
198 // Save keyboard scroll type for "value_changed" handler
199 win
->m_scrollEventType
= scrollType
;
203 //-----------------------------------------------------------------------------
204 // "button_press_event"
205 //-----------------------------------------------------------------------------
209 gtk_button_press_event(GtkWidget
*, GdkEventButton
*, wxSlider
* win
)
211 win
->m_mouseButtonDown
= true;
217 //-----------------------------------------------------------------------------
219 //-----------------------------------------------------------------------------
223 gtk_event_after(GtkRange
* range
, GdkEvent
* event
, wxSlider
* win
)
225 if (event
->type
== GDK_BUTTON_RELEASE
)
227 g_signal_handlers_block_by_func(range
, (gpointer
) gtk_event_after
, win
);
229 if (win
->m_needThumbRelease
)
231 win
->m_needThumbRelease
= false;
232 ProcessScrollEvent(win
, wxEVT_SCROLL_THUMBRELEASE
);
234 // Keep slider at an integral position
235 win
->GTKDisableEvents();
236 gtk_range_set_value(GTK_RANGE (win
->m_scale
), win
->GetValue());
237 win
->GTKEnableEvents();
242 //-----------------------------------------------------------------------------
243 // "button_release_event"
244 //-----------------------------------------------------------------------------
248 gtk_button_release_event(GtkRange
* range
, GdkEventButton
*, wxSlider
* win
)
250 win
->m_mouseButtonDown
= false;
251 if (win
->m_isScrolling
)
253 win
->m_isScrolling
= false;
254 g_signal_handlers_unblock_by_func(range
, (gpointer
) gtk_event_after
, win
);
260 //-----------------------------------------------------------------------------
262 //-----------------------------------------------------------------------------
265 static gchar
* gtk_format_value(GtkScale
*, double value
, void*)
267 // Format value as nearest integer
268 return g_strdup_printf("%d", wxRound(value
));
272 //-----------------------------------------------------------------------------
274 //-----------------------------------------------------------------------------
281 wxSlider::~wxSlider()
283 if (m_scale
&& m_scale
!= m_widget
)
284 GTKDisconnect(m_scale
);
287 bool wxSlider::Create(wxWindow
*parent
,
295 const wxValidator
& validator
,
296 const wxString
& name
)
299 m_scrollEventType
= GTK_SCROLL_NONE
;
300 m_needThumbRelease
= false;
301 m_blockScrollEvent
= false;
303 if (!PreCreation( parent
, pos
, size
) ||
304 !CreateBase( parent
, id
, pos
, size
, style
, validator
, name
))
306 wxFAIL_MSG( wxT("wxSlider creation failed") );
310 const bool isVertical
= (style
& wxSL_VERTICAL
) != 0;
311 m_scale
= gtk_scale_new(GtkOrientation(isVertical
), NULL
);
313 if (style
& wxSL_MIN_MAX_LABELS
)
315 gtk_widget_show( m_scale
);
317 m_widget
= gtk_box_new(GtkOrientation(!isVertical
), 0);
318 gtk_box_pack_start(GTK_BOX(m_widget
), m_scale
, true, true, 0);
320 GtkWidget
* box
= gtk_box_new(GtkOrientation(isVertical
), 0);
321 gtk_widget_show(box
);
322 gtk_box_pack_start(GTK_BOX(m_widget
), box
, true, true, 0);
324 m_minLabel
= gtk_label_new(NULL
);
325 gtk_widget_show( m_minLabel
);
326 gtk_box_pack_start(GTK_BOX(box
), m_minLabel
, false, false, 0);
328 // expanding empty space between the min/max labels
329 GtkWidget
*space
= gtk_label_new(NULL
);
330 gtk_widget_show( space
);
331 gtk_box_pack_start(GTK_BOX(box
), space
, true, false, 0);
333 m_maxLabel
= gtk_label_new(NULL
);
334 gtk_widget_show( m_maxLabel
);
335 gtk_box_pack_end(GTK_BOX(box
), m_maxLabel
, false, false, 0);
343 g_object_ref(m_widget
);
345 const bool showValueLabel
= (style
& wxSL_VALUE_LABEL
) != 0;
346 gtk_scale_set_draw_value(GTK_SCALE (m_scale
), showValueLabel
);
347 if ( showValueLabel
)
349 // position the label appropriately: notice that wxSL_DIRECTION flags
350 // specify the position of the ticks, not label, under MSW and so the
351 // label is on the opposite side
352 GtkPositionType posLabel
;
353 if ( style
& wxSL_VERTICAL
)
355 if ( style
& wxSL_LEFT
)
356 posLabel
= GTK_POS_RIGHT
;
357 else // if ( style & wxSL_RIGHT ) -- this is also the default
358 posLabel
= GTK_POS_LEFT
;
360 else // horizontal slider
362 if ( style
& wxSL_TOP
)
363 posLabel
= GTK_POS_BOTTOM
;
364 else // if ( style & wxSL_BOTTOM) -- this is again the default
365 posLabel
= GTK_POS_TOP
;
368 gtk_scale_set_value_pos( GTK_SCALE(m_scale
), posLabel
);
371 // Keep full precision in position value
372 gtk_scale_set_digits(GTK_SCALE (m_scale
), -1);
374 if (style
& wxSL_INVERSE
)
375 gtk_range_set_inverted( GTK_RANGE(m_scale
), TRUE
);
377 g_signal_connect(m_scale
, "button_press_event", G_CALLBACK(gtk_button_press_event
), this);
378 g_signal_connect(m_scale
, "button_release_event", G_CALLBACK(gtk_button_release_event
), this);
379 g_signal_connect(m_scale
, "move_slider", G_CALLBACK(gtk_move_slider
), this);
380 g_signal_connect(m_scale
, "format_value", G_CALLBACK(gtk_format_value
), NULL
);
381 g_signal_connect(m_scale
, "value_changed", G_CALLBACK(gtk_value_changed
), this);
382 gulong handler_id
= g_signal_connect(m_scale
, "event_after", G_CALLBACK(gtk_event_after
), this);
383 g_signal_handler_block(m_scale
, handler_id
);
385 SetRange( minValue
, maxValue
);
387 // don't call the public SetValue() as it won't do anything unless the
388 // value really changed
389 GTKSetValue( value
);
391 m_parent
->DoAddChild( this );
398 void wxSlider::GTKDisableEvents()
400 m_blockScrollEvent
= true;
403 void wxSlider::GTKEnableEvents()
405 m_blockScrollEvent
= false;
408 bool wxSlider::GTKEventsDisabled() const
410 return m_blockScrollEvent
;
413 int wxSlider::GetValue() const
415 return wxRound(m_pos
);
418 void wxSlider::SetValue( int value
)
420 if (GetValue() != value
)
424 void wxSlider::GTKSetValue(int value
)
427 gtk_range_set_value(GTK_RANGE (m_scale
), value
);
428 // GTK only updates value label if handle moves at least 1 pixel
429 gtk_widget_queue_draw(m_scale
);
433 void wxSlider::SetRange( int minValue
, int maxValue
)
436 if (minValue
== maxValue
)
438 gtk_range_set_range(GTK_RANGE (m_scale
), minValue
, maxValue
);
439 gtk_range_set_increments(GTK_RANGE (m_scale
), 1, (maxValue
- minValue
+ 9) / 10);
442 if (HasFlag(wxSL_MIN_MAX_LABELS
))
446 str
.Printf( "%d", minValue
);
447 if (HasFlag(wxSL_INVERSE
))
448 gtk_label_set_text( GTK_LABEL(m_maxLabel
), str
.utf8_str() );
450 gtk_label_set_text( GTK_LABEL(m_minLabel
), str
.utf8_str() );
452 str
.Printf( "%d", maxValue
);
453 if (HasFlag(wxSL_INVERSE
))
454 gtk_label_set_text( GTK_LABEL(m_minLabel
), str
.utf8_str() );
456 gtk_label_set_text( GTK_LABEL(m_maxLabel
), str
.utf8_str() );
461 int wxSlider::GetMin() const
463 GtkAdjustment
* adj
= gtk_range_get_adjustment(GTK_RANGE(m_scale
));
464 return int(gtk_adjustment_get_lower(adj
));
467 int wxSlider::GetMax() const
469 GtkAdjustment
* adj
= gtk_range_get_adjustment(GTK_RANGE(m_scale
));
470 return int(gtk_adjustment_get_upper(adj
));
473 void wxSlider::SetPageSize( int pageSize
)
476 gtk_range_set_increments(GTK_RANGE (m_scale
), GetLineSize(), pageSize
);
480 int wxSlider::GetPageSize() const
482 GtkAdjustment
* adj
= gtk_range_get_adjustment(GTK_RANGE(m_scale
));
483 return int(gtk_adjustment_get_page_increment(adj
));
486 // GTK does not support changing the size of the slider
487 void wxSlider::SetThumbLength(int)
491 int wxSlider::GetThumbLength() const
496 void wxSlider::SetLineSize( int lineSize
)
499 gtk_range_set_increments(GTK_RANGE (m_scale
), lineSize
, GetPageSize());
503 int wxSlider::GetLineSize() const
505 GtkAdjustment
* adj
= gtk_range_get_adjustment(GTK_RANGE(m_scale
));
506 return int(gtk_adjustment_get_step_increment(adj
));
509 GdkWindow
*wxSlider::GTKGetWindow(wxArrayGdkWindows
& WXUNUSED(windows
)) const
512 // no access to internal GdkWindows
515 return GTK_RANGE(m_scale
)->event_window
;
521 wxSlider::GetClassDefaultAttributes(wxWindowVariant
WXUNUSED(variant
))
523 return GetDefaultAttributesFromGTKWidget(gtk_scale_new(GTK_ORIENTATION_VERTICAL
, NULL
));
526 #endif // wxUSE_SLIDER