83ff6ca35daefb0408d40f41c5b1923053ac2088
[claws.git] / src / gtk / gtkvscrollbutton.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3  *
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.
8  *
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.
13  *
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.
18  */
19
20 /*
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/. 
25  */
26
27  /*
28   * Simple composite widget to provide vertical scrolling, based
29   * on GtkRange widget code.
30   * Modified by the Sylpheed Team and others 2003
31   */
32
33 #ifdef HAVE_CONFIG_H
34 #  include "config.h"
35 #endif
36
37 #include <gtk/gtksignal.h>
38 #include <gtk/gtkbutton.h>
39 #include <gtk/gtkmisc.h>
40 #include <gtk/gtk.h>
41 #include <gtk/gtkbox.h>
42 #include <gtk/gtkmain.h>
43
44 #include "gtkvscrollbutton.h"
45
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
50
51
52 enum {
53     ARG_0,
54     ARG_ADJUSTMENT
55 };
56
57 static void gtk_vscrollbutton_class_init(GtkVScrollbuttonClass * klass);
58 static void gtk_vscrollbutton_init(GtkVScrollbutton * vscrollbutton);
59
60 GtkType gtk_vscrollbutton_get_type              (void);
61 GtkWidget *gtk_vscrollbutton_new                (GtkAdjustment    *adjustment);
62
63 static gint gtk_vscrollbutton_button_release    (GtkWidget        *widget,
64                                                  GdkEventButton   *event,
65                                                  GtkVScrollbutton *scrollbutton);
66
67 static void gtk_vscrollbutton_set_adjustment    (GtkVScrollbutton *scrollbutton,
68                                                  GtkAdjustment    *adjustment);
69
70 static gint gtk_vscrollbutton_button_press      (GtkWidget        *widget,
71                                                  GdkEventButton   *event,
72                                                  GtkVScrollbutton *scrollbutton);
73
74 static gint gtk_vscrollbutton_button_release    (GtkWidget        *widget,
75                                                  GdkEventButton   *event,
76                                                  GtkVScrollbutton *scrollbutton);
77
78 static gint gtk_vscrollbutton_scroll            (GtkVScrollbutton *scrollbutton);
79
80 static gboolean gtk_vscrollbutton_timer_1st_time(GtkVScrollbutton *scrollbutton);
81
82 static void gtk_vscrollbutton_add_timer         (GtkVScrollbutton *scrollbutton);
83
84 static void gtk_vscrollbutton_remove_timer      (GtkVScrollbutton *scrollbutton);
85
86 static gint gtk_real_vscrollbutton_timer        (GtkVScrollbutton *scrollbutton);
87
88 GtkType gtk_vscrollbutton_get_type(void)
89 {
90     static GtkType vscrollbutton_type = 0;
91
92     if (!vscrollbutton_type) {
93         static const GtkTypeInfo vscrollbutton_info = {
94             "GtkVScrollbutton",
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,
102         };
103
104         vscrollbutton_type =
105             gtk_type_unique(GTK_TYPE_VBOX, &vscrollbutton_info);
106     }
107
108     return vscrollbutton_type;
109 }
110
111 static void gtk_vscrollbutton_class_init(GtkVScrollbuttonClass *class)
112 {
113 }
114
115 static void gtk_vscrollbutton_init(GtkVScrollbutton *scrollbutton)
116 {
117     GtkWidget *arrow;
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);
132
133     gtk_signal_connect(GTK_OBJECT(scrollbutton->upbutton),
134                        "button_press_event",
135                        GTK_SIGNAL_FUNC(gtk_vscrollbutton_button_press),
136                        scrollbutton);
137     gtk_signal_connect(GTK_OBJECT(scrollbutton->downbutton),
138                        "button_press_event",
139                        GTK_SIGNAL_FUNC(gtk_vscrollbutton_button_press),
140                        scrollbutton);
141     gtk_signal_connect(GTK_OBJECT(scrollbutton->upbutton),
142                        "button_release_event",
143                        GTK_SIGNAL_FUNC
144                        (gtk_vscrollbutton_button_release), scrollbutton);
145     gtk_signal_connect(GTK_OBJECT(scrollbutton->downbutton),
146                        "button_release_event",
147                        GTK_SIGNAL_FUNC
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;
154 }
155
156 GtkWidget *gtk_vscrollbutton_new(GtkAdjustment *adjustment)
157 {
158     GtkWidget *vscrollbutton;
159     vscrollbutton = GTK_WIDGET(gtk_type_new(gtk_vscrollbutton_get_type()));
160     gtk_vscrollbutton_set_adjustment(GTK_VSCROLLBUTTON(vscrollbutton),
161                                      adjustment);
162     return vscrollbutton;
163 }
164
165
166 void gtk_vscrollbutton_set_adjustment(GtkVScrollbutton *scrollbutton,
167                                       GtkAdjustment *adjustment)
168 {
169     g_return_if_fail(scrollbutton != NULL);
170     g_return_if_fail(GTK_IS_VSCROLLBUTTON(scrollbutton));
171
172     if (!adjustment)
173             adjustment =
174             GTK_ADJUSTMENT(gtk_adjustment_new(0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
175     else
176         g_return_if_fail(GTK_IS_ADJUSTMENT(adjustment));
177
178     if (scrollbutton->adjustment != adjustment) {
179         if (scrollbutton->adjustment) {
180             gtk_signal_disconnect_by_data(GTK_OBJECT
181                                           (scrollbutton->
182                                            adjustment), (gpointer)
183                                           scrollbutton);
184             gtk_object_unref(GTK_OBJECT(scrollbutton->adjustment));
185         }
186
187         scrollbutton->adjustment = adjustment;
188         gtk_object_ref(GTK_OBJECT(adjustment));
189         gtk_object_sink(GTK_OBJECT(adjustment));
190     }
191 }
192
193
194 static gint gtk_vscrollbutton_button_press(GtkWidget *widget,
195                                            GdkEventButton *event,
196                                            GtkVScrollbutton *scrollbutton)
197 {
198     if (!GTK_WIDGET_HAS_FOCUS(widget))
199         gtk_widget_grab_focus(widget);
200
201     if (scrollbutton->button == 0) {
202         gtk_grab_add(widget);
203         scrollbutton->button = event->button;
204
205         if (widget == scrollbutton->downbutton)
206             scrollbutton->scroll_type = GTK_SCROLL_STEP_FORWARD;
207         else
208             scrollbutton->scroll_type = GTK_SCROLL_STEP_BACKWARD;
209         gtk_vscrollbutton_scroll(scrollbutton);
210         gtk_vscrollbutton_add_timer(scrollbutton);
211     }
212     return TRUE;
213 }
214
215
216 static gint gtk_vscrollbutton_button_release(GtkWidget *widget,
217                                              GdkEventButton *event,
218                                              GtkVScrollbutton *scrollbutton)
219 {
220     if (!GTK_WIDGET_HAS_FOCUS(widget))
221         gtk_widget_grab_focus(widget);
222
223     if (scrollbutton->button == event->button) {
224         gtk_grab_remove(widget);
225
226         scrollbutton->button = 0;
227         gtk_vscrollbutton_remove_timer(scrollbutton);
228     }
229     return TRUE;
230 }
231
232 static gint gtk_vscrollbutton_scroll(GtkVScrollbutton *scrollbutton)
233 {
234     gfloat new_value;
235     gint return_val;
236
237     g_return_val_if_fail(scrollbutton != NULL, FALSE);
238     g_return_val_if_fail(GTK_IS_VSCROLLBUTTON(scrollbutton), FALSE);
239
240     new_value = scrollbutton->adjustment->value;
241     return_val = TRUE;
242
243     switch (scrollbutton->scroll_type) {
244
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;
249             return_val = FALSE;
250             scrollbutton->timer = 0;
251         }
252         break;
253
254     case GTK_SCROLL_STEP_FORWARD:
255         new_value += scrollbutton->adjustment->step_increment;
256         if (new_value >=
257             (scrollbutton->adjustment->upper -
258              scrollbutton->adjustment->page_size)) {
259             new_value =
260                 scrollbutton->adjustment->upper -
261                 scrollbutton->adjustment->page_size;
262             return_val = FALSE;
263             scrollbutton->timer = 0;
264         }
265         break;
266     
267     default:
268         break;
269     
270     }
271
272     if (new_value != scrollbutton->adjustment->value) {
273         scrollbutton->adjustment->value = new_value;
274         gtk_signal_emit_by_name(GTK_OBJECT
275                                 (scrollbutton->adjustment),
276                                 "value_changed");
277     }
278
279     return return_val;
280 }
281
282 static gboolean
283 gtk_vscrollbutton_timer_1st_time(GtkVScrollbutton *scrollbutton)
284 {
285     /*
286      * If the real timeout function succeeds and the timeout is still set,
287      * replace it with a quicker one so successive scrolling goes faster.
288      */
289     gtk_object_ref(GTK_OBJECT(scrollbutton));
290     if (scrollbutton->timer) {
291         /* We explicitely remove ourselves here in the paranoia
292          * that due to things happening above in the callback
293          * above, we might have been removed, and another added.
294          */
295         g_source_remove(scrollbutton->timer);
296         scrollbutton->timer = gtk_timeout_add(SCROLL_LATER_DELAY,
297                                               (GtkFunction)
298                                               gtk_real_vscrollbutton_timer,
299                                               scrollbutton);
300     }
301     gtk_object_unref(GTK_OBJECT(scrollbutton));
302     return FALSE;               /* don't keep calling this function */
303 }
304
305
306 static void gtk_vscrollbutton_add_timer(GtkVScrollbutton *scrollbutton)
307 {
308     g_return_if_fail(scrollbutton != NULL);
309     g_return_if_fail(GTK_IS_VSCROLLBUTTON(scrollbutton));
310
311     if (!scrollbutton->timer) {
312         scrollbutton->need_timer = TRUE;
313         scrollbutton->timer = gtk_timeout_add(SCROLL_INITIAL_DELAY,
314                                               (GtkFunction)
315                                               gtk_vscrollbutton_timer_1st_time,
316                                               scrollbutton);
317     }
318 }
319
320 static void gtk_vscrollbutton_remove_timer(GtkVScrollbutton *scrollbutton)
321 {
322     g_return_if_fail(scrollbutton != NULL);
323     g_return_if_fail(GTK_IS_VSCROLLBUTTON(scrollbutton));
324
325     if (scrollbutton->timer) {
326         gtk_timeout_remove(scrollbutton->timer);
327         scrollbutton->timer = 0;
328     }
329     scrollbutton->need_timer = FALSE;
330 }
331
332 static gint gtk_real_vscrollbutton_timer(GtkVScrollbutton *scrollbutton)
333 {
334     gint return_val;
335
336     GDK_THREADS_ENTER();
337
338     return_val = TRUE;
339     if (!scrollbutton->timer) {
340         return_val = FALSE;
341         if (scrollbutton->need_timer)
342             scrollbutton->timer =
343                 gtk_timeout_add(SCROLL_TIMER_LENGTH, 
344                                 (GtkFunction) gtk_real_vscrollbutton_timer,
345                                 (gpointer) scrollbutton);
346         else {
347             GDK_THREADS_LEAVE();
348             return FALSE;
349         }
350         scrollbutton->need_timer = FALSE;
351     }
352     GDK_THREADS_LEAVE();
353     return_val = gtk_vscrollbutton_scroll(scrollbutton);
354     return return_val;
355 }