]> git.saurik.com Git - wxWidgets.git/blame - src/gtk/slider.cpp
changed hardware at York
[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
cc57f388
JJ
15#ifdef __VMS
16 //Missing definition in OpenVMS C++ header files.
17 double round(double __x);
18#endif
19
c801d85f 20#include "wx/slider.h"
dcf924a3 21
de6185e2
WS
22#ifndef WX_PRECOMP
23 #include "wx/utils.h"
24#endif
dcf924a3 25
9e691f46 26#include "wx/gtk/private.h"
83624f79 27
66bd6b93
RR
28//-----------------------------------------------------------------------------
29// data
30//-----------------------------------------------------------------------------
31
1e6feb95 32extern bool g_blockEventsOnDrag;
66bd6b93 33
2b024653
VZ
34// ----------------------------------------------------------------------------
35// helper functions
36// ----------------------------------------------------------------------------
37
2b024653
VZ
38// process a scroll event
39static void
12a480c1 40ProcessScrollEvent(wxSlider *win, wxEventType evtType)
2b024653 41{
46c48053
VZ
42 const int orient = win->HasFlag(wxSL_VERTICAL) ? wxVERTICAL
43 : wxHORIZONTAL;
2b024653 44
12a480c1 45 const int value = win->GetValue();
2b024653 46
46c48053
VZ
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
8e3e14c4
VZ
58 if ( evtType != wxEVT_SCROLL_THUMBTRACK )
59 {
46c48053
VZ
60 wxScrollEvent event(wxEVT_SCROLL_CHANGED, win->GetId(), value, orient);
61 event.SetEventObject( win );
62 win->GetEventHandler()->ProcessEvent( event );
8e3e14c4
VZ
63 }
64
46c48053
VZ
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 );
2b024653 70}
57a1fd73 71
12a480c1
PC
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
c801d85f 123//-----------------------------------------------------------------------------
97b3455a 124// "value_changed"
c801d85f
KB
125//-----------------------------------------------------------------------------
126
865bb325 127extern "C" {
12a480c1
PC
128static void
129gtk_value_changed(GtkRange* range, wxSlider* win)
80810ca3 130{
acfd422a
RR
131 if (g_isIdle) wxapp_install_idle_handler();
132
a2053b27 133 if (!win->m_hasVMT) return;
2d17d68f 134 if (g_blockEventsOnDrag) return;
e1811a01 135
12a480c1
PC
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;
2b024653 143 return;
12a480c1 144 }
80810ca3 145
12a480c1
PC
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;
2d17d68f 161
12a480c1
PC
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}
80810ca3 194
12a480c1
PC
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}
2b024653 206}
80810ca3 207
12a480c1
PC
208//-----------------------------------------------------------------------------
209// "button_press_event"
210//-----------------------------------------------------------------------------
211
212extern "C" {
213static gboolean
214gtk_button_press_event(GtkWidget*, GdkEventButton*, wxSlider* win)
2b024653 215{
12a480c1 216 win->m_mouseButtonDown = true;
80810ca3 217
12a480c1
PC
218 return false;
219}
2b024653 220}
80810ca3 221
12a480c1
PC
222// Single shot idle callback, to generate thumb release event, and
223// truncate position to integral value. Idle callback is used
224// because position cannot be changed from button release event.
225extern "C" {
226static gboolean
227idle_thumbrelease(void* data)
2b024653 228{
cf26ac62 229 gdk_threads_enter();
12a480c1 230 wxSlider* win = (wxSlider*)data;
2b024653 231 win->m_isScrolling = false;
12a480c1
PC
232 if (win->m_needThumbRelease)
233 {
234 win->m_needThumbRelease = false;
235 ProcessScrollEvent(win, wxEVT_SCROLL_THUMBRELEASE);
236 }
237 // Keep slider at an integral position
238 win->BlockScrollEvent();
239 gtk_range_set_value((GtkRange*)win->m_widget, win->GetValue());
240 win->UnblockScrollEvent();
cf26ac62 241 gdk_threads_leave();
12a480c1
PC
242 return false;
243}
244}
2b024653 245
12a480c1
PC
246//-----------------------------------------------------------------------------
247// "button_release_event"
248//-----------------------------------------------------------------------------
2b024653 249
12a480c1
PC
250extern "C" {
251static gboolean
252gtk_button_release_event(GtkWidget*, GdkEventButton*, wxSlider* win)
253{
254 win->m_mouseButtonDown = false;
255 if (win->m_isScrolling)
256 {
257 g_idle_add(idle_thumbrelease, win);
258 }
259 return false;
260}
6de97a3b 261}
2b024653 262
12a480c1
PC
263//-----------------------------------------------------------------------------
264// "format_value"
265//-----------------------------------------------------------------------------
266
267extern "C" {
268static gchar* gtk_format_value(GtkScale*, double value, void*)
269{
270 // Format value as nearest integer
271 return g_strdup_printf("%d", int(round(value)));
272}
865bb325 273}
c801d85f 274
97b3455a
RR
275//-----------------------------------------------------------------------------
276// wxSlider
277//-----------------------------------------------------------------------------
278
c801d85f
KB
279IMPLEMENT_DYNAMIC_CLASS(wxSlider,wxControl)
280
12a480c1
PC
281wxSlider::wxSlider()
282{
283 m_pos = 0;
284 m_scrollEventType = 0;
285 m_needThumbRelease = false;
286}
287
debe6624
JS
288bool wxSlider::Create(wxWindow *parent, wxWindowID id,
289 int value, int minValue, int maxValue,
c801d85f 290 const wxPoint& pos, const wxSize& size,
6de97a3b 291 long style, const wxValidator& validator, const wxString& name )
c801d85f 292{
de6185e2
WS
293 m_acceptsFocus = true;
294 m_needParent = true;
80810ca3 295
4dcaf11a
RR
296 if (!PreCreation( parent, pos, size ) ||
297 !CreateBase( parent, id, pos, size, style, validator, name ))
298 {
223d09f6 299 wxFAIL_MSG( wxT("wxSlider creation failed") );
de6185e2 300 return false;
4dcaf11a 301 }
6de97a3b 302
12a480c1
PC
303 m_pos = 0;
304 m_scrollEventType = 0;
305 m_needThumbRelease = false;
c801d85f 306
2e563988 307 if (style & wxSL_VERTICAL)
2d17d68f 308 m_widget = gtk_vscale_new( (GtkAdjustment *) NULL );
19da4326
RR
309 else
310 m_widget = gtk_hscale_new( (GtkAdjustment *) NULL );
80810ca3 311
12a480c1
PC
312 gtk_scale_set_draw_value((GtkScale*)m_widget, (style & wxSL_LABELS) != 0);
313 // Keep full precision in position value
314 gtk_scale_set_digits((GtkScale*)m_widget, -1);
80810ca3 315
1e219378
KH
316 if (style & wxSL_INVERSE)
317 gtk_range_set_inverted( GTK_RANGE(m_widget), TRUE );
318
12a480c1
PC
319 g_signal_connect(m_widget, "button_press_event", G_CALLBACK(gtk_button_press_event), this);
320 g_signal_connect(m_widget, "button_release_event", G_CALLBACK(gtk_button_release_event), this);
321 g_signal_connect(m_widget, "move_slider", G_CALLBACK(gtk_move_slider), this);
322 g_signal_connect(m_widget, "format_value", G_CALLBACK(gtk_format_value), NULL);
323 g_signal_connect(m_widget, "value_changed", G_CALLBACK(gtk_value_changed), this);
91b167dd 324
2d17d68f
RR
325 SetRange( minValue, maxValue );
326 SetValue( value );
80810ca3 327
f03fc89f 328 m_parent->DoAddChild( this );
80810ca3 329
abdeb9e7 330 PostCreation(size);
80810ca3 331
de6185e2 332 return true;
6de97a3b 333}
c801d85f 334
1e1fafb9 335int wxSlider::GetValue() const
c801d85f 336{
12a480c1 337 return int(round(m_pos));
6de97a3b 338}
c801d85f 339
debe6624 340void wxSlider::SetValue( int value )
c801d85f 341{
12a480c1
PC
342 if (GetValue() != value)
343 {
344 BlockScrollEvent();
345 gtk_range_set_value((GtkRange*)m_widget, value);
346 UnblockScrollEvent();
347 }
6de97a3b 348}
c801d85f 349
debe6624 350void wxSlider::SetRange( int minValue, int maxValue )
c801d85f 351{
12a480c1
PC
352 BlockScrollEvent();
353 gtk_range_set_range((GtkRange*)m_widget, minValue, maxValue);
354 gtk_range_set_increments((GtkRange*)m_widget, 1, (maxValue - minValue + 9) / 10);
355 UnblockScrollEvent();
6de97a3b 356}
c801d85f 357
1e1fafb9 358int wxSlider::GetMin() const
c801d85f 359{
12a480c1 360 return int(((GtkRange*)m_widget)->adjustment->lower);
6de97a3b 361}
c801d85f 362
1e1fafb9 363int wxSlider::GetMax() const
c801d85f 364{
12a480c1 365 return int(((GtkRange*)m_widget)->adjustment->upper);
6de97a3b 366}
c801d85f 367
debe6624 368void wxSlider::SetPageSize( int pageSize )
c801d85f 369{
12a480c1
PC
370 BlockScrollEvent();
371 gtk_range_set_increments((GtkRange*)m_widget, 1, pageSize);
372 UnblockScrollEvent();
6de97a3b 373}
c801d85f 374
1e1fafb9 375int wxSlider::GetPageSize() const
c801d85f 376{
12a480c1 377 return int(((GtkRange*)m_widget)->adjustment->page_increment);
6de97a3b 378}
c801d85f 379
12a480c1
PC
380// GTK does not support changing the size of the slider
381void wxSlider::SetThumbLength(int)
c801d85f 382{
6de97a3b 383}
c801d85f 384
1e1fafb9 385int wxSlider::GetThumbLength() const
c801d85f 386{
12a480c1 387 return 0;
6de97a3b 388}
c801d85f 389
debe6624 390void wxSlider::SetLineSize( int WXUNUSED(lineSize) )
c801d85f 391{
6de97a3b 392}
c801d85f 393
1e1fafb9 394int wxSlider::GetLineSize() const
c801d85f 395{
2d17d68f 396 return 0;
6de97a3b 397}
c801d85f 398
b4071e91
RR
399bool wxSlider::IsOwnGtkWindow( GdkWindow *window )
400{
2d17d68f 401 GtkRange *range = GTK_RANGE(m_widget);
2b5f62a0 402 return (range->event_window == window);
b4071e91
RR
403}
404
9d522606
RD
405// static
406wxVisualAttributes
407wxSlider::GetClassDefaultAttributes(wxWindowVariant WXUNUSED(variant))
408{
409 return GetDefaultAttributesFromGTKWidget(gtk_vscale_new);
410}
411
de6185e2 412#endif // wxUSE_SLIDER