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 2 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, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
21 * Modified by the GTK+ Team and others 1997-1999. See the AUTHORS
22 * file for a list of people on the GTK+ Team. See the ChangeLog
23 * files for a list of changes. These files are distributed with
24 * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
28 * Simple composite widget to provide vertical scrolling, based
29 * on GtkRange widget code.
30 * Modified by the Sylpheed Team and others 2003
37 #include <gtk/gtksignal.h>
38 #include <gtk/gtkbutton.h>
39 #include <gtk/gtkmisc.h>
41 #include <gtk/gtkbox.h>
42 #include <gtk/gtkmain.h>
44 #include "gtkvscrollbutton.h"
46 #define SCROLL_TIMER_LENGTH 20
47 #define SCROLL_INITIAL_DELAY 100 /* must hold button this long before ... */
48 #define SCROLL_LATER_DELAY 20 /* ... it starts repeating at this rate */
49 #define SCROLL_DELAY_LENGTH 300
57 static void gtk_vscrollbutton_class_init(GtkVScrollbuttonClass * klass);
58 static void gtk_vscrollbutton_init(GtkVScrollbutton * vscrollbutton);
60 GtkType gtk_vscrollbutton_get_type (void);
61 GtkWidget *gtk_vscrollbutton_new (GtkAdjustment *adjustment);
63 static gint gtk_vscrollbutton_button_release (GtkWidget *widget,
64 GdkEventButton *event,
65 GtkVScrollbutton *scrollbutton);
67 static void gtk_vscrollbutton_set_adjustment (GtkVScrollbutton *scrollbutton,
68 GtkAdjustment *adjustment);
70 static gint gtk_vscrollbutton_button_press (GtkWidget *widget,
71 GdkEventButton *event,
72 GtkVScrollbutton *scrollbutton);
74 static gint gtk_vscrollbutton_button_release (GtkWidget *widget,
75 GdkEventButton *event,
76 GtkVScrollbutton *scrollbutton);
78 static gint gtk_vscrollbutton_scroll (GtkVScrollbutton *scrollbutton);
80 static gboolean gtk_vscrollbutton_timer_1st_time(GtkVScrollbutton *scrollbutton);
82 static void gtk_vscrollbutton_add_timer (GtkVScrollbutton *scrollbutton);
84 static void gtk_vscrollbutton_remove_timer (GtkVScrollbutton *scrollbutton);
86 static gint gtk_real_vscrollbutton_timer (GtkVScrollbutton *scrollbutton);
88 GtkType gtk_vscrollbutton_get_type(void)
90 static GtkType vscrollbutton_type = 0;
92 if (!vscrollbutton_type) {
93 static const GtkTypeInfo vscrollbutton_info = {
95 sizeof(GtkVScrollbutton),
96 sizeof(GtkVScrollbuttonClass),
97 (GtkClassInitFunc) gtk_vscrollbutton_class_init,
98 (GtkObjectInitFunc) gtk_vscrollbutton_init,
99 /* reserved_1 */ NULL,
100 /* reserved_2 */ NULL,
101 (GtkClassInitFunc) NULL,
105 gtk_type_unique(GTK_TYPE_VBOX, &vscrollbutton_info);
108 return vscrollbutton_type;
111 static void gtk_vscrollbutton_class_init(GtkVScrollbuttonClass *class)
115 static void gtk_vscrollbutton_init(GtkVScrollbutton *scrollbutton)
118 scrollbutton->upbutton = gtk_button_new();
119 scrollbutton->downbutton = gtk_button_new();
120 arrow = gtk_arrow_new(GTK_ARROW_UP, GTK_SHADOW_NONE);
121 gtk_widget_show(arrow);
122 gtk_container_add(GTK_CONTAINER(scrollbutton->upbutton), arrow);
123 gtk_widget_set_usize(scrollbutton->upbutton, -1, 16);
124 arrow = gtk_arrow_new(GTK_ARROW_DOWN, GTK_SHADOW_NONE);
125 gtk_widget_show(arrow);
126 gtk_container_add(GTK_CONTAINER(scrollbutton->downbutton), arrow);
127 gtk_widget_set_usize(scrollbutton->downbutton, -1, 16);
128 GTK_WIDGET_UNSET_FLAGS(scrollbutton->upbutton, GTK_CAN_FOCUS);
129 GTK_WIDGET_UNSET_FLAGS(scrollbutton->downbutton, GTK_CAN_FOCUS);
130 gtk_widget_show(scrollbutton->downbutton);
131 gtk_widget_show(scrollbutton->upbutton);
133 gtk_signal_connect(GTK_OBJECT(scrollbutton->upbutton),
134 "button_press_event",
135 GTK_SIGNAL_FUNC(gtk_vscrollbutton_button_press),
137 gtk_signal_connect(GTK_OBJECT(scrollbutton->downbutton),
138 "button_press_event",
139 GTK_SIGNAL_FUNC(gtk_vscrollbutton_button_press),
141 gtk_signal_connect(GTK_OBJECT(scrollbutton->upbutton),
142 "button_release_event",
144 (gtk_vscrollbutton_button_release), scrollbutton);
145 gtk_signal_connect(GTK_OBJECT(scrollbutton->downbutton),
146 "button_release_event",
148 (gtk_vscrollbutton_button_release), scrollbutton);
149 gtk_box_pack_start(GTK_BOX(&scrollbutton->vbox),
150 scrollbutton->upbutton, TRUE, TRUE, 0);
151 gtk_box_pack_end(GTK_BOX(&scrollbutton->vbox),
152 scrollbutton->downbutton, TRUE, TRUE, 0);
153 scrollbutton->timer = 0;
156 GtkWidget *gtk_vscrollbutton_new(GtkAdjustment *adjustment)
158 GtkWidget *vscrollbutton;
159 vscrollbutton = GTK_WIDGET(gtk_type_new(gtk_vscrollbutton_get_type()));
160 gtk_vscrollbutton_set_adjustment(GTK_VSCROLLBUTTON(vscrollbutton),
162 return vscrollbutton;
166 void gtk_vscrollbutton_set_adjustment(GtkVScrollbutton *scrollbutton,
167 GtkAdjustment *adjustment)
169 g_return_if_fail(scrollbutton != NULL);
170 g_return_if_fail(GTK_IS_VSCROLLBUTTON(scrollbutton));
174 GTK_ADJUSTMENT(gtk_adjustment_new(0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
176 g_return_if_fail(GTK_IS_ADJUSTMENT(adjustment));
178 if (scrollbutton->adjustment != adjustment) {
179 if (scrollbutton->adjustment) {
180 gtk_signal_disconnect_by_data(GTK_OBJECT
182 adjustment), (gpointer)
184 gtk_object_unref(GTK_OBJECT(scrollbutton->adjustment));
187 scrollbutton->adjustment = adjustment;
188 gtk_object_ref(GTK_OBJECT(adjustment));
189 gtk_object_sink(GTK_OBJECT(adjustment));
194 static gint gtk_vscrollbutton_button_press(GtkWidget *widget,
195 GdkEventButton *event,
196 GtkVScrollbutton *scrollbutton)
198 if (!GTK_WIDGET_HAS_FOCUS(widget))
199 gtk_widget_grab_focus(widget);
201 if (scrollbutton->button == 0) {
202 gtk_grab_add(widget);
203 scrollbutton->button = event->button;
205 if (widget == scrollbutton->downbutton)
206 scrollbutton->scroll_type = GTK_SCROLL_STEP_FORWARD;
208 scrollbutton->scroll_type = GTK_SCROLL_STEP_BACKWARD;
209 gtk_vscrollbutton_scroll(scrollbutton);
210 gtk_vscrollbutton_add_timer(scrollbutton);
216 static gint gtk_vscrollbutton_button_release(GtkWidget *widget,
217 GdkEventButton *event,
218 GtkVScrollbutton *scrollbutton)
220 if (!GTK_WIDGET_HAS_FOCUS(widget))
221 gtk_widget_grab_focus(widget);
223 if (scrollbutton->button == event->button) {
224 gtk_grab_remove(widget);
226 scrollbutton->button = 0;
227 gtk_vscrollbutton_remove_timer(scrollbutton);
232 static gint gtk_vscrollbutton_scroll(GtkVScrollbutton *scrollbutton)
237 g_return_val_if_fail(scrollbutton != NULL, FALSE);
238 g_return_val_if_fail(GTK_IS_VSCROLLBUTTON(scrollbutton), FALSE);
240 new_value = scrollbutton->adjustment->value;
243 switch (scrollbutton->scroll_type) {
245 case GTK_SCROLL_STEP_BACKWARD:
246 new_value -= scrollbutton->adjustment->step_increment;
247 if (new_value <= scrollbutton->adjustment->lower) {
248 new_value = scrollbutton->adjustment->lower;
250 scrollbutton->timer = 0;
254 case GTK_SCROLL_STEP_FORWARD:
255 new_value += scrollbutton->adjustment->step_increment;
257 (scrollbutton->adjustment->upper -
258 scrollbutton->adjustment->page_size)) {
260 scrollbutton->adjustment->upper -
261 scrollbutton->adjustment->page_size;
263 scrollbutton->timer = 0;
268 if (new_value != scrollbutton->adjustment->value) {
269 scrollbutton->adjustment->value = new_value;
270 gtk_signal_emit_by_name(GTK_OBJECT
271 (scrollbutton->adjustment),
279 gtk_vscrollbutton_timer_1st_time(GtkVScrollbutton *scrollbutton)
282 * If the real timeout function succeeds and the timeout is still set,
283 * replace it with a quicker one so successive scrolling goes faster.
285 gtk_object_ref(GTK_OBJECT(scrollbutton));
286 if (scrollbutton->timer) {
287 /* We explicitely remove ourselves here in the paranoia
288 * that due to things happening above in the callback
289 * above, we might have been removed, and another added.
291 g_source_remove(scrollbutton->timer);
292 scrollbutton->timer = gtk_timeout_add(SCROLL_LATER_DELAY,
294 gtk_real_vscrollbutton_timer,
297 gtk_object_unref(GTK_OBJECT(scrollbutton));
298 return FALSE; /* don't keep calling this function */
302 static void gtk_vscrollbutton_add_timer(GtkVScrollbutton *scrollbutton)
304 g_return_if_fail(scrollbutton != NULL);
305 g_return_if_fail(GTK_IS_VSCROLLBUTTON(scrollbutton));
307 if (!scrollbutton->timer) {
308 scrollbutton->need_timer = TRUE;
309 scrollbutton->timer = gtk_timeout_add(SCROLL_INITIAL_DELAY,
311 gtk_vscrollbutton_timer_1st_time,
316 static void gtk_vscrollbutton_remove_timer(GtkVScrollbutton *scrollbutton)
318 g_return_if_fail(scrollbutton != NULL);
319 g_return_if_fail(GTK_IS_VSCROLLBUTTON(scrollbutton));
321 if (scrollbutton->timer) {
322 gtk_timeout_remove(scrollbutton->timer);
323 scrollbutton->timer = 0;
325 scrollbutton->need_timer = FALSE;
328 static gint gtk_real_vscrollbutton_timer(GtkVScrollbutton *scrollbutton)
335 if (!scrollbutton->timer) {
337 if (scrollbutton->need_timer)
338 scrollbutton->timer =
339 gtk_timeout_add(SCROLL_TIMER_LENGTH,
340 (GtkFunction) gtk_real_vscrollbutton_timer,
341 (gpointer) scrollbutton);
346 scrollbutton->need_timer = FALSE;
349 return_val = gtk_vscrollbutton_scroll(scrollbutton);