1 /////////////////////////////////////////////////////////////////////////////
2 // Name:        src/gtk/scrolbar.cpp
3 // Purpose:
4 // Author:      Robert Roebling
5 // Copyright:   (c) 1998 Robert Roebling
6 // Licence:     wxWindows licence
7 /////////////////////////////////////////////////////////////////////////////
8 
9 // For compilers that support precompilation, includes "wx.h".
10 #include "wx/wxprec.h"
11 
12 #if wxUSE_SCROLLBAR
13 
14 #include "wx/scrolbar.h"
15 
16 #ifndef WX_PRECOMP
17     #include "wx/utils.h"
18 #endif
19 
20 #include "wx/gtk/private.h"
21 
22 //-----------------------------------------------------------------------------
23 // "value_changed" from scrollbar
24 //-----------------------------------------------------------------------------
25 
26 extern "C" {
27 static void
gtk_value_changed(GtkRange * range,wxScrollBar * win)28 gtk_value_changed(GtkRange* range, wxScrollBar* win)
29 {
30     wxEventType eventType = win->GTKGetScrollEventType(range);
31     if (eventType != wxEVT_NULL)
32     {
33         const int orient = win->HasFlag(wxSB_VERTICAL) ? wxVERTICAL : wxHORIZONTAL;
34         const int value = win->GetThumbPosition();
35         const int id = win->GetId();
36 
37         // first send the specific event for the user action
38         wxScrollEvent evtSpec(eventType, id, value, orient);
39         evtSpec.SetEventObject(win);
40         win->HandleWindowEvent(evtSpec);
41 
42         if (!win->m_isScrolling)
43         {
44             // and if it's over also send a general "changed" event
45             wxScrollEvent evtChanged(wxEVT_SCROLL_CHANGED, id, value, orient);
46             evtChanged.SetEventObject(win);
47             win->HandleWindowEvent(evtChanged);
48         }
49     }
50 }
51 }
52 
53 //-----------------------------------------------------------------------------
54 // "button_press_event" from scrollbar
55 //-----------------------------------------------------------------------------
56 
57 extern "C" {
58 static gboolean
gtk_button_press_event(GtkRange *,GdkEventButton *,wxScrollBar * win)59 gtk_button_press_event(GtkRange*, GdkEventButton*, wxScrollBar* win)
60 {
61     win->m_mouseButtonDown = true;
62     return false;
63 }
64 }
65 
66 //-----------------------------------------------------------------------------
67 // "event_after" from scrollbar
68 //-----------------------------------------------------------------------------
69 
70 extern "C" {
71 static void
gtk_event_after(GtkRange * range,GdkEvent * event,wxScrollBar * win)72 gtk_event_after(GtkRange* range, GdkEvent* event, wxScrollBar* win)
73 {
74     if (event->type == GDK_BUTTON_RELEASE)
75     {
76         g_signal_handlers_block_by_func(range, (void*)gtk_event_after, win);
77 
78         const int value = win->GetThumbPosition();
79         const int orient = win->HasFlag(wxSB_VERTICAL) ? wxVERTICAL : wxHORIZONTAL;
80         const int id = win->GetId();
81 
82         wxScrollEvent evtRel(wxEVT_SCROLL_THUMBRELEASE, id, value, orient);
83         evtRel.SetEventObject(win);
84         win->HandleWindowEvent(evtRel);
85 
86         wxScrollEvent evtChanged(wxEVT_SCROLL_CHANGED, id, value, orient);
87         evtChanged.SetEventObject(win);
88         win->HandleWindowEvent(evtChanged);
89     }
90 }
91 }
92 
93 //-----------------------------------------------------------------------------
94 // "button_release_event" from scrollbar
95 //-----------------------------------------------------------------------------
96 
97 extern "C" {
98 static gboolean
gtk_button_release_event(GtkRange * range,GdkEventButton *,wxScrollBar * win)99 gtk_button_release_event(GtkRange* range, GdkEventButton*, wxScrollBar* win)
100 {
101     win->m_mouseButtonDown = false;
102     // If thumb tracking
103     if (win->m_isScrolling)
104     {
105         win->m_isScrolling = false;
106         // Hook up handler to send thumb release event after this emission is finished.
107         // To allow setting scroll position from event handler, sending event must
108         // be deferred until after the GtkRange handler for this signal has run
109         g_signal_handlers_unblock_by_func(range, (void*)gtk_event_after, win);
110     }
111 
112     return false;
113 }
114 }
115 
116 //-----------------------------------------------------------------------------
117 // wxScrollBar
118 //-----------------------------------------------------------------------------
119 
wxScrollBar()120 wxScrollBar::wxScrollBar()
121 {
122 }
123 
~wxScrollBar()124 wxScrollBar::~wxScrollBar()
125 {
126 }
127 
Create(wxWindow * parent,wxWindowID id,const wxPoint & pos,const wxSize & size,long style,const wxValidator & validator,const wxString & name)128 bool wxScrollBar::Create(wxWindow *parent, wxWindowID id,
129            const wxPoint& pos, const wxSize& size,
130            long style, const wxValidator& validator, const wxString& name )
131 {
132     if (!PreCreation( parent, pos, size ) ||
133         !CreateBase( parent, id, pos, size, style, validator, name ))
134     {
135         wxFAIL_MSG( wxT("wxScrollBar creation failed") );
136         return false;
137     }
138 
139     const bool isVertical = (style & wxSB_VERTICAL) != 0;
140     m_widget = gtk_scrollbar_new(GtkOrientation(isVertical), NULL);
141     g_object_ref(m_widget);
142 
143     m_scrollBar[0] = (GtkRange*)m_widget;
144 
145     g_signal_connect_after(m_widget, "value_changed",
146                      G_CALLBACK(gtk_value_changed), this);
147     g_signal_connect(m_widget, "button_press_event",
148                      G_CALLBACK(gtk_button_press_event), this);
149     g_signal_connect(m_widget, "button_release_event",
150                      G_CALLBACK(gtk_button_release_event), this);
151 
152     gulong handler_id;
153     handler_id = g_signal_connect(
154         m_widget, "event_after", G_CALLBACK(gtk_event_after), this);
155     g_signal_handler_block(m_widget, handler_id);
156 
157     m_parent->DoAddChild( this );
158 
159     PostCreation(size);
160 
161     return true;
162 }
163 
GetThumbPosition() const164 int wxScrollBar::GetThumbPosition() const
165 {
166     return wxRound(gtk_range_get_value(GTK_RANGE(m_widget)));
167 }
168 
GetThumbSize() const169 int wxScrollBar::GetThumbSize() const
170 {
171     GtkAdjustment* adj = gtk_range_get_adjustment(GTK_RANGE(m_widget));
172     return int(gtk_adjustment_get_page_size(adj));
173 }
174 
GetPageSize() const175 int wxScrollBar::GetPageSize() const
176 {
177     GtkAdjustment* adj = gtk_range_get_adjustment(GTK_RANGE(m_widget));
178     return int(gtk_adjustment_get_page_increment(adj));
179 }
180 
GetRange() const181 int wxScrollBar::GetRange() const
182 {
183     GtkAdjustment* adj = gtk_range_get_adjustment(GTK_RANGE(m_widget));
184     return int(gtk_adjustment_get_upper(adj));
185 }
186 
SetThumbPosition(int viewStart)187 void wxScrollBar::SetThumbPosition( int viewStart )
188 {
189     if (GetThumbPosition() != viewStart)
190     {
191         g_signal_handlers_block_by_func(m_widget,
192             (gpointer)gtk_value_changed, this);
193 
194         gtk_range_set_value((GtkRange*)m_widget, viewStart);
195         m_scrollPos[0] = gtk_range_get_value((GtkRange*)m_widget);
196 
197         g_signal_handlers_unblock_by_func(m_widget,
198             (gpointer)gtk_value_changed, this);
199     }
200 }
201 
SetScrollbar(int position,int thumbSize,int range,int pageSize,bool)202 void wxScrollBar::SetScrollbar(int position, int thumbSize, int range, int pageSize, bool)
203 {
204     if (range == 0)
205     {
206         // GtkRange requires upper > lower
207         range =
208         thumbSize = 1;
209     }
210     g_signal_handlers_block_by_func(m_widget, (void*)gtk_value_changed, this);
211     GtkRange* widget = GTK_RANGE(m_widget);
212     GtkAdjustment* adj = gtk_range_get_adjustment(widget);
213 
214     g_object_freeze_notify(G_OBJECT(adj));
215     gtk_range_set_increments(widget, 1, pageSize);
216     gtk_adjustment_set_page_size(adj, thumbSize);
217     gtk_range_set_range(widget, 0, range);
218     g_object_thaw_notify(G_OBJECT(adj));
219 
220     gtk_range_set_value(widget, position);
221     m_scrollPos[0] = gtk_range_get_value(widget);
222     g_signal_handlers_unblock_by_func(m_widget, (void*)gtk_value_changed, this);
223 }
224 
SetPageSize(int pageLength)225 void wxScrollBar::SetPageSize( int pageLength )
226 {
227     SetScrollbar(GetThumbPosition(), GetThumbSize(), GetRange(), pageLength);
228 }
229 
SetRange(int range)230 void wxScrollBar::SetRange(int range)
231 {
232     SetScrollbar(GetThumbPosition(), GetThumbSize(), range, GetPageSize());
233 }
234 
235 // static
236 wxVisualAttributes
GetClassDefaultAttributes(wxWindowVariant WXUNUSED (variant))237 wxScrollBar::GetClassDefaultAttributes(wxWindowVariant WXUNUSED(variant))
238 {
239     return GetDefaultAttributesFromGTKWidget(gtk_scrollbar_new(GTK_ORIENTATION_VERTICAL, NULL));
240 }
241 
242 #endif // wxUSE_SCROLLBAR
243