]> git.saurik.com Git - wxWidgets.git/blame_incremental - src/gtk/slider.cpp
show help for the page under mouse when the [?] button is used to request help
[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 wxSlider* win = (wxSlider*)data;
225 win->m_isScrolling = false;
226 if (win->m_needThumbRelease)
227 {
228 win->m_needThumbRelease = false;
229 ProcessScrollEvent(win, wxEVT_SCROLL_THUMBRELEASE);
230 }
231 // Keep slider at an integral position
232 win->BlockScrollEvent();
233 gtk_range_set_value((GtkRange*)win->m_widget, win->GetValue());
234 win->UnblockScrollEvent();
235 return false;
236}
237}
238
239//-----------------------------------------------------------------------------
240// "button_release_event"
241//-----------------------------------------------------------------------------
242
243extern "C" {
244static gboolean
245gtk_button_release_event(GtkWidget*, GdkEventButton*, wxSlider* win)
246{
247 win->m_mouseButtonDown = false;
248 if (win->m_isScrolling)
249 {
250 g_idle_add(idle_thumbrelease, win);
251 }
252 return false;
253}
254}
255
256//-----------------------------------------------------------------------------
257// "format_value"
258//-----------------------------------------------------------------------------
259
260extern "C" {
261static gchar* gtk_format_value(GtkScale*, double value, void*)
262{
263 // Format value as nearest integer
264 return g_strdup_printf("%d", int(round(value)));
265}
266}
267
268//-----------------------------------------------------------------------------
269// wxSlider
270//-----------------------------------------------------------------------------
271
272IMPLEMENT_DYNAMIC_CLASS(wxSlider,wxControl)
273
274wxSlider::wxSlider()
275{
276 m_pos = 0;
277 m_scrollEventType = 0;
278 m_needThumbRelease = false;
279}
280
281bool 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 )
285{
286 m_acceptsFocus = true;
287 m_needParent = true;
288
289 if (!PreCreation( parent, pos, size ) ||
290 !CreateBase( parent, id, pos, size, style, validator, name ))
291 {
292 wxFAIL_MSG( wxT("wxSlider creation failed") );
293 return false;
294 }
295
296 m_pos = 0;
297 m_scrollEventType = 0;
298 m_needThumbRelease = false;
299
300 if (style & wxSL_VERTICAL)
301 m_widget = gtk_vscale_new( (GtkAdjustment *) NULL );
302 else
303 m_widget = gtk_hscale_new( (GtkAdjustment *) NULL );
304
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);
308
309 if (style & wxSL_INVERSE)
310 gtk_range_set_inverted( GTK_RANGE(m_widget), TRUE );
311
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);
317
318 SetRange( minValue, maxValue );
319 SetValue( value );
320
321 m_parent->DoAddChild( this );
322
323 PostCreation(size);
324
325 return true;
326}
327
328int wxSlider::GetValue() const
329{
330 return int(round(m_pos));
331}
332
333void wxSlider::SetValue( int value )
334{
335 if (GetValue() != value)
336 {
337 BlockScrollEvent();
338 gtk_range_set_value((GtkRange*)m_widget, value);
339 UnblockScrollEvent();
340 }
341}
342
343void wxSlider::SetRange( int minValue, int maxValue )
344{
345 BlockScrollEvent();
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();
349}
350
351int wxSlider::GetMin() const
352{
353 return int(((GtkRange*)m_widget)->adjustment->lower);
354}
355
356int wxSlider::GetMax() const
357{
358 return int(((GtkRange*)m_widget)->adjustment->upper);
359}
360
361void wxSlider::SetPageSize( int pageSize )
362{
363 BlockScrollEvent();
364 gtk_range_set_increments((GtkRange*)m_widget, 1, pageSize);
365 UnblockScrollEvent();
366}
367
368int wxSlider::GetPageSize() const
369{
370 return int(((GtkRange*)m_widget)->adjustment->page_increment);
371}
372
373// GTK does not support changing the size of the slider
374void wxSlider::SetThumbLength(int)
375{
376}
377
378int wxSlider::GetThumbLength() const
379{
380 return 0;
381}
382
383void wxSlider::SetLineSize( int WXUNUSED(lineSize) )
384{
385}
386
387int wxSlider::GetLineSize() const
388{
389 return 0;
390}
391
392bool wxSlider::IsOwnGtkWindow( GdkWindow *window )
393{
394 GtkRange *range = GTK_RANGE(m_widget);
395 return (range->event_window == window);
396}
397
398// static
399wxVisualAttributes
400wxSlider::GetClassDefaultAttributes(wxWindowVariant WXUNUSED(variant))
401{
402 return GetDefaultAttributesFromGTKWidget(gtk_vscale_new);
403}
404
405#endif // wxUSE_SLIDER