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
38 #include "gtkvscrollbutton.h"
40 #define SCROLL_TIMER_LENGTH 20
41 #define SCROLL_INITIAL_DELAY 100 /* must hold button this long before ... */
42 #define SCROLL_LATER_DELAY 20 /* ... it starts repeating at this rate */
43 #define SCROLL_DELAY_LENGTH 300
51 static void gtk_vscrollbutton_class_init(GtkVScrollbuttonClass * klass);
52 static void gtk_vscrollbutton_init(GtkVScrollbutton * vscrollbutton);
54 GType gtk_vscrollbutton_get_type (void);
55 GtkWidget *gtk_vscrollbutton_new (GtkAdjustment *adjustment);
57 static gint gtk_vscrollbutton_button_release (GtkWidget *widget,
58 GdkEventButton *event,
59 GtkVScrollbutton *scrollbutton);
61 static void gtk_vscrollbutton_set_adjustment (GtkVScrollbutton *scrollbutton,
62 GtkAdjustment *adjustment);
64 static gint gtk_vscrollbutton_button_press (GtkWidget *widget,
65 GdkEventButton *event,
66 GtkVScrollbutton *scrollbutton);
68 static gint gtk_vscrollbutton_button_release (GtkWidget *widget,
69 GdkEventButton *event,
70 GtkVScrollbutton *scrollbutton);
72 gint gtk_vscrollbutton_scroll (GtkVScrollbutton *scrollbutton);
74 static gboolean gtk_vscrollbutton_timer_1st_time(GtkVScrollbutton *scrollbutton);
76 static void gtk_vscrollbutton_add_timer (GtkVScrollbutton *scrollbutton);
78 static void gtk_vscrollbutton_remove_timer (GtkVScrollbutton *scrollbutton);
80 static gint gtk_real_vscrollbutton_timer (GtkVScrollbutton *scrollbutton);
82 static void gtk_vscrollbutton_set_sensitivity (GtkAdjustment *adjustment,
83 GtkVScrollbutton *scrollbutton);
85 GType gtk_vscrollbutton_get_type(void)
87 static GType vscrollbutton_type = 0;
89 if (!vscrollbutton_type) {
90 static const GTypeInfo vscrollbutton_info = {
91 sizeof (GtkVScrollbuttonClass),
94 (GBaseFinalizeFunc) NULL,
96 (GClassInitFunc) gtk_vscrollbutton_class_init,
97 (GClassFinalizeFunc) NULL,
98 NULL, /* class_data */
100 sizeof (GtkVScrollbutton),
102 (GInstanceInitFunc) gtk_vscrollbutton_init,
105 vscrollbutton_type = g_type_register_static (GTK_TYPE_VBOX, "GtkVScrollbutton", &vscrollbutton_info, (GTypeFlags)0);
108 return vscrollbutton_type;
111 static void gtk_vscrollbutton_class_init(GtkVScrollbuttonClass *class)
115 static GdkCursor *hand_cursor = NULL;
117 static gboolean vscroll_visi_notify(GtkWidget *widget,
118 GdkEventVisibility *event,
121 gdk_window_set_cursor(widget->window, hand_cursor);
125 static gboolean vscroll_leave_notify(GtkWidget *widget,
126 GdkEventCrossing *event,
129 gdk_window_set_cursor(widget->window, NULL);
133 static gboolean vscroll_enter_notify(GtkWidget *widget,
134 GdkEventCrossing *event,
137 gdk_window_set_cursor(widget->window, hand_cursor);
142 static void gtk_vscrollbutton_init(GtkVScrollbutton *scrollbutton)
147 hand_cursor = gdk_cursor_new(GDK_HAND2);
149 scrollbutton->upbutton = gtk_event_box_new();
150 scrollbutton->downbutton = gtk_event_box_new();
151 arrow = gtk_arrow_new(GTK_ARROW_UP, GTK_SHADOW_NONE);
152 gtk_widget_show(arrow);
153 gtk_container_add(GTK_CONTAINER(scrollbutton->upbutton), arrow);
154 gtk_widget_set_size_request(scrollbutton->upbutton, -1, 16);
155 arrow = gtk_arrow_new(GTK_ARROW_DOWN, GTK_SHADOW_NONE);
156 gtk_widget_show(arrow);
157 gtk_container_add(GTK_CONTAINER(scrollbutton->downbutton), arrow);
158 gtk_widget_set_size_request(scrollbutton->downbutton, -1, 16);
159 GTK_WIDGET_UNSET_FLAGS(scrollbutton->upbutton, GTK_CAN_FOCUS);
160 GTK_WIDGET_UNSET_FLAGS(scrollbutton->downbutton, GTK_CAN_FOCUS);
161 gtk_widget_show(scrollbutton->downbutton);
162 gtk_widget_show(scrollbutton->upbutton);
164 g_signal_connect(G_OBJECT(scrollbutton->upbutton), "visibility-notify-event",
165 G_CALLBACK(vscroll_visi_notify), NULL);
166 g_signal_connect(G_OBJECT(scrollbutton->upbutton), "motion-notify-event",
167 G_CALLBACK(vscroll_visi_notify), NULL);
168 g_signal_connect(G_OBJECT(scrollbutton->upbutton), "leave-notify-event",
169 G_CALLBACK(vscroll_leave_notify), NULL);
170 g_signal_connect(G_OBJECT(scrollbutton->upbutton), "enter-notify-event",
171 G_CALLBACK(vscroll_enter_notify), NULL);
172 g_signal_connect(G_OBJECT(scrollbutton->downbutton), "visibility-notify-event",
173 G_CALLBACK(vscroll_visi_notify), NULL);
174 g_signal_connect(G_OBJECT(scrollbutton->downbutton), "motion-notify-event",
175 G_CALLBACK(vscroll_visi_notify), NULL);
176 g_signal_connect(G_OBJECT(scrollbutton->downbutton), "leave-notify-event",
177 G_CALLBACK(vscroll_leave_notify), NULL);
178 g_signal_connect(G_OBJECT(scrollbutton->downbutton), "enter-notify-event",
179 G_CALLBACK(vscroll_enter_notify), NULL);
181 g_signal_connect(G_OBJECT(scrollbutton->upbutton),
182 "button_press_event",
183 G_CALLBACK(gtk_vscrollbutton_button_press),
185 g_signal_connect(G_OBJECT(scrollbutton->downbutton),
186 "button_press_event",
187 G_CALLBACK(gtk_vscrollbutton_button_press),
189 g_signal_connect(G_OBJECT(scrollbutton->upbutton),
190 "button_release_event",
192 (gtk_vscrollbutton_button_release), scrollbutton);
193 g_signal_connect(G_OBJECT(scrollbutton->downbutton),
194 "button_release_event",
196 (gtk_vscrollbutton_button_release), scrollbutton);
197 gtk_box_pack_start(GTK_BOX(&scrollbutton->vbox),
198 scrollbutton->upbutton, TRUE, TRUE, 0);
199 gtk_box_pack_end(GTK_BOX(&scrollbutton->vbox),
200 scrollbutton->downbutton, TRUE, TRUE, 0);
201 scrollbutton->timer = 0;
204 GtkWidget *gtk_vscrollbutton_new(GtkAdjustment *adjustment)
206 GtkWidget *vscrollbutton;
207 vscrollbutton = g_object_new (gtk_vscrollbutton_get_type(),
209 gtk_vscrollbutton_set_adjustment(GTK_VSCROLLBUTTON(vscrollbutton),
211 g_signal_connect(G_OBJECT(GTK_VSCROLLBUTTON(vscrollbutton)->adjustment),
214 (gtk_vscrollbutton_set_sensitivity), vscrollbutton);
215 g_signal_connect(G_OBJECT(GTK_VSCROLLBUTTON(vscrollbutton)->adjustment),
218 (gtk_vscrollbutton_set_sensitivity), vscrollbutton);
219 return vscrollbutton;
223 void gtk_vscrollbutton_set_adjustment(GtkVScrollbutton *scrollbutton,
224 GtkAdjustment *adjustment)
226 cm_return_if_fail(scrollbutton != NULL);
227 cm_return_if_fail(GTK_IS_VSCROLLBUTTON(scrollbutton));
231 GTK_ADJUSTMENT(gtk_adjustment_new(0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
233 cm_return_if_fail(GTK_IS_ADJUSTMENT(adjustment));
235 if (scrollbutton->adjustment != adjustment) {
236 if (scrollbutton->adjustment) {
237 g_signal_handlers_disconnect_matched(scrollbutton->adjustment,
240 (gpointer) scrollbutton);
241 g_object_unref(G_OBJECT(scrollbutton->adjustment));
244 scrollbutton->adjustment = adjustment;
245 g_object_ref(G_OBJECT(adjustment));
246 #if GLIB_CHECK_VERSION(2,10,0)
247 g_object_ref_sink (G_OBJECT(adjustment));
249 gtk_object_ref (G_OBJECT (adjustment));
250 gtk_object_sink (G_OBJECT (adjustment));
255 static gint gtk_vscrollbutton_button_press(GtkWidget *widget,
256 GdkEventButton *event,
257 GtkVScrollbutton *scrollbutton)
259 if (!GTK_WIDGET_HAS_FOCUS(widget))
260 gtk_widget_grab_focus(widget);
262 if (scrollbutton->button == 0) {
263 gtk_grab_add(widget);
264 scrollbutton->button = event->button;
266 if (widget == scrollbutton->downbutton)
267 scrollbutton->scroll_type = GTK_SCROLL_STEP_FORWARD;
269 scrollbutton->scroll_type = GTK_SCROLL_STEP_BACKWARD;
270 gtk_vscrollbutton_scroll(scrollbutton);
271 gtk_vscrollbutton_add_timer(scrollbutton);
277 static gint gtk_vscrollbutton_button_release(GtkWidget *widget,
278 GdkEventButton *event,
279 GtkVScrollbutton *scrollbutton)
281 if (!GTK_WIDGET_HAS_FOCUS(widget))
282 gtk_widget_grab_focus(widget);
284 if (scrollbutton->button == event->button) {
285 gtk_grab_remove(widget);
287 scrollbutton->button = 0;
288 gtk_vscrollbutton_remove_timer(scrollbutton);
289 gtk_vscrollbutton_set_sensitivity(scrollbutton->adjustment, scrollbutton);
294 gint gtk_vscrollbutton_scroll(GtkVScrollbutton *scrollbutton)
299 cm_return_val_if_fail(scrollbutton != NULL, FALSE);
300 cm_return_val_if_fail(GTK_IS_VSCROLLBUTTON(scrollbutton), FALSE);
302 new_value = scrollbutton->adjustment->value;
305 switch (scrollbutton->scroll_type) {
307 case GTK_SCROLL_STEP_BACKWARD:
308 new_value -= scrollbutton->adjustment->step_increment;
309 if (new_value <= scrollbutton->adjustment->lower) {
310 new_value = scrollbutton->adjustment->lower;
312 scrollbutton->timer = 0;
316 case GTK_SCROLL_STEP_FORWARD:
317 new_value += scrollbutton->adjustment->step_increment;
319 (scrollbutton->adjustment->upper -
320 scrollbutton->adjustment->page_size)) {
322 scrollbutton->adjustment->upper -
323 scrollbutton->adjustment->page_size;
325 scrollbutton->timer = 0;
334 if (new_value != scrollbutton->adjustment->value) {
335 scrollbutton->adjustment->value = new_value;
336 g_signal_emit_by_name(G_OBJECT
337 (scrollbutton->adjustment),
339 gtk_widget_queue_resize(GTK_WIDGET(scrollbutton)); /* ensure resize */
346 gtk_vscrollbutton_timer_1st_time(GtkVScrollbutton *scrollbutton)
349 * If the real timeout function succeeds and the timeout is still set,
350 * replace it with a quicker one so successive scrolling goes faster.
352 g_object_ref(G_OBJECT(scrollbutton));
353 if (scrollbutton->timer) {
354 /* We explicitely remove ourselves here in the paranoia
355 * that due to things happening above in the callback
356 * above, we might have been removed, and another added.
358 g_source_remove(scrollbutton->timer);
359 scrollbutton->timer = g_timeout_add(SCROLL_LATER_DELAY,
361 gtk_real_vscrollbutton_timer,
364 g_object_unref(G_OBJECT(scrollbutton));
365 return FALSE; /* don't keep calling this function */
369 static void gtk_vscrollbutton_add_timer(GtkVScrollbutton *scrollbutton)
371 cm_return_if_fail(scrollbutton != NULL);
372 cm_return_if_fail(GTK_IS_VSCROLLBUTTON(scrollbutton));
374 if (!scrollbutton->timer) {
375 scrollbutton->need_timer = TRUE;
376 scrollbutton->timer = g_timeout_add(SCROLL_INITIAL_DELAY,
378 gtk_vscrollbutton_timer_1st_time,
383 static void gtk_vscrollbutton_remove_timer(GtkVScrollbutton *scrollbutton)
385 cm_return_if_fail(scrollbutton != NULL);
386 cm_return_if_fail(GTK_IS_VSCROLLBUTTON(scrollbutton));
388 if (scrollbutton->timer) {
389 g_source_remove(scrollbutton->timer);
390 scrollbutton->timer = 0;
392 scrollbutton->need_timer = FALSE;
395 static gint gtk_real_vscrollbutton_timer(GtkVScrollbutton *scrollbutton)
402 if (!scrollbutton->timer) {
404 if (scrollbutton->need_timer)
405 scrollbutton->timer =
406 g_timeout_add(SCROLL_TIMER_LENGTH,
407 (GtkFunction) gtk_real_vscrollbutton_timer,
408 (gpointer) scrollbutton);
413 scrollbutton->need_timer = FALSE;
416 return_val = gtk_vscrollbutton_scroll(scrollbutton);
420 static void gtk_vscrollbutton_set_sensitivity (GtkAdjustment *adjustment,
421 GtkVScrollbutton *scrollbutton)
423 if (!GTK_WIDGET_REALIZED(GTK_WIDGET(scrollbutton))) return;
424 if (scrollbutton->button != 0) return; /* not while something is pressed */
426 gtk_widget_set_sensitive(scrollbutton->upbutton,
427 (adjustment->value > adjustment->lower));
428 gtk_widget_set_sensitive(scrollbutton->downbutton,
429 (adjustment->value < (adjustment->upper - adjustment->page_size)));