]> git.saurik.com Git - wxWidgets.git/blame_incremental - src/gtk/slider.cpp
[ 1508778 ] Fix for wxOwnerDrawnComboBox list selection rendering.
[wxWidgets.git] / src / gtk / slider.cpp
... / ...
CommitLineData
1/////////////////////////////////////////////////////////////////////////////
2// Name: src/gtk/slider.cpp
3// Purpose:
4// Author: Robert Roebling
5// Id: $Id$
6// Copyright: (c) 1998 Robert Roebling
7// Licence: wxWindows licence
8/////////////////////////////////////////////////////////////////////////////
9
10// For compilers that support precompilation, includes "wx.h".
11#include "wx/wxprec.h"
12
13#if wxUSE_SLIDER
14
15#ifdef __VMS
16 //Missing definition in OpenVMS C++ header files.
17 double round(double __x);
18#endif
19
20#include "wx/slider.h"
21
22#ifndef WX_PRECOMP
23 #include "wx/utils.h"
24#endif
25
26#include "wx/gtk/private.h"
27
28//-----------------------------------------------------------------------------
29// data
30//-----------------------------------------------------------------------------
31
32extern bool g_blockEventsOnDrag;
33
34// ----------------------------------------------------------------------------
35// helper functions
36// ----------------------------------------------------------------------------
37
38// process a scroll event
39static void
40ProcessScrollEvent(wxSlider *win, wxEventType evtType)
41{
42 const int orient = win->HasFlag(wxSL_VERTICAL) ? wxVERTICAL
43 : wxHORIZONTAL;
44
45 const int value = win->GetValue();
46
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 )
50 {
51 wxScrollEvent event( evtType, win->GetId(), value, orient );
52 event.SetEventObject( win );
53 win->GetEventHandler()->ProcessEvent( event );
54 }
55
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 )
59 {
60 wxScrollEvent event(wxEVT_SCROLL_CHANGED, win->GetId(), value, orient);
61 event.SetEventObject( win );
62 win->GetEventHandler()->ProcessEvent( event );
63 }
64
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 );
70}
71
72static inline wxEventType GtkScrollTypeToWx(int scrollType)
73{
74 wxEventType eventType;
75 switch (scrollType)
76 {
77 case GTK_SCROLL_STEP_BACKWARD:
78 case GTK_SCROLL_STEP_LEFT:
79 case GTK_SCROLL_STEP_UP:
80 eventType = wxEVT_SCROLL_LINEUP;
81 break;
82 case GTK_SCROLL_STEP_DOWN:
83 case GTK_SCROLL_STEP_FORWARD:
84 case GTK_SCROLL_STEP_RIGHT:
85 eventType = wxEVT_SCROLL_LINEDOWN;
86 break;
87 case GTK_SCROLL_PAGE_BACKWARD:
88 case GTK_SCROLL_PAGE_LEFT:
89 case GTK_SCROLL_PAGE_UP:
90 eventType = wxEVT_SCROLL_PAGEUP;
91 break;
92 case GTK_SCROLL_PAGE_DOWN:
93 case GTK_SCROLL_PAGE_FORWARD:
94 case GTK_SCROLL_PAGE_RIGHT:
95 eventType = wxEVT_SCROLL_PAGEDOWN;
96 break;
97 case GTK_SCROLL_START:
98 eventType = wxEVT_SCROLL_TOP;
99 break;
100 case GTK_SCROLL_END:
101 eventType = wxEVT_SCROLL_BOTTOM;
102 break;
103 case GTK_SCROLL_JUMP:
104 eventType = wxEVT_SCROLL_THUMBTRACK;
105 break;
106 default:
107 wxFAIL_MSG(_T("Unknown GtkScrollType"));
108 eventType = wxEVT_NULL;
109 break;
110 }
111 return eventType;
112}
113
114// Determine if increment is the same as +/-x, allowing for some small
115// difference due to possible inexactness in floating point arithmetic
116static inline bool IsScrollIncrement(double increment, double x)
117{
118 wxASSERT(increment > 0);
119 const double tolerance = 1.0 / 1024;
120 return fabs(increment - fabs(x)) < tolerance;
121}
122
123//-----------------------------------------------------------------------------
124// "value_changed"
125//-----------------------------------------------------------------------------
126
127extern "C" {
128static void
129gtk_value_changed(GtkRange* range, wxSlider* win)
130{
131 if (g_isIdle) wxapp_install_idle_handler();
132
133 if (!win->m_hasVMT) return;
134 if (g_blockEventsOnDrag) return;
135
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)
141 {
142 win->m_scrollEventType = GTK_SCROLL_NONE;
143 return;
144 }
145
146 wxEventType eventType = wxEVT_NULL;
147 if (win->m_isScrolling)
148 {
149 eventType = wxEVT_SCROLL_THUMBTRACK;
150 }
151 else if (win->m_scrollEventType != GTK_SCROLL_NONE)
152 {
153 // Scroll event from "move-slider" (keyboard)
154 eventType = GtkScrollTypeToWx(win->m_scrollEventType);
155 }
156 else if (win->m_mouseButtonDown)
157 {
158 // Difference from last change event
159 const double diff = adj->value - oldPos;
160 const bool isDown = diff > 0;
161
162 if (IsScrollIncrement(adj->page_increment, diff))
163 {
164 eventType = isDown ? wxEVT_SCROLL_PAGEDOWN : wxEVT_SCROLL_PAGEUP;
165 }
166 else if (wxIsSameDouble(adj->value, 0))
167 {
168 eventType = wxEVT_SCROLL_PAGEUP;
169 }
170 else if (wxIsSameDouble(adj->value, adj->upper))
171 {
172 eventType = wxEVT_SCROLL_PAGEDOWN;
173 }
174 else
175 {
176 // Assume track event
177 eventType = wxEVT_SCROLL_THUMBTRACK;
178 // Remember that we're tracking
179 win->m_isScrolling = true;
180 }
181 }
182
183 win->m_scrollEventType = GTK_SCROLL_NONE;
184
185 // If integral position has changed
186 if (int(round(oldPos)) != pos)
187 {
188 wxCHECK_RET(eventType != wxEVT_NULL, _T("Unknown slider scroll event type"));
189 ProcessScrollEvent(win, eventType);
190 win->m_needThumbRelease = eventType == wxEVT_SCROLL_THUMBTRACK;
191 }
192}
193}
194
195//-----------------------------------------------------------------------------
196// "move_slider" (keyboard event)
197//-----------------------------------------------------------------------------
198
199extern "C" {
200static void
201gtk_move_slider(GtkRange*, GtkScrollType scrollType, wxSlider* win)
202{
203 // Save keyboard scroll type for "value_changed" handler
204 win->m_scrollEventType = scrollType;
205}
206}
207
208//-----------------------------------------------------------------------------
209// "button_press_event"
210//-----------------------------------------------------------------------------
211
212extern "C" {
213static gboolean
214gtk_button_press_event(GtkWidget*, GdkEventButton*, wxSlider* win)
215{
216 win->m_mouseButtonDown = true;
217
218 return false;
219}
220}
221
222//-----------------------------------------------------------------------------
223// "event_after"
224//-----------------------------------------------------------------------------
225
226extern "C" {
227static void
228gtk_event_after(GtkRange* range, GdkEvent* event, wxSlider* win)
229{
230 if (event->type == GDK_BUTTON_RELEASE)
231 {
232 g_signal_handlers_block_by_func(range, (void*)gtk_event_after, win);
233
234 if (win->m_needThumbRelease)
235 {
236 win->m_needThumbRelease = false;
237 ProcessScrollEvent(win, wxEVT_SCROLL_THUMBRELEASE);
238 }
239 // Keep slider at an integral position
240 win->BlockScrollEvent();
241 gtk_range_set_value((GtkRange*)win->m_widget, win->GetValue());
242 win->UnblockScrollEvent();
243 }
244}
245}
246
247//-----------------------------------------------------------------------------
248// "button_release_event"
249//-----------------------------------------------------------------------------
250
251extern "C" {
252static gboolean
253gtk_button_release_event(GtkRange* range, GdkEventButton*, wxSlider* win)
254{
255 win->m_mouseButtonDown = false;
256 if (win->m_isScrolling)
257 {
258 win->m_isScrolling = false;
259 g_signal_handlers_unblock_by_func(range, (void*)gtk_event_after, win);
260 }
261 return false;
262}
263}
264
265//-----------------------------------------------------------------------------
266// "format_value"
267//-----------------------------------------------------------------------------
268
269extern "C" {
270static gchar* gtk_format_value(GtkScale*, double value, void*)
271{
272 // Format value as nearest integer
273 return g_strdup_printf("%d", int(round(value)));
274}
275}
276
277//-----------------------------------------------------------------------------
278// wxSlider
279//-----------------------------------------------------------------------------
280
281IMPLEMENT_DYNAMIC_CLASS(wxSlider,wxControl)
282
283wxSlider::wxSlider()
284{
285 m_pos = 0;
286 m_scrollEventType = 0;
287 m_needThumbRelease = false;
288}
289
290bool 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 )
294{
295 m_acceptsFocus = true;
296 m_needParent = true;
297
298 if (!PreCreation( parent, pos, size ) ||
299 !CreateBase( parent, id, pos, size, style, validator, name ))
300 {
301 wxFAIL_MSG( wxT("wxSlider creation failed") );
302 return false;
303 }
304
305 m_pos = 0;
306 m_scrollEventType = 0;
307 m_needThumbRelease = false;
308
309 if (style & wxSL_VERTICAL)
310 m_widget = gtk_vscale_new( (GtkAdjustment *) NULL );
311 else
312 m_widget = gtk_hscale_new( (GtkAdjustment *) NULL );
313
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);
317
318 if (style & wxSL_INVERSE)
319 gtk_range_set_inverted( GTK_RANGE(m_widget), TRUE );
320
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);
326 gulong handler_id;
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);
330
331 SetRange( minValue, maxValue );
332 SetValue( value );
333
334 m_parent->DoAddChild( this );
335
336 PostCreation(size);
337
338 return true;
339}
340
341int wxSlider::GetValue() const
342{
343 return int(round(m_pos));
344}
345
346void wxSlider::SetValue( int value )
347{
348 if (GetValue() != value)
349 {
350 BlockScrollEvent();
351 gtk_range_set_value((GtkRange*)m_widget, value);
352 UnblockScrollEvent();
353 }
354}
355
356void wxSlider::SetRange( int minValue, int maxValue )
357{
358 BlockScrollEvent();
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();
362}
363
364int wxSlider::GetMin() const
365{
366 return int(((GtkRange*)m_widget)->adjustment->lower);
367}
368
369int wxSlider::GetMax() const
370{
371 return int(((GtkRange*)m_widget)->adjustment->upper);
372}
373
374void wxSlider::SetPageSize( int pageSize )
375{
376 BlockScrollEvent();
377 gtk_range_set_increments((GtkRange*)m_widget, 1, pageSize);
378 UnblockScrollEvent();
379}
380
381int wxSlider::GetPageSize() const
382{
383 return int(((GtkRange*)m_widget)->adjustment->page_increment);
384}
385
386// GTK does not support changing the size of the slider
387void wxSlider::SetThumbLength(int)
388{
389}
390
391int wxSlider::GetThumbLength() const
392{
393 return 0;
394}
395
396void wxSlider::SetLineSize( int WXUNUSED(lineSize) )
397{
398}
399
400int wxSlider::GetLineSize() const
401{
402 return 0;
403}
404
405bool wxSlider::IsOwnGtkWindow( GdkWindow *window )
406{
407 GtkRange *range = GTK_RANGE(m_widget);
408 return (range->event_window == window);
409}
410
411// static
412wxVisualAttributes
413wxSlider::GetClassDefaultAttributes(wxWindowVariant WXUNUSED(variant))
414{
415 return GetDefaultAttributesFromGTKWidget(gtk_vscale_new);
416}
417
418#endif // wxUSE_SLIDER