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"
22 #include "wx/gtk/private.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
->GetEventHandler()->ProcessEvent( 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
->GetEventHandler()->ProcessEvent( 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
->GetEventHandler()->ProcessEvent( 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(_T("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 if (g_isIdle
) wxapp_install_idle_handler();
129 GtkAdjustment
* adj
= gtk_range_get_adjustment (range
);
130 const int pos
= wxRound(adj
->value
);
131 const double oldPos
= win
->m_pos
;
132 win
->m_pos
= adj
->value
;
134 if (!win
->m_hasVMT
|| g_blockEventsOnDrag
)
137 if (win
->m_blockScrollEvent
)
139 win
->m_scrollEventType
= GTK_SCROLL_NONE
;
143 wxEventType eventType
= wxEVT_NULL
;
144 if (win
->m_isScrolling
)
146 eventType
= wxEVT_SCROLL_THUMBTRACK
;
148 else if (win
->m_scrollEventType
!= GTK_SCROLL_NONE
)
150 // Scroll event from "move-slider" (keyboard)
151 eventType
= GtkScrollTypeToWx(win
->m_scrollEventType
);
153 else if (win
->m_mouseButtonDown
)
155 // Difference from last change event
156 const double diff
= adj
->value
- oldPos
;
157 const bool isDown
= diff
> 0;
159 if (IsScrollIncrement(adj
->page_increment
, diff
))
161 eventType
= isDown
? wxEVT_SCROLL_PAGEDOWN
: wxEVT_SCROLL_PAGEUP
;
163 else if (wxIsSameDouble(adj
->value
, 0))
165 eventType
= wxEVT_SCROLL_PAGEUP
;
167 else if (wxIsSameDouble(adj
->value
, adj
->upper
))
169 eventType
= wxEVT_SCROLL_PAGEDOWN
;
173 // Assume track event
174 eventType
= wxEVT_SCROLL_THUMBTRACK
;
175 // Remember that we're tracking
176 win
->m_isScrolling
= true;
180 win
->m_scrollEventType
= GTK_SCROLL_NONE
;
182 // If integral position has changed
183 if (wxRound(oldPos
) != pos
)
185 wxCHECK_RET(eventType
!= wxEVT_NULL
, _T("Unknown slider scroll event type"));
186 ProcessScrollEvent(win
, eventType
);
187 win
->m_needThumbRelease
= eventType
== wxEVT_SCROLL_THUMBTRACK
;
192 //-----------------------------------------------------------------------------
193 // "move_slider" (keyboard event)
194 //-----------------------------------------------------------------------------
198 gtk_move_slider(GtkRange
*, GtkScrollType scrollType
, wxSlider
* win
)
200 // Save keyboard scroll type for "value_changed" handler
201 win
->m_scrollEventType
= scrollType
;
205 //-----------------------------------------------------------------------------
206 // "button_press_event"
207 //-----------------------------------------------------------------------------
211 gtk_button_press_event(GtkWidget
*, GdkEventButton
*, wxSlider
* win
)
213 win
->m_mouseButtonDown
= true;
219 //-----------------------------------------------------------------------------
221 //-----------------------------------------------------------------------------
225 gtk_event_after(GtkRange
* range
, GdkEvent
* event
, wxSlider
* win
)
227 if (event
->type
== GDK_BUTTON_RELEASE
)
229 g_signal_handlers_block_by_func(range
, (gpointer
) gtk_event_after
, win
);
231 if (win
->m_needThumbRelease
)
233 win
->m_needThumbRelease
= false;
234 ProcessScrollEvent(win
, wxEVT_SCROLL_THUMBRELEASE
);
236 // Keep slider at an integral position
237 win
->BlockScrollEvent();
238 gtk_range_set_value(GTK_RANGE (win
->m_widget
), win
->GetValue());
239 win
->UnblockScrollEvent();
244 //-----------------------------------------------------------------------------
245 // "button_release_event"
246 //-----------------------------------------------------------------------------
250 gtk_button_release_event(GtkRange
* range
, GdkEventButton
*, wxSlider
* win
)
252 win
->m_mouseButtonDown
= false;
253 if (win
->m_isScrolling
)
255 win
->m_isScrolling
= false;
256 g_signal_handlers_unblock_by_func(range
, (gpointer
) gtk_event_after
, win
);
262 //-----------------------------------------------------------------------------
264 //-----------------------------------------------------------------------------
267 static gchar
* gtk_format_value(GtkScale
*, double value
, void*)
269 // Format value as nearest integer
270 return g_strdup_printf("%d", wxRound(value
));
274 //-----------------------------------------------------------------------------
276 //-----------------------------------------------------------------------------
278 IMPLEMENT_DYNAMIC_CLASS(wxSlider
,wxControl
)
283 m_scrollEventType
= 0;
284 m_needThumbRelease
= false;
287 bool wxSlider::Create(wxWindow
*parent
, wxWindowID id
,
288 int value
, int minValue
, int maxValue
,
289 const wxPoint
& pos
, const wxSize
& size
,
290 long style
, const wxValidator
& validator
, const wxString
& name
)
292 m_acceptsFocus
= true;
295 if (!PreCreation( parent
, pos
, size
) ||
296 !CreateBase( parent
, id
, pos
, size
, style
, validator
, name
))
298 wxFAIL_MSG( wxT("wxSlider creation failed") );
303 m_scrollEventType
= 0;
304 m_needThumbRelease
= false;
306 if (style
& wxSL_VERTICAL
)
307 m_widget
= gtk_vscale_new( (GtkAdjustment
*) NULL
);
309 m_widget
= gtk_hscale_new( (GtkAdjustment
*) NULL
);
311 gtk_scale_set_draw_value(GTK_SCALE (m_widget
), (style
& wxSL_LABELS
) != 0);
312 // Keep full precision in position value
313 gtk_scale_set_digits(GTK_SCALE (m_widget
), -1);
315 if (style
& wxSL_INVERSE
)
316 gtk_range_set_inverted( GTK_RANGE(m_widget
), TRUE
);
318 g_signal_connect(m_widget
, "button_press_event", G_CALLBACK(gtk_button_press_event
), this);
319 g_signal_connect(m_widget
, "button_release_event", G_CALLBACK(gtk_button_release_event
), this);
320 g_signal_connect(m_widget
, "move_slider", G_CALLBACK(gtk_move_slider
), this);
321 g_signal_connect(m_widget
, "format_value", G_CALLBACK(gtk_format_value
), NULL
);
322 g_signal_connect(m_widget
, "value_changed", G_CALLBACK(gtk_value_changed
), this);
324 handler_id
= g_signal_connect(
325 m_widget
, "event_after", G_CALLBACK(gtk_event_after
), this);
326 g_signal_handler_block(m_widget
, handler_id
);
328 SetRange( minValue
, maxValue
);
331 m_parent
->DoAddChild( this );
338 int wxSlider::GetValue() const
340 return wxRound(m_pos
);
343 void wxSlider::SetValue( int value
)
345 if (GetValue() != value
)
348 gtk_range_set_value(GTK_RANGE (m_widget
), value
);
349 UnblockScrollEvent();
353 void wxSlider::SetRange( int minValue
, int maxValue
)
356 gtk_range_set_range(GTK_RANGE (m_widget
), minValue
, maxValue
);
357 gtk_range_set_increments(GTK_RANGE (m_widget
), 1, (maxValue
- minValue
+ 9) / 10);
358 UnblockScrollEvent();
361 int wxSlider::GetMin() const
363 return int(gtk_range_get_adjustment (GTK_RANGE (m_widget
))->lower
);
366 int wxSlider::GetMax() const
368 return int(gtk_range_get_adjustment (GTK_RANGE (m_widget
))->upper
);
371 void wxSlider::SetPageSize( int pageSize
)
374 gtk_range_set_increments(GTK_RANGE (m_widget
), GetLineSize(), pageSize
);
375 UnblockScrollEvent();
378 int wxSlider::GetPageSize() const
380 return int(gtk_range_get_adjustment (GTK_RANGE (m_widget
))->page_increment
);
383 // GTK does not support changing the size of the slider
384 void wxSlider::SetThumbLength(int)
388 int wxSlider::GetThumbLength() const
393 void wxSlider::SetLineSize( int lineSize
)
396 gtk_range_set_increments(GTK_RANGE (m_widget
), lineSize
, GetPageSize());
397 UnblockScrollEvent();
400 int wxSlider::GetLineSize() const
402 return int(gtk_range_get_adjustment (GTK_RANGE (m_widget
))->step_increment
);
405 GdkWindow
*wxSlider::GTKGetWindow(wxArrayGdkWindows
& WXUNUSED(windows
)) const
407 return GTK_RANGE(m_widget
)->event_window
;
412 wxSlider::GetClassDefaultAttributes(wxWindowVariant
WXUNUSED(variant
))
414 return GetDefaultAttributesFromGTKWidget(gtk_vscale_new
);
417 #endif // wxUSE_SLIDER