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