1 /* GTK - The GIMP Toolkit
2 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 3 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
19 * Modified by the GTK+ Team and others 1997-1999. See the AUTHORS
20 * file for a list of people on the GTK+ Team. See the ChangeLog
21 * files for a list of changes. These files are distributed with
22 * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
26 * Simple composite widget to provide vertical scrolling, based
27 * on GtkRange widget code.
28 * Modified by the Sylpheed Team and others 2003
33 #include "claws-features.h"
39 #include "gtkvscrollbutton.h"
42 #define SCROLL_TIMER_LENGTH 20
43 #define SCROLL_INITIAL_DELAY 100 /* must hold button this long before ... */
44 #define SCROLL_LATER_DELAY 20 /* ... it starts repeating at this rate */
45 #define SCROLL_DELAY_LENGTH 300
53 static void gtk_vscrollbutton_class_init(GtkVScrollbuttonClass
* klass
);
54 static void gtk_vscrollbutton_init(GtkVScrollbutton
* vscrollbutton
);
56 GType
gtk_vscrollbutton_get_type (void);
57 GtkWidget
*gtk_vscrollbutton_new (GtkAdjustment
*adjustment
);
59 static gint
gtk_vscrollbutton_button_release (GtkWidget
*widget
,
60 GdkEventButton
*event
,
61 GtkVScrollbutton
*scrollbutton
);
63 static void gtk_vscrollbutton_set_adjustment (GtkVScrollbutton
*scrollbutton
,
64 GtkAdjustment
*adjustment
);
66 static gint
gtk_vscrollbutton_button_press (GtkWidget
*widget
,
67 GdkEventButton
*event
,
68 GtkVScrollbutton
*scrollbutton
);
70 static gint
gtk_vscrollbutton_button_release (GtkWidget
*widget
,
71 GdkEventButton
*event
,
72 GtkVScrollbutton
*scrollbutton
);
74 gint
gtk_vscrollbutton_scroll (GtkVScrollbutton
*scrollbutton
);
76 static gboolean
gtk_vscrollbutton_timer_1st_time(GtkVScrollbutton
*scrollbutton
);
78 static void gtk_vscrollbutton_add_timer (GtkVScrollbutton
*scrollbutton
);
80 static void gtk_vscrollbutton_remove_timer (GtkVScrollbutton
*scrollbutton
);
82 static gboolean
gtk_real_vscrollbutton_timer (GtkVScrollbutton
*scrollbutton
);
84 static void gtk_vscrollbutton_set_sensitivity (GtkAdjustment
*adjustment
,
85 GtkVScrollbutton
*scrollbutton
);
87 GType
gtk_vscrollbutton_get_type(void)
89 static GType vscrollbutton_type
= 0;
91 if (!vscrollbutton_type
) {
92 static const GTypeInfo vscrollbutton_info
= {
93 sizeof (GtkVScrollbuttonClass
),
96 (GBaseFinalizeFunc
) NULL
,
98 (GClassInitFunc
) gtk_vscrollbutton_class_init
,
99 (GClassFinalizeFunc
) NULL
,
100 NULL
, /* class_data */
102 sizeof (GtkVScrollbutton
),
104 (GInstanceInitFunc
) gtk_vscrollbutton_init
,
106 (const GTypeValueTable
*) NULL
/* value table */
109 vscrollbutton_type
= g_type_register_static (GTK_TYPE_BOX
, "GtkVScrollbutton", &vscrollbutton_info
, (GTypeFlags
)0);
112 return vscrollbutton_type
;
115 static void gtk_vscrollbutton_class_init(GtkVScrollbuttonClass
*class)
119 static GdkCursor
*hand_cursor
= NULL
;
121 static gboolean
vscroll_visi_notify(GtkWidget
*widget
,
122 GdkEventVisibility
*event
,
125 gdk_window_set_cursor(gtk_widget_get_window(widget
), hand_cursor
);
129 static gboolean
vscroll_leave_notify(GtkWidget
*widget
,
130 GdkEventCrossing
*event
,
133 gdk_window_set_cursor(gtk_widget_get_window(widget
), NULL
);
137 static gboolean
vscroll_enter_notify(GtkWidget
*widget
,
138 GdkEventCrossing
*event
,
141 gdk_window_set_cursor(gtk_widget_get_window(widget
), hand_cursor
);
146 static void gtk_vscrollbutton_init(GtkVScrollbutton
*scrollbutton
)
151 hand_cursor
= gdk_cursor_new_for_display(
152 gtk_widget_get_display(GTK_WIDGET(scrollbutton
)),GDK_HAND2
);
154 scrollbutton
->upbutton
= gtk_event_box_new();
155 scrollbutton
->downbutton
= gtk_event_box_new();
156 arrow
= gtk_image_new_from_icon_name("pan-up-symbolic", GTK_ICON_SIZE_MENU
);
157 gtk_widget_show(arrow
);
158 gtk_container_add(GTK_CONTAINER(scrollbutton
->upbutton
), arrow
);
159 gtk_widget_set_size_request(scrollbutton
->upbutton
, -1, 16);
160 arrow
= gtk_image_new_from_icon_name("pan-down-symbolic", GTK_ICON_SIZE_MENU
);
161 gtk_widget_show(arrow
);
162 gtk_container_add(GTK_CONTAINER(scrollbutton
->downbutton
), arrow
);
163 gtk_widget_set_size_request(scrollbutton
->downbutton
, -1, 16);
164 gtk_widget_set_can_focus(scrollbutton
->upbutton
, FALSE
);
165 gtk_widget_set_can_focus(scrollbutton
->downbutton
, FALSE
);
166 gtk_widget_show(scrollbutton
->downbutton
);
167 gtk_widget_show(scrollbutton
->upbutton
);
169 g_signal_connect(G_OBJECT(scrollbutton
->upbutton
), "motion-notify-event",
170 G_CALLBACK(vscroll_visi_notify
), NULL
);
171 g_signal_connect(G_OBJECT(scrollbutton
->upbutton
), "leave-notify-event",
172 G_CALLBACK(vscroll_leave_notify
), NULL
);
173 g_signal_connect(G_OBJECT(scrollbutton
->upbutton
), "enter-notify-event",
174 G_CALLBACK(vscroll_enter_notify
), NULL
);
175 g_signal_connect(G_OBJECT(scrollbutton
->downbutton
), "motion-notify-event",
176 G_CALLBACK(vscroll_visi_notify
), NULL
);
177 g_signal_connect(G_OBJECT(scrollbutton
->downbutton
), "leave-notify-event",
178 G_CALLBACK(vscroll_leave_notify
), NULL
);
179 g_signal_connect(G_OBJECT(scrollbutton
->downbutton
), "enter-notify-event",
180 G_CALLBACK(vscroll_enter_notify
), NULL
);
182 g_signal_connect(G_OBJECT(scrollbutton
->upbutton
),
183 "button_press_event",
184 G_CALLBACK(gtk_vscrollbutton_button_press
),
186 g_signal_connect(G_OBJECT(scrollbutton
->downbutton
),
187 "button_press_event",
188 G_CALLBACK(gtk_vscrollbutton_button_press
),
190 g_signal_connect(G_OBJECT(scrollbutton
->upbutton
),
191 "button_release_event",
193 (gtk_vscrollbutton_button_release
), scrollbutton
);
194 g_signal_connect(G_OBJECT(scrollbutton
->downbutton
),
195 "button_release_event",
197 (gtk_vscrollbutton_button_release
), scrollbutton
);
198 gtk_box_pack_start(GTK_BOX(&scrollbutton
->vbox
),
199 scrollbutton
->upbutton
, TRUE
, TRUE
, 0);
200 gtk_box_pack_end(GTK_BOX(&scrollbutton
->vbox
),
201 scrollbutton
->downbutton
, TRUE
, TRUE
, 0);
202 scrollbutton
->timer
= 0;
205 GtkWidget
*gtk_vscrollbutton_new(GtkAdjustment
*adjustment
)
207 GtkWidget
*vscrollbutton
;
208 vscrollbutton
= g_object_new (gtk_vscrollbutton_get_type(),
210 gtk_vscrollbutton_set_adjustment(GTK_VSCROLLBUTTON(vscrollbutton
),
212 g_signal_connect(G_OBJECT(GTK_VSCROLLBUTTON(vscrollbutton
)->adjustment
),
215 (gtk_vscrollbutton_set_sensitivity
), vscrollbutton
);
216 g_signal_connect(G_OBJECT(GTK_VSCROLLBUTTON(vscrollbutton
)->adjustment
),
219 (gtk_vscrollbutton_set_sensitivity
), vscrollbutton
);
220 return vscrollbutton
;
224 void gtk_vscrollbutton_set_adjustment(GtkVScrollbutton
*scrollbutton
,
225 GtkAdjustment
*adjustment
)
227 cm_return_if_fail(scrollbutton
!= NULL
);
228 cm_return_if_fail(GTK_IS_VSCROLLBUTTON(scrollbutton
));
232 GTK_ADJUSTMENT(gtk_adjustment_new(0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
234 cm_return_if_fail(GTK_IS_ADJUSTMENT(adjustment
));
236 if (scrollbutton
->adjustment
!= adjustment
) {
237 if (scrollbutton
->adjustment
) {
238 g_signal_handlers_disconnect_matched(scrollbutton
->adjustment
,
241 (gpointer
) scrollbutton
);
242 g_object_unref(G_OBJECT(scrollbutton
->adjustment
));
245 scrollbutton
->adjustment
= adjustment
;
246 g_object_ref(G_OBJECT(adjustment
));
247 g_object_ref_sink (G_OBJECT(adjustment
));
251 static gint
gtk_vscrollbutton_button_press(GtkWidget
*widget
,
252 GdkEventButton
*event
,
253 GtkVScrollbutton
*scrollbutton
)
255 if (!gtk_widget_has_focus(widget
))
256 gtk_widget_grab_focus(widget
);
258 if (scrollbutton
->button
== 0) {
259 gtk_grab_add(widget
);
260 scrollbutton
->button
= event
->button
;
262 if (widget
== scrollbutton
->downbutton
)
263 scrollbutton
->scroll_type
= GTK_SCROLL_STEP_FORWARD
;
265 scrollbutton
->scroll_type
= GTK_SCROLL_STEP_BACKWARD
;
266 gtk_vscrollbutton_scroll(scrollbutton
);
267 gtk_vscrollbutton_add_timer(scrollbutton
);
273 static gint
gtk_vscrollbutton_button_release(GtkWidget
*widget
,
274 GdkEventButton
*event
,
275 GtkVScrollbutton
*scrollbutton
)
277 if (!gtk_widget_has_focus(widget
))
278 gtk_widget_grab_focus(widget
);
280 if (scrollbutton
->button
== event
->button
) {
281 gtk_grab_remove(widget
);
283 scrollbutton
->button
= 0;
284 gtk_vscrollbutton_remove_timer(scrollbutton
);
285 gtk_vscrollbutton_set_sensitivity(scrollbutton
->adjustment
, scrollbutton
);
290 gboolean
gtk_vscrollbutton_scroll(GtkVScrollbutton
*scrollbutton
)
298 cm_return_val_if_fail(scrollbutton
!= NULL
, FALSE
);
299 cm_return_val_if_fail(GTK_IS_VSCROLLBUTTON(scrollbutton
), FALSE
);
301 new_value
= value
= gtk_adjustment_get_value(scrollbutton
->adjustment
);
304 switch (scrollbutton
->scroll_type
) {
306 case GTK_SCROLL_STEP_BACKWARD
:
307 new_value
= value
- gtk_adjustment_get_step_increment(scrollbutton
->adjustment
);
308 bound
= gtk_adjustment_get_lower(scrollbutton
->adjustment
);
309 if (new_value
<= bound
) {
312 scrollbutton
->timer
= 0;
316 case GTK_SCROLL_STEP_FORWARD
:
317 new_value
= value
+ gtk_adjustment_get_step_increment(scrollbutton
->adjustment
);
318 bound
= gtk_adjustment_get_upper(scrollbutton
->adjustment
);
319 page_size
= gtk_adjustment_get_page_size(scrollbutton
->adjustment
);
320 if (new_value
>= (bound
- page_size
)) {
321 new_value
= bound
- page_size
;
323 scrollbutton
->timer
= 0;
332 if (new_value
!= value
) {
333 gtk_adjustment_set_value(scrollbutton
->adjustment
, new_value
);
334 g_signal_emit_by_name(G_OBJECT
335 (scrollbutton
->adjustment
),
337 gtk_widget_queue_resize(GTK_WIDGET(scrollbutton
)); /* ensure resize */
344 gtk_vscrollbutton_timer_1st_time(GtkVScrollbutton
*scrollbutton
)
347 * If the real timeout function succeeds and the timeout is still set,
348 * replace it with a quicker one so successive scrolling goes faster.
350 g_object_ref(G_OBJECT(scrollbutton
));
351 if (scrollbutton
->timer
) {
352 /* We explicitely remove ourselves here in the paranoia
353 * that due to things happening above in the callback
354 * above, we might have been removed, and another added.
356 g_source_remove(scrollbutton
->timer
);
357 scrollbutton
->timer
= g_timeout_add(SCROLL_LATER_DELAY
,
359 gtk_real_vscrollbutton_timer
,
362 g_object_unref(G_OBJECT(scrollbutton
));
363 return FALSE
; /* don't keep calling this function */
367 static void gtk_vscrollbutton_add_timer(GtkVScrollbutton
*scrollbutton
)
369 cm_return_if_fail(scrollbutton
!= NULL
);
370 cm_return_if_fail(GTK_IS_VSCROLLBUTTON(scrollbutton
));
372 if (!scrollbutton
->timer
) {
373 scrollbutton
->need_timer
= TRUE
;
374 scrollbutton
->timer
= g_timeout_add(SCROLL_INITIAL_DELAY
,
376 gtk_vscrollbutton_timer_1st_time
,
381 static void gtk_vscrollbutton_remove_timer(GtkVScrollbutton
*scrollbutton
)
383 cm_return_if_fail(scrollbutton
!= NULL
);
384 cm_return_if_fail(GTK_IS_VSCROLLBUTTON(scrollbutton
));
386 if (scrollbutton
->timer
) {
387 g_source_remove(scrollbutton
->timer
);
388 scrollbutton
->timer
= 0;
390 scrollbutton
->need_timer
= FALSE
;
393 static gboolean
gtk_real_vscrollbutton_timer(GtkVScrollbutton
*scrollbutton
)
400 if (!scrollbutton
->timer
) {
402 if (scrollbutton
->need_timer
)
403 scrollbutton
->timer
=
404 g_timeout_add(SCROLL_TIMER_LENGTH
,
405 (GSourceFunc
) gtk_real_vscrollbutton_timer
,
406 (gpointer
) scrollbutton
);
411 scrollbutton
->need_timer
= FALSE
;
414 return_val
= gtk_vscrollbutton_scroll(scrollbutton
);
418 static void gtk_vscrollbutton_set_sensitivity (GtkAdjustment
*adjustment
,
419 GtkVScrollbutton
*scrollbutton
)
422 if (!gtk_widget_get_realized(GTK_WIDGET(scrollbutton
))) return;
423 if (scrollbutton
->button
!= 0) return; /* not while something is pressed */
425 value
= gtk_adjustment_get_value(adjustment
);
426 gtk_widget_set_sensitive(scrollbutton
->upbutton
,
427 (value
> gtk_adjustment_get_lower(adjustment
)));
428 gtk_widget_set_sensitive(scrollbutton
->downbutton
,
429 (value
< (gtk_adjustment_get_upper(adjustment
) -
430 gtk_adjustment_get_page_size(adjustment
))));