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