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
36 #include <gtk/gtksignal.h>
37 #include <gtk/gtkbutton.h>
38 #include <gtk/gtkmisc.h>
40 #include <gtk/gtkbox.h>
41 #include <gtk/gtkmain.h>
43 #include "gtkvscrollbutton.h"
45 #define SCROLL_TIMER_LENGTH 20
46 #define SCROLL_INITIAL_DELAY 100 /* must hold button this long before ... */
47 #define SCROLL_LATER_DELAY 20 /* ... it starts repeating at this rate */
48 #define SCROLL_DELAY_LENGTH 300
56 static void gtk_vscrollbutton_class_init(GtkVScrollbuttonClass * klass);
57 static void gtk_vscrollbutton_init(GtkVScrollbutton * vscrollbutton);
59 GtkType gtk_vscrollbutton_get_type (void);
60 GtkWidget *gtk_vscrollbutton_new (GtkAdjustment *adjustment);
62 static gint gtk_vscrollbutton_button_release (GtkWidget *widget,
63 GdkEventButton *event,
64 GtkVScrollbutton *scrollbutton);
66 static void gtk_vscrollbutton_set_adjustment (GtkVScrollbutton *scrollbutton,
67 GtkAdjustment *adjustment);
69 static gint gtk_vscrollbutton_button_press (GtkWidget *widget,
70 GdkEventButton *event,
71 GtkVScrollbutton *scrollbutton);
73 static gint gtk_vscrollbutton_button_release (GtkWidget *widget,
74 GdkEventButton *event,
75 GtkVScrollbutton *scrollbutton);
77 gint gtk_vscrollbutton_scroll (GtkVScrollbutton *scrollbutton);
79 static gboolean gtk_vscrollbutton_timer_1st_time(GtkVScrollbutton *scrollbutton);
81 static void gtk_vscrollbutton_add_timer (GtkVScrollbutton *scrollbutton);
83 static void gtk_vscrollbutton_remove_timer (GtkVScrollbutton *scrollbutton);
85 static gint gtk_real_vscrollbutton_timer (GtkVScrollbutton *scrollbutton);
87 static void gtk_vscrollbutton_set_sensitivity (GtkAdjustment *adjustment,
88 GtkVScrollbutton *scrollbutton);
90 GtkType gtk_vscrollbutton_get_type(void)
92 static GtkType vscrollbutton_type = 0;
94 if (!vscrollbutton_type) {
95 static const GtkTypeInfo vscrollbutton_info = {
97 sizeof(GtkVScrollbutton),
98 sizeof(GtkVScrollbuttonClass),
99 (GtkClassInitFunc) gtk_vscrollbutton_class_init,
100 (GtkObjectInitFunc) gtk_vscrollbutton_init,
101 /* reserved_1 */ NULL,
102 /* reserved_2 */ NULL,
103 (GtkClassInitFunc) NULL,
107 gtk_type_unique(GTK_TYPE_VBOX, &vscrollbutton_info);
110 return vscrollbutton_type;
113 static void gtk_vscrollbutton_class_init(GtkVScrollbuttonClass *class)
117 static void gtk_vscrollbutton_init(GtkVScrollbutton *scrollbutton)
120 scrollbutton->upbutton = gtk_button_new();
121 scrollbutton->downbutton = gtk_button_new();
122 arrow = gtk_arrow_new(GTK_ARROW_UP, GTK_SHADOW_NONE);
123 gtk_widget_show(arrow);
124 gtk_container_add(GTK_CONTAINER(scrollbutton->upbutton), arrow);
126 gtk_widget_set_size_request(scrollbutton->upbutton, -1, 16);
128 gtk_widget_set_size_request(scrollbutton->upbutton, -1, arrow->requisition.height + 8);
130 arrow = gtk_arrow_new(GTK_ARROW_DOWN, GTK_SHADOW_NONE);
131 gtk_widget_show(arrow);
132 gtk_container_add(GTK_CONTAINER(scrollbutton->downbutton), arrow);
134 gtk_widget_set_size_request(scrollbutton->downbutton, -1, 16);
136 gtk_widget_set_size_request(scrollbutton->downbutton, -1, arrow->requisition.height + 8);
138 GTK_WIDGET_UNSET_FLAGS(scrollbutton->upbutton, GTK_CAN_FOCUS);
139 GTK_WIDGET_UNSET_FLAGS(scrollbutton->downbutton, GTK_CAN_FOCUS);
140 gtk_widget_show(scrollbutton->downbutton);
141 gtk_widget_show(scrollbutton->upbutton);
143 g_signal_connect(G_OBJECT(scrollbutton->upbutton),
144 "button_press_event",
145 G_CALLBACK(gtk_vscrollbutton_button_press),
147 g_signal_connect(G_OBJECT(scrollbutton->downbutton),
148 "button_press_event",
149 G_CALLBACK(gtk_vscrollbutton_button_press),
151 g_signal_connect(G_OBJECT(scrollbutton->upbutton),
152 "button_release_event",
154 (gtk_vscrollbutton_button_release), scrollbutton);
155 g_signal_connect(G_OBJECT(scrollbutton->downbutton),
156 "button_release_event",
158 (gtk_vscrollbutton_button_release), scrollbutton);
159 gtk_box_pack_start(GTK_BOX(&scrollbutton->vbox),
160 scrollbutton->upbutton, TRUE, TRUE, 0);
161 gtk_box_pack_end(GTK_BOX(&scrollbutton->vbox),
162 scrollbutton->downbutton, TRUE, TRUE, 0);
163 scrollbutton->timer = 0;
166 GtkWidget *gtk_vscrollbutton_new(GtkAdjustment *adjustment)
168 GtkWidget *vscrollbutton;
169 vscrollbutton = GTK_WIDGET(gtk_type_new(gtk_vscrollbutton_get_type()));
170 gtk_vscrollbutton_set_adjustment(GTK_VSCROLLBUTTON(vscrollbutton),
172 g_signal_connect(G_OBJECT(GTK_VSCROLLBUTTON(vscrollbutton)->adjustment),
175 (gtk_vscrollbutton_set_sensitivity), vscrollbutton);
176 g_signal_connect(G_OBJECT(GTK_VSCROLLBUTTON(vscrollbutton)->adjustment),
179 (gtk_vscrollbutton_set_sensitivity), vscrollbutton);
180 return vscrollbutton;
184 void gtk_vscrollbutton_set_adjustment(GtkVScrollbutton *scrollbutton,
185 GtkAdjustment *adjustment)
187 g_return_if_fail(scrollbutton != NULL);
188 g_return_if_fail(GTK_IS_VSCROLLBUTTON(scrollbutton));
192 GTK_ADJUSTMENT(gtk_adjustment_new(0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
194 g_return_if_fail(GTK_IS_ADJUSTMENT(adjustment));
196 if (scrollbutton->adjustment != adjustment) {
197 if (scrollbutton->adjustment) {
198 g_signal_handlers_disconnect_matched(scrollbutton->adjustment,
201 (gpointer) scrollbutton);
202 g_object_unref(G_OBJECT(scrollbutton->adjustment));
205 scrollbutton->adjustment = adjustment;
206 g_object_ref(G_OBJECT(adjustment));
207 gtk_object_sink(GTK_OBJECT(adjustment));
211 static gint gtk_vscrollbutton_button_press(GtkWidget *widget,
212 GdkEventButton *event,
213 GtkVScrollbutton *scrollbutton)
215 if (!GTK_WIDGET_HAS_FOCUS(widget))
216 gtk_widget_grab_focus(widget);
218 if (scrollbutton->button == 0) {
219 gtk_grab_add(widget);
220 scrollbutton->button = event->button;
222 if (widget == scrollbutton->downbutton)
223 scrollbutton->scroll_type = GTK_SCROLL_STEP_FORWARD;
225 scrollbutton->scroll_type = GTK_SCROLL_STEP_BACKWARD;
226 gtk_vscrollbutton_scroll(scrollbutton);
227 gtk_vscrollbutton_add_timer(scrollbutton);
233 static gint gtk_vscrollbutton_button_release(GtkWidget *widget,
234 GdkEventButton *event,
235 GtkVScrollbutton *scrollbutton)
237 if (!GTK_WIDGET_HAS_FOCUS(widget))
238 gtk_widget_grab_focus(widget);
240 if (scrollbutton->button == event->button) {
241 gtk_grab_remove(widget);
243 scrollbutton->button = 0;
244 gtk_vscrollbutton_remove_timer(scrollbutton);
245 gtk_vscrollbutton_set_sensitivity(scrollbutton->adjustment, scrollbutton);
250 gint gtk_vscrollbutton_scroll(GtkVScrollbutton *scrollbutton)
255 g_return_val_if_fail(scrollbutton != NULL, FALSE);
256 g_return_val_if_fail(GTK_IS_VSCROLLBUTTON(scrollbutton), FALSE);
258 new_value = scrollbutton->adjustment->value;
261 switch (scrollbutton->scroll_type) {
263 case GTK_SCROLL_STEP_BACKWARD:
264 new_value -= scrollbutton->adjustment->step_increment;
265 if (new_value <= scrollbutton->adjustment->lower) {
266 new_value = scrollbutton->adjustment->lower;
268 scrollbutton->timer = 0;
272 case GTK_SCROLL_STEP_FORWARD:
273 new_value += scrollbutton->adjustment->step_increment;
275 (scrollbutton->adjustment->upper -
276 scrollbutton->adjustment->page_size)) {
278 scrollbutton->adjustment->upper -
279 scrollbutton->adjustment->page_size;
281 scrollbutton->timer = 0;
290 if (new_value != scrollbutton->adjustment->value) {
291 scrollbutton->adjustment->value = new_value;
292 g_signal_emit_by_name(G_OBJECT
293 (scrollbutton->adjustment),
295 gtk_widget_queue_resize(GTK_WIDGET(scrollbutton)); /* ensure resize */
302 gtk_vscrollbutton_timer_1st_time(GtkVScrollbutton *scrollbutton)
305 * If the real timeout function succeeds and the timeout is still set,
306 * replace it with a quicker one so successive scrolling goes faster.
308 g_object_ref(G_OBJECT(scrollbutton));
309 if (scrollbutton->timer) {
310 /* We explicitely remove ourselves here in the paranoia
311 * that due to things happening above in the callback
312 * above, we might have been removed, and another added.
314 g_source_remove(scrollbutton->timer);
315 scrollbutton->timer = g_timeout_add(SCROLL_LATER_DELAY,
317 gtk_real_vscrollbutton_timer,
320 g_object_unref(G_OBJECT(scrollbutton));
321 return FALSE; /* don't keep calling this function */
325 static void gtk_vscrollbutton_add_timer(GtkVScrollbutton *scrollbutton)
327 g_return_if_fail(scrollbutton != NULL);
328 g_return_if_fail(GTK_IS_VSCROLLBUTTON(scrollbutton));
330 if (!scrollbutton->timer) {
331 scrollbutton->need_timer = TRUE;
332 scrollbutton->timer = g_timeout_add(SCROLL_INITIAL_DELAY,
334 gtk_vscrollbutton_timer_1st_time,
339 static void gtk_vscrollbutton_remove_timer(GtkVScrollbutton *scrollbutton)
341 g_return_if_fail(scrollbutton != NULL);
342 g_return_if_fail(GTK_IS_VSCROLLBUTTON(scrollbutton));
344 if (scrollbutton->timer) {
345 g_source_remove(scrollbutton->timer);
346 scrollbutton->timer = 0;
348 scrollbutton->need_timer = FALSE;
351 static gint gtk_real_vscrollbutton_timer(GtkVScrollbutton *scrollbutton)
358 if (!scrollbutton->timer) {
360 if (scrollbutton->need_timer)
361 scrollbutton->timer =
362 g_timeout_add(SCROLL_TIMER_LENGTH,
363 (GtkFunction) gtk_real_vscrollbutton_timer,
364 (gpointer) scrollbutton);
369 scrollbutton->need_timer = FALSE;
372 return_val = gtk_vscrollbutton_scroll(scrollbutton);
376 static void gtk_vscrollbutton_set_sensitivity (GtkAdjustment *adjustment,
377 GtkVScrollbutton *scrollbutton)
379 if (!GTK_WIDGET_REALIZED(GTK_WIDGET(scrollbutton))) return;
380 if (scrollbutton->button != 0) return; /* not while something is pressed */
382 gtk_widget_set_sensitive(scrollbutton->upbutton,
383 (adjustment->value > adjustment->lower));
384 gtk_widget_set_sensitive(scrollbutton->downbutton,
385 (adjustment->value < (adjustment->upper - adjustment->page_size)));