]> git.saurik.com Git - wxWidgets.git/blame - 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
c801d85f 1/////////////////////////////////////////////////////////////////////////////
de6185e2 2// Name: src/gtk/slider.cpp
c801d85f
KB
3// Purpose:
4// Author: Robert Roebling
f96aa4d9
RR
5// Id: $Id$
6// Copyright: (c) 1998 Robert Roebling
65571936 7// Licence: wxWindows licence
c801d85f
KB
8/////////////////////////////////////////////////////////////////////////////
9
14f355c2
VS
10// For compilers that support precompilation, includes "wx.h".
11#include "wx/wxprec.h"
12
de6185e2
WS
13#if wxUSE_SLIDER
14
c801d85f 15#include "wx/slider.h"
dcf924a3 16
de6185e2
WS
17#ifndef WX_PRECOMP
18 #include "wx/utils.h"
19#endif
dcf924a3 20
9e691f46 21#include "wx/gtk/private.h"
83624f79 22
66bd6b93
RR
23//-----------------------------------------------------------------------------
24// data
25//-----------------------------------------------------------------------------
26
1e6feb95 27extern bool g_blockEventsOnDrag;
66bd6b93 28
2b024653
VZ
29// ----------------------------------------------------------------------------
30// helper functions
31// ----------------------------------------------------------------------------
32
2b024653
VZ
33// process a scroll event
34static void
12a480c1 35ProcessScrollEvent(wxSlider *win, wxEventType evtType)
2b024653 36{
46c48053
VZ
37 const int orient = win->HasFlag(wxSL_VERTICAL) ? wxVERTICAL
38 : wxHORIZONTAL;
2b024653 39
12a480c1 40 const int value = win->GetValue();
2b024653 41
46c48053
VZ
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
8e3e14c4
VZ
53 if ( evtType != wxEVT_SCROLL_THUMBTRACK )
54 {
46c48053
VZ
55 wxScrollEvent event(wxEVT_SCROLL_CHANGED, win->GetId(), value, orient);
56 event.SetEventObject( win );
57 win->GetEventHandler()->ProcessEvent( event );
8e3e14c4
VZ
58 }
59
46c48053
VZ
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 );
2b024653 65}
57a1fd73 66
12a480c1
PC
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
c801d85f 118//-----------------------------------------------------------------------------
97b3455a 119// "value_changed"
c801d85f
KB
120//-----------------------------------------------------------------------------
121
865bb325 122extern "C" {
12a480c1
PC
123static void
124gtk_value_changed(GtkRange* range, wxSlider* win)
80810ca3 125{
acfd422a
RR
126 if (g_isIdle) wxapp_install_idle_handler();
127
a2053b27 128 if (!win->m_hasVMT) return;
2d17d68f 129 if (g_blockEventsOnDrag) return;
e1811a01 130
12a480c1
PC
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;
2b024653 138 return;
12a480c1 139 }
80810ca3 140
12a480c1
PC
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;
2d17d68f 156
12a480c1
PC
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}
80810ca3 189
12a480c1
PC
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}
2b024653 201}
80810ca3 202
12a480c1
PC
203//-----------------------------------------------------------------------------
204// "button_press_event"
205//-----------------------------------------------------------------------------
206
207extern "C" {
208static gboolean
209gtk_button_press_event(GtkWidget*, GdkEventButton*, wxSlider* win)
2b024653 210{
12a480c1 211 win->m_mouseButtonDown = true;
80810ca3 212
12a480c1
PC
213 return false;
214}
2b024653 215}
80810ca3 216
12a480c1
PC
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)
2b024653 223{
12a480c1 224 wxSlider* win = (wxSlider*)data;
2b024653 225 win->m_isScrolling = false;
12a480c1
PC
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}
2b024653 238
12a480c1
PC
239//-----------------------------------------------------------------------------
240// "button_release_event"
241//-----------------------------------------------------------------------------
2b024653 242
12a480c1
PC
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}
6de97a3b 254}
2b024653 255
12a480c1
PC
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}
865bb325 266}
c801d85f 267
97b3455a
RR
268//-----------------------------------------------------------------------------
269// wxSlider
270//-----------------------------------------------------------------------------
271
c801d85f
KB
272IMPLEMENT_DYNAMIC_CLASS(wxSlider,wxControl)
273
12a480c1
PC
274wxSlider::wxSlider()
275{
276 m_pos = 0;
277 m_scrollEventType = 0;
278 m_needThumbRelease = false;
279}
280
debe6624
JS
281bool wxSlider::Create(wxWindow *parent, wxWindowID id,
282 int value, int minValue, int maxValue,
c801d85f 283 const wxPoint& pos, const wxSize& size,
6de97a3b 284 long style, const wxValidator& validator, const wxString& name )
c801d85f 285{
de6185e2
WS
286 m_acceptsFocus = true;
287 m_needParent = true;
80810ca3 288
4dcaf11a
RR
289 if (!PreCreation( parent, pos, size ) ||
290 !CreateBase( parent, id, pos, size, style, validator, name ))
291 {
223d09f6 292 wxFAIL_MSG( wxT("wxSlider creation failed") );
de6185e2 293 return false;
4dcaf11a 294 }
6de97a3b 295
12a480c1
PC
296 m_pos = 0;
297 m_scrollEventType = 0;
298 m_needThumbRelease = false;
c801d85f 299
2e563988 300 if (style & wxSL_VERTICAL)
2d17d68f 301 m_widget = gtk_vscale_new( (GtkAdjustment *) NULL );
19da4326
RR
302 else
303 m_widget = gtk_hscale_new( (GtkAdjustment *) NULL );
80810ca3 304
12a480c1
PC
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);
80810ca3 308
1e219378
KH
309 if (style & wxSL_INVERSE)
310 gtk_range_set_inverted( GTK_RANGE(m_widget), TRUE );
311
12a480c1
PC
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);
91b167dd 317
2d17d68f
RR
318 SetRange( minValue, maxValue );
319 SetValue( value );
80810ca3 320
f03fc89f 321 m_parent->DoAddChild( this );
80810ca3 322
abdeb9e7 323 PostCreation(size);
80810ca3 324
de6185e2 325 return true;
6de97a3b 326}
c801d85f 327
1e1fafb9 328int wxSlider::GetValue() const
c801d85f 329{
12a480c1 330 return int(round(m_pos));
6de97a3b 331}
c801d85f 332
debe6624 333void wxSlider::SetValue( int value )
c801d85f 334{
12a480c1
PC
335 if (GetValue() != value)
336 {
337 BlockScrollEvent();
338 gtk_range_set_value((GtkRange*)m_widget, value);
339 UnblockScrollEvent();
340 }
6de97a3b 341}
c801d85f 342
debe6624 343void wxSlider::SetRange( int minValue, int maxValue )
c801d85f 344{
12a480c1
PC
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();
6de97a3b 349}
c801d85f 350
1e1fafb9 351int wxSlider::GetMin() const
c801d85f 352{
12a480c1 353 return int(((GtkRange*)m_widget)->adjustment->lower);
6de97a3b 354}
c801d85f 355
1e1fafb9 356int wxSlider::GetMax() const
c801d85f 357{
12a480c1 358 return int(((GtkRange*)m_widget)->adjustment->upper);
6de97a3b 359}
c801d85f 360
debe6624 361void wxSlider::SetPageSize( int pageSize )
c801d85f 362{
12a480c1
PC
363 BlockScrollEvent();
364 gtk_range_set_increments((GtkRange*)m_widget, 1, pageSize);
365 UnblockScrollEvent();
6de97a3b 366}
c801d85f 367
1e1fafb9 368int wxSlider::GetPageSize() const
c801d85f 369{
12a480c1 370 return int(((GtkRange*)m_widget)->adjustment->page_increment);
6de97a3b 371}
c801d85f 372
12a480c1
PC
373// GTK does not support changing the size of the slider
374void wxSlider::SetThumbLength(int)
c801d85f 375{
6de97a3b 376}
c801d85f 377
1e1fafb9 378int wxSlider::GetThumbLength() const
c801d85f 379{
12a480c1 380 return 0;
6de97a3b 381}
c801d85f 382
debe6624 383void wxSlider::SetLineSize( int WXUNUSED(lineSize) )
c801d85f 384{
6de97a3b 385}
c801d85f 386
1e1fafb9 387int wxSlider::GetLineSize() const
c801d85f 388{
2d17d68f 389 return 0;
6de97a3b 390}
c801d85f 391
b4071e91
RR
392bool wxSlider::IsOwnGtkWindow( GdkWindow *window )
393{
2d17d68f 394 GtkRange *range = GTK_RANGE(m_widget);
2b5f62a0 395 return (range->event_window == window);
b4071e91
RR
396}
397
9d522606
RD
398// static
399wxVisualAttributes
400wxSlider::GetClassDefaultAttributes(wxWindowVariant WXUNUSED(variant))
401{
402 return GetDefaultAttributesFromGTKWidget(gtk_vscale_new);
403}
404
de6185e2 405#endif // wxUSE_SLIDER