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"
21 #include "wx/gtk/private.h"
23 //-----------------------------------------------------------------------------
25 //-----------------------------------------------------------------------------
27 extern bool g_blockEventsOnDrag
;
29 // ----------------------------------------------------------------------------
31 // ----------------------------------------------------------------------------
33 // process a scroll event
35 ProcessScrollEvent(wxSlider
*win
, wxEventType evtType
)
37 const int orient
= win
->HasFlag(wxSL_VERTICAL
) ? wxVERTICAL
40 const int value
= win
->GetValue();
42 // if we have any "special" event (i.e. the value changed by a line or a
43 // page), send this specific event first
44 if ( evtType
!= wxEVT_NULL
)
46 wxScrollEvent
event( evtType
, win
->GetId(), value
, orient
);
47 event
.SetEventObject( win
);
48 win
->GetEventHandler()->ProcessEvent( event
);
51 // but, in any case, except if we're dragging the slider (and so the change
52 // is not definitive), send a generic "changed" event
53 if ( evtType
!= wxEVT_SCROLL_THUMBTRACK
)
55 wxScrollEvent
event(wxEVT_SCROLL_CHANGED
, win
->GetId(), value
, orient
);
56 event
.SetEventObject( win
);
57 win
->GetEventHandler()->ProcessEvent( event
);
60 // and also generate a command event for compatibility
61 wxCommandEvent
event( wxEVT_COMMAND_SLIDER_UPDATED
, win
->GetId() );
62 event
.SetEventObject( win
);
63 event
.SetInt( value
);
64 win
->GetEventHandler()->ProcessEvent( event
);
67 static inline wxEventType
GtkScrollTypeToWx(int scrollType
)
69 wxEventType eventType
;
72 case GTK_SCROLL_STEP_BACKWARD
:
73 case GTK_SCROLL_STEP_LEFT
:
74 case GTK_SCROLL_STEP_UP
:
75 eventType
= wxEVT_SCROLL_LINEUP
;
77 case GTK_SCROLL_STEP_DOWN
:
78 case GTK_SCROLL_STEP_FORWARD
:
79 case GTK_SCROLL_STEP_RIGHT
:
80 eventType
= wxEVT_SCROLL_LINEDOWN
;
82 case GTK_SCROLL_PAGE_BACKWARD
:
83 case GTK_SCROLL_PAGE_LEFT
:
84 case GTK_SCROLL_PAGE_UP
:
85 eventType
= wxEVT_SCROLL_PAGEUP
;
87 case GTK_SCROLL_PAGE_DOWN
:
88 case GTK_SCROLL_PAGE_FORWARD
:
89 case GTK_SCROLL_PAGE_RIGHT
:
90 eventType
= wxEVT_SCROLL_PAGEDOWN
;
92 case GTK_SCROLL_START
:
93 eventType
= wxEVT_SCROLL_TOP
;
96 eventType
= wxEVT_SCROLL_BOTTOM
;
99 eventType
= wxEVT_SCROLL_THUMBTRACK
;
102 wxFAIL_MSG(_T("Unknown GtkScrollType"));
103 eventType
= wxEVT_NULL
;
109 // Determine if increment is the same as +/-x, allowing for some small
110 // difference due to possible inexactness in floating point arithmetic
111 static inline bool IsScrollIncrement(double increment
, double x
)
113 wxASSERT(increment
> 0);
114 const double tolerance
= 1.0 / 1024;
115 return fabs(increment
- fabs(x
)) < tolerance
;
118 //-----------------------------------------------------------------------------
120 //-----------------------------------------------------------------------------
124 gtk_value_changed(GtkRange
* range
, wxSlider
* win
)
126 if (g_isIdle
) wxapp_install_idle_handler();
128 if (!win
->m_hasVMT
) return;
129 if (g_blockEventsOnDrag
) return;
131 GtkAdjustment
* adj
= range
->adjustment
;
132 const int pos
= int(round(adj
->value
));
133 const double oldPos
= win
->m_pos
;
134 win
->m_pos
= adj
->value
;
135 if (win
->m_blockScrollEvent
)
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 (int(round(oldPos
)) != pos
)
183 wxCHECK_RET(eventType
!= wxEVT_NULL
, _T("Unknown slider scroll event type"));
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 // Single shot idle callback, to generate thumb release event, and
218 // truncate position to integral value. Idle callback is used
219 // because position cannot be changed from button release event.
222 idle_thumbrelease(void* data
)
224 wxSlider
* win
= (wxSlider
*)data
;
225 win
->m_isScrolling
= false;
226 if (win
->m_needThumbRelease
)
228 win
->m_needThumbRelease
= false;
229 ProcessScrollEvent(win
, wxEVT_SCROLL_THUMBRELEASE
);
231 // Keep slider at an integral position
232 win
->BlockScrollEvent();
233 gtk_range_set_value((GtkRange
*)win
->m_widget
, win
->GetValue());
234 win
->UnblockScrollEvent();
239 //-----------------------------------------------------------------------------
240 // "button_release_event"
241 //-----------------------------------------------------------------------------
245 gtk_button_release_event(GtkWidget
*, GdkEventButton
*, wxSlider
* win
)
247 win
->m_mouseButtonDown
= false;
248 if (win
->m_isScrolling
)
250 g_idle_add(idle_thumbrelease
, win
);
256 //-----------------------------------------------------------------------------
258 //-----------------------------------------------------------------------------
261 static gchar
* gtk_format_value(GtkScale
*, double value
, void*)
263 // Format value as nearest integer
264 return g_strdup_printf("%d", int(round(value
)));
268 //-----------------------------------------------------------------------------
270 //-----------------------------------------------------------------------------
272 IMPLEMENT_DYNAMIC_CLASS(wxSlider
,wxControl
)
277 m_scrollEventType
= 0;
278 m_needThumbRelease
= false;
281 bool wxSlider::Create(wxWindow
*parent
, wxWindowID id
,
282 int value
, int minValue
, int maxValue
,
283 const wxPoint
& pos
, const wxSize
& size
,
284 long style
, const wxValidator
& validator
, const wxString
& name
)
286 m_acceptsFocus
= true;
289 if (!PreCreation( parent
, pos
, size
) ||
290 !CreateBase( parent
, id
, pos
, size
, style
, validator
, name
))
292 wxFAIL_MSG( wxT("wxSlider creation failed") );
297 m_scrollEventType
= 0;
298 m_needThumbRelease
= false;
300 if (style
& wxSL_VERTICAL
)
301 m_widget
= gtk_vscale_new( (GtkAdjustment
*) NULL
);
303 m_widget
= gtk_hscale_new( (GtkAdjustment
*) NULL
);
305 gtk_scale_set_draw_value((GtkScale
*)m_widget
, (style
& wxSL_LABELS
) != 0);
306 // Keep full precision in position value
307 gtk_scale_set_digits((GtkScale
*)m_widget
, -1);
309 if (style
& wxSL_INVERSE
)
310 gtk_range_set_inverted( GTK_RANGE(m_widget
), TRUE
);
312 g_signal_connect(m_widget
, "button_press_event", G_CALLBACK(gtk_button_press_event
), this);
313 g_signal_connect(m_widget
, "button_release_event", G_CALLBACK(gtk_button_release_event
), this);
314 g_signal_connect(m_widget
, "move_slider", G_CALLBACK(gtk_move_slider
), this);
315 g_signal_connect(m_widget
, "format_value", G_CALLBACK(gtk_format_value
), NULL
);
316 g_signal_connect(m_widget
, "value_changed", G_CALLBACK(gtk_value_changed
), this);
318 SetRange( minValue
, maxValue
);
321 m_parent
->DoAddChild( this );
328 int wxSlider::GetValue() const
330 return int(round(m_pos
));
333 void wxSlider::SetValue( int value
)
335 if (GetValue() != value
)
338 gtk_range_set_value((GtkRange
*)m_widget
, value
);
339 UnblockScrollEvent();
343 void wxSlider::SetRange( int minValue
, int maxValue
)
346 gtk_range_set_range((GtkRange
*)m_widget
, minValue
, maxValue
);
347 gtk_range_set_increments((GtkRange
*)m_widget
, 1, (maxValue
- minValue
+ 9) / 10);
348 UnblockScrollEvent();
351 int wxSlider::GetMin() const
353 return int(((GtkRange
*)m_widget
)->adjustment
->lower
);
356 int wxSlider::GetMax() const
358 return int(((GtkRange
*)m_widget
)->adjustment
->upper
);
361 void wxSlider::SetPageSize( int pageSize
)
364 gtk_range_set_increments((GtkRange
*)m_widget
, 1, pageSize
);
365 UnblockScrollEvent();
368 int wxSlider::GetPageSize() const
370 return int(((GtkRange
*)m_widget
)->adjustment
->page_increment
);
373 // GTK does not support changing the size of the slider
374 void wxSlider::SetThumbLength(int)
378 int wxSlider::GetThumbLength() const
383 void wxSlider::SetLineSize( int WXUNUSED(lineSize
) )
387 int wxSlider::GetLineSize() const
392 bool wxSlider::IsOwnGtkWindow( GdkWindow
*window
)
394 GtkRange
*range
= GTK_RANGE(m_widget
);
395 return (range
->event_window
== window
);
400 wxSlider::GetClassDefaultAttributes(wxWindowVariant
WXUNUSED(variant
))
402 return GetDefaultAttributesFromGTKWidget(gtk_vscale_new
);
405 #endif // wxUSE_SLIDER