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"
16 //Missing definition in OpenVMS C++ header files.
17 double round(double __x
);
20 #include "wx/slider.h"
26 #include "wx/gtk/private.h"
28 //-----------------------------------------------------------------------------
30 //-----------------------------------------------------------------------------
32 extern bool g_blockEventsOnDrag
;
34 // ----------------------------------------------------------------------------
36 // ----------------------------------------------------------------------------
38 // process a scroll event
40 ProcessScrollEvent(wxSlider
*win
, wxEventType evtType
)
42 const int orient
= win
->HasFlag(wxSL_VERTICAL
) ? wxVERTICAL
45 const int value
= win
->GetValue();
47 // if we have any "special" event (i.e. the value changed by a line or a
48 // page), send this specific event first
49 if ( evtType
!= wxEVT_NULL
)
51 wxScrollEvent
event( evtType
, win
->GetId(), value
, orient
);
52 event
.SetEventObject( win
);
53 win
->GetEventHandler()->ProcessEvent( event
);
56 // but, in any case, except if we're dragging the slider (and so the change
57 // is not definitive), send a generic "changed" event
58 if ( evtType
!= wxEVT_SCROLL_THUMBTRACK
)
60 wxScrollEvent
event(wxEVT_SCROLL_CHANGED
, win
->GetId(), value
, orient
);
61 event
.SetEventObject( win
);
62 win
->GetEventHandler()->ProcessEvent( event
);
65 // and also generate a command event for compatibility
66 wxCommandEvent
event( wxEVT_COMMAND_SLIDER_UPDATED
, win
->GetId() );
67 event
.SetEventObject( win
);
68 event
.SetInt( value
);
69 win
->GetEventHandler()->ProcessEvent( event
);
72 static inline wxEventType
GtkScrollTypeToWx(int scrollType
)
74 wxEventType eventType
;
77 case GTK_SCROLL_STEP_BACKWARD
:
78 case GTK_SCROLL_STEP_LEFT
:
79 case GTK_SCROLL_STEP_UP
:
80 eventType
= wxEVT_SCROLL_LINEUP
;
82 case GTK_SCROLL_STEP_DOWN
:
83 case GTK_SCROLL_STEP_FORWARD
:
84 case GTK_SCROLL_STEP_RIGHT
:
85 eventType
= wxEVT_SCROLL_LINEDOWN
;
87 case GTK_SCROLL_PAGE_BACKWARD
:
88 case GTK_SCROLL_PAGE_LEFT
:
89 case GTK_SCROLL_PAGE_UP
:
90 eventType
= wxEVT_SCROLL_PAGEUP
;
92 case GTK_SCROLL_PAGE_DOWN
:
93 case GTK_SCROLL_PAGE_FORWARD
:
94 case GTK_SCROLL_PAGE_RIGHT
:
95 eventType
= wxEVT_SCROLL_PAGEDOWN
;
97 case GTK_SCROLL_START
:
98 eventType
= wxEVT_SCROLL_TOP
;
101 eventType
= wxEVT_SCROLL_BOTTOM
;
103 case GTK_SCROLL_JUMP
:
104 eventType
= wxEVT_SCROLL_THUMBTRACK
;
107 wxFAIL_MSG(_T("Unknown GtkScrollType"));
108 eventType
= wxEVT_NULL
;
114 // Determine if increment is the same as +/-x, allowing for some small
115 // difference due to possible inexactness in floating point arithmetic
116 static inline bool IsScrollIncrement(double increment
, double x
)
118 wxASSERT(increment
> 0);
119 const double tolerance
= 1.0 / 1024;
120 return fabs(increment
- fabs(x
)) < tolerance
;
123 //-----------------------------------------------------------------------------
125 //-----------------------------------------------------------------------------
129 gtk_value_changed(GtkRange
* range
, wxSlider
* win
)
131 if (g_isIdle
) wxapp_install_idle_handler();
133 if (!win
->m_hasVMT
) return;
134 if (g_blockEventsOnDrag
) return;
136 GtkAdjustment
* adj
= range
->adjustment
;
137 const int pos
= int(round(adj
->value
));
138 const double oldPos
= win
->m_pos
;
139 win
->m_pos
= adj
->value
;
140 if (win
->m_blockScrollEvent
)
142 win
->m_scrollEventType
= GTK_SCROLL_NONE
;
146 wxEventType eventType
= wxEVT_NULL
;
147 if (win
->m_isScrolling
)
149 eventType
= wxEVT_SCROLL_THUMBTRACK
;
151 else if (win
->m_scrollEventType
!= GTK_SCROLL_NONE
)
153 // Scroll event from "move-slider" (keyboard)
154 eventType
= GtkScrollTypeToWx(win
->m_scrollEventType
);
156 else if (win
->m_mouseButtonDown
)
158 // Difference from last change event
159 const double diff
= adj
->value
- oldPos
;
160 const bool isDown
= diff
> 0;
162 if (IsScrollIncrement(adj
->page_increment
, diff
))
164 eventType
= isDown
? wxEVT_SCROLL_PAGEDOWN
: wxEVT_SCROLL_PAGEUP
;
166 else if (wxIsSameDouble(adj
->value
, 0))
168 eventType
= wxEVT_SCROLL_PAGEUP
;
170 else if (wxIsSameDouble(adj
->value
, adj
->upper
))
172 eventType
= wxEVT_SCROLL_PAGEDOWN
;
176 // Assume track event
177 eventType
= wxEVT_SCROLL_THUMBTRACK
;
178 // Remember that we're tracking
179 win
->m_isScrolling
= true;
183 win
->m_scrollEventType
= GTK_SCROLL_NONE
;
185 // If integral position has changed
186 if (int(round(oldPos
)) != pos
)
188 wxCHECK_RET(eventType
!= wxEVT_NULL
, _T("Unknown slider scroll event type"));
189 ProcessScrollEvent(win
, eventType
);
190 win
->m_needThumbRelease
= eventType
== wxEVT_SCROLL_THUMBTRACK
;
195 //-----------------------------------------------------------------------------
196 // "move_slider" (keyboard event)
197 //-----------------------------------------------------------------------------
201 gtk_move_slider(GtkRange
*, GtkScrollType scrollType
, wxSlider
* win
)
203 // Save keyboard scroll type for "value_changed" handler
204 win
->m_scrollEventType
= scrollType
;
208 //-----------------------------------------------------------------------------
209 // "button_press_event"
210 //-----------------------------------------------------------------------------
214 gtk_button_press_event(GtkWidget
*, GdkEventButton
*, wxSlider
* win
)
216 win
->m_mouseButtonDown
= true;
222 //-----------------------------------------------------------------------------
224 //-----------------------------------------------------------------------------
228 gtk_event_after(GtkRange
* range
, GdkEvent
* event
, wxSlider
* win
)
230 if (event
->type
== GDK_BUTTON_RELEASE
)
232 g_signal_handlers_block_by_func(range
, (void*)gtk_event_after
, win
);
234 if (win
->m_needThumbRelease
)
236 win
->m_needThumbRelease
= false;
237 ProcessScrollEvent(win
, wxEVT_SCROLL_THUMBRELEASE
);
239 // Keep slider at an integral position
240 win
->BlockScrollEvent();
241 gtk_range_set_value((GtkRange
*)win
->m_widget
, win
->GetValue());
242 win
->UnblockScrollEvent();
247 //-----------------------------------------------------------------------------
248 // "button_release_event"
249 //-----------------------------------------------------------------------------
253 gtk_button_release_event(GtkRange
* range
, GdkEventButton
*, wxSlider
* win
)
255 win
->m_mouseButtonDown
= false;
256 if (win
->m_isScrolling
)
258 win
->m_isScrolling
= false;
259 g_signal_handlers_unblock_by_func(range
, (void*)gtk_event_after
, win
);
265 //-----------------------------------------------------------------------------
267 //-----------------------------------------------------------------------------
270 static gchar
* gtk_format_value(GtkScale
*, double value
, void*)
272 // Format value as nearest integer
273 return g_strdup_printf("%d", int(round(value
)));
277 //-----------------------------------------------------------------------------
279 //-----------------------------------------------------------------------------
281 IMPLEMENT_DYNAMIC_CLASS(wxSlider
,wxControl
)
286 m_scrollEventType
= 0;
287 m_needThumbRelease
= false;
290 bool wxSlider::Create(wxWindow
*parent
, wxWindowID id
,
291 int value
, int minValue
, int maxValue
,
292 const wxPoint
& pos
, const wxSize
& size
,
293 long style
, const wxValidator
& validator
, const wxString
& name
)
295 m_acceptsFocus
= true;
298 if (!PreCreation( parent
, pos
, size
) ||
299 !CreateBase( parent
, id
, pos
, size
, style
, validator
, name
))
301 wxFAIL_MSG( wxT("wxSlider creation failed") );
306 m_scrollEventType
= 0;
307 m_needThumbRelease
= false;
309 if (style
& wxSL_VERTICAL
)
310 m_widget
= gtk_vscale_new( (GtkAdjustment
*) NULL
);
312 m_widget
= gtk_hscale_new( (GtkAdjustment
*) NULL
);
314 gtk_scale_set_draw_value((GtkScale
*)m_widget
, (style
& wxSL_LABELS
) != 0);
315 // Keep full precision in position value
316 gtk_scale_set_digits((GtkScale
*)m_widget
, -1);
318 if (style
& wxSL_INVERSE
)
319 gtk_range_set_inverted( GTK_RANGE(m_widget
), TRUE
);
321 g_signal_connect(m_widget
, "button_press_event", G_CALLBACK(gtk_button_press_event
), this);
322 g_signal_connect(m_widget
, "button_release_event", G_CALLBACK(gtk_button_release_event
), this);
323 g_signal_connect(m_widget
, "move_slider", G_CALLBACK(gtk_move_slider
), this);
324 g_signal_connect(m_widget
, "format_value", G_CALLBACK(gtk_format_value
), NULL
);
325 g_signal_connect(m_widget
, "value_changed", G_CALLBACK(gtk_value_changed
), this);
327 handler_id
= g_signal_connect(
328 m_widget
, "event_after", G_CALLBACK(gtk_event_after
), this);
329 g_signal_handler_block(m_widget
, handler_id
);
331 SetRange( minValue
, maxValue
);
334 m_parent
->DoAddChild( this );
341 int wxSlider::GetValue() const
343 return int(round(m_pos
));
346 void wxSlider::SetValue( int value
)
348 if (GetValue() != value
)
351 gtk_range_set_value((GtkRange
*)m_widget
, value
);
352 UnblockScrollEvent();
356 void wxSlider::SetRange( int minValue
, int maxValue
)
359 gtk_range_set_range((GtkRange
*)m_widget
, minValue
, maxValue
);
360 gtk_range_set_increments((GtkRange
*)m_widget
, 1, (maxValue
- minValue
+ 9) / 10);
361 UnblockScrollEvent();
364 int wxSlider::GetMin() const
366 return int(((GtkRange
*)m_widget
)->adjustment
->lower
);
369 int wxSlider::GetMax() const
371 return int(((GtkRange
*)m_widget
)->adjustment
->upper
);
374 void wxSlider::SetPageSize( int pageSize
)
377 gtk_range_set_increments((GtkRange
*)m_widget
, 1, pageSize
);
378 UnblockScrollEvent();
381 int wxSlider::GetPageSize() const
383 return int(((GtkRange
*)m_widget
)->adjustment
->page_increment
);
386 // GTK does not support changing the size of the slider
387 void wxSlider::SetThumbLength(int)
391 int wxSlider::GetThumbLength() const
396 void wxSlider::SetLineSize( int WXUNUSED(lineSize
) )
400 int wxSlider::GetLineSize() const
405 bool wxSlider::IsOwnGtkWindow( GdkWindow
*window
)
407 GtkRange
*range
= GTK_RANGE(m_widget
);
408 return (range
->event_window
== window
);
413 wxSlider::GetClassDefaultAttributes(wxWindowVariant
WXUNUSED(variant
))
415 return GetDefaultAttributesFromGTKWidget(gtk_vscale_new
);
418 #endif // wxUSE_SLIDER