talons

Fork of Claws Mail https://www.claws-mail
Log | Files | Refs | README | LICENSE

gtkvscrollbutton.c (13610B)


      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 3 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, see <http://www.gnu.org/licenses/>.
     16  */
     17 
     18 /*
     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/.
     23  */
     24 
     25  /*
     26   * Simple composite widget to provide vertical scrolling, based
     27   * on GtkRange widget code.
     28   * Modified by the Sylpheed Team and others 2003
     29   */
     30 
     31 #include <glib.h>
     32 #include <gtk/gtk.h>
     33 #include "utils.h"
     34 #include "gtkvscrollbutton.h"
     35 #include "gtkutils.h"
     36 
     37 #define SCROLL_TIMER_LENGTH  20
     38 #define SCROLL_INITIAL_DELAY 100	/* must hold button this long before ... */
     39 #define SCROLL_LATER_DELAY   20	/* ... it starts repeating at this rate  */
     40 #define SCROLL_DELAY_LENGTH  300
     41 
     42 
     43 enum {
     44     ARG_0,
     45     ARG_ADJUSTMENT
     46 };
     47 
     48 static void gtk_vscrollbutton_class_init(GtkVScrollbuttonClass * klass);
     49 static void gtk_vscrollbutton_init(GtkVScrollbutton * vscrollbutton);
     50 
     51 GType gtk_vscrollbutton_get_type		(void);
     52 GtkWidget *gtk_vscrollbutton_new		(GtkAdjustment 	  *adjustment);
     53 
     54 static gint gtk_vscrollbutton_button_release	(GtkWidget 	  *widget,
     55 					     	 GdkEventButton   *event,
     56 					     	 GtkVScrollbutton *scrollbutton);
     57 
     58 static void gtk_vscrollbutton_set_adjustment	(GtkVScrollbutton *scrollbutton,
     59 					     	 GtkAdjustment 	  *adjustment);
     60 
     61 static gint gtk_vscrollbutton_button_press	(GtkWidget 	  *widget,
     62 					   	 GdkEventButton   *event,
     63 					   	 GtkVScrollbutton *scrollbutton);
     64 
     65 static gint gtk_vscrollbutton_button_release	(GtkWidget 	  *widget,
     66 					     	 GdkEventButton   *event,
     67 					     	 GtkVScrollbutton *scrollbutton);
     68 
     69 gint gtk_vscrollbutton_scroll		(GtkVScrollbutton *scrollbutton);
     70 
     71 static gboolean gtk_vscrollbutton_timer_1st_time(GtkVScrollbutton *scrollbutton);
     72 
     73 static void gtk_vscrollbutton_add_timer		(GtkVScrollbutton *scrollbutton);
     74 
     75 static void gtk_vscrollbutton_remove_timer	(GtkVScrollbutton *scrollbutton);
     76 
     77 static gboolean gtk_real_vscrollbutton_timer	(GtkVScrollbutton *scrollbutton);
     78 
     79 static void gtk_vscrollbutton_set_sensitivity   (GtkAdjustment    *adjustment,
     80 						 GtkVScrollbutton *scrollbutton);
     81 
     82 GType gtk_vscrollbutton_get_type(void)
     83 {
     84     static GType vscrollbutton_type = 0;
     85 
     86     if (!vscrollbutton_type) {
     87 	static const GTypeInfo vscrollbutton_info = {
     88 			sizeof (GtkVScrollbuttonClass),
     89 
     90 			(GBaseInitFunc) NULL,
     91 			(GBaseFinalizeFunc) NULL,
     92 
     93 			(GClassInitFunc) gtk_vscrollbutton_class_init,
     94 			(GClassFinalizeFunc) NULL,
     95 			NULL,	/* class_data */
     96 
     97 			sizeof (GtkVScrollbutton),
     98 			0,	/* n_preallocs */
     99 			(GInstanceInitFunc) gtk_vscrollbutton_init,
    100 
    101 			(const GTypeValueTable *) NULL	/* value table */
    102 	};
    103 
    104 	vscrollbutton_type = g_type_register_static (GTK_TYPE_BOX, "GtkVScrollbutton", &vscrollbutton_info, (GTypeFlags)0);
    105     }
    106 
    107     return vscrollbutton_type;
    108 }
    109 
    110 static void gtk_vscrollbutton_class_init(GtkVScrollbuttonClass *class)
    111 {
    112 }
    113 
    114 static GdkCursor *hand_cursor = NULL;
    115 
    116 static gboolean vscroll_visi_notify(GtkWidget *widget,
    117 				       GdkEventVisibility *event,
    118 				       gpointer data)
    119 {
    120 	gdk_window_set_cursor(gtk_widget_get_window(widget), hand_cursor);
    121 	return FALSE;
    122 }
    123 
    124 static gboolean vscroll_leave_notify(GtkWidget *widget,
    125 				      GdkEventCrossing *event,
    126 				       gpointer data)
    127 {
    128 	gdk_window_set_cursor(gtk_widget_get_window(widget), NULL);
    129 	return FALSE;
    130 }
    131 
    132 static gboolean vscroll_enter_notify(GtkWidget *widget,
    133 				      GdkEventCrossing *event,
    134 				       gpointer data)
    135 {
    136 	gdk_window_set_cursor(gtk_widget_get_window(widget), hand_cursor);
    137 	return FALSE;
    138 }
    139 
    140 
    141 static void gtk_vscrollbutton_init(GtkVScrollbutton *scrollbutton)
    142 {
    143     GtkWidget *arrow;
    144 
    145     if (!hand_cursor)
    146 	    hand_cursor = gdk_cursor_new_for_display(
    147 					gtk_widget_get_display(GTK_WIDGET(scrollbutton)),GDK_HAND2);
    148 
    149     scrollbutton->upbutton = gtk_event_box_new();
    150     scrollbutton->downbutton = gtk_event_box_new();
    151     arrow = gtk_image_new_from_icon_name("pan-up-symbolic", GTK_ICON_SIZE_MENU);
    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_image_new_from_icon_name("pan-down-symbolic", GTK_ICON_SIZE_MENU);
    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_set_can_focus(scrollbutton->upbutton, FALSE);
    160     gtk_widget_set_can_focus(scrollbutton->downbutton, FALSE);
    161     gtk_widget_show(scrollbutton->downbutton);
    162     gtk_widget_show(scrollbutton->upbutton);
    163 
    164     g_signal_connect(G_OBJECT(scrollbutton->upbutton), "motion-notify-event",
    165 		     G_CALLBACK(vscroll_visi_notify), NULL);
    166     g_signal_connect(G_OBJECT(scrollbutton->upbutton), "leave-notify-event",
    167 		     G_CALLBACK(vscroll_leave_notify), NULL);
    168     g_signal_connect(G_OBJECT(scrollbutton->upbutton), "enter-notify-event",
    169 		     G_CALLBACK(vscroll_enter_notify), NULL);
    170     g_signal_connect(G_OBJECT(scrollbutton->downbutton), "motion-notify-event",
    171 		     G_CALLBACK(vscroll_visi_notify), NULL);
    172     g_signal_connect(G_OBJECT(scrollbutton->downbutton), "leave-notify-event",
    173 		     G_CALLBACK(vscroll_leave_notify), NULL);
    174     g_signal_connect(G_OBJECT(scrollbutton->downbutton), "enter-notify-event",
    175 		     G_CALLBACK(vscroll_enter_notify), NULL);
    176 
    177     g_signal_connect(G_OBJECT(scrollbutton->upbutton),
    178 		       "button_press_event",
    179 		       G_CALLBACK(gtk_vscrollbutton_button_press),
    180 		       scrollbutton);
    181     g_signal_connect(G_OBJECT(scrollbutton->downbutton),
    182 		       "button_press_event",
    183 		       G_CALLBACK(gtk_vscrollbutton_button_press),
    184 		       scrollbutton);
    185     g_signal_connect(G_OBJECT(scrollbutton->upbutton),
    186 		     "button_release_event",
    187 		     G_CALLBACK
    188 		     (gtk_vscrollbutton_button_release), scrollbutton);
    189     g_signal_connect(G_OBJECT(scrollbutton->downbutton),
    190 		     "button_release_event",
    191 		     G_CALLBACK
    192 		     (gtk_vscrollbutton_button_release), scrollbutton);
    193     gtk_box_pack_start(GTK_BOX(&scrollbutton->vbox),
    194 		       scrollbutton->upbutton, TRUE, TRUE, 0);
    195     gtk_box_pack_end(GTK_BOX(&scrollbutton->vbox),
    196 		     scrollbutton->downbutton, TRUE, TRUE, 0);
    197     scrollbutton->timer = 0;
    198 }
    199 
    200 GtkWidget *gtk_vscrollbutton_new(GtkAdjustment *adjustment)
    201 {
    202     GtkWidget *vscrollbutton;
    203     vscrollbutton = g_object_new (gtk_vscrollbutton_get_type(),
    204 			NULL);
    205     gtk_vscrollbutton_set_adjustment(GTK_VSCROLLBUTTON(vscrollbutton),
    206 				     adjustment);
    207     g_signal_connect(G_OBJECT(GTK_VSCROLLBUTTON(vscrollbutton)->adjustment),
    208 		       "value_changed",
    209 		       G_CALLBACK
    210 		       (gtk_vscrollbutton_set_sensitivity), vscrollbutton);
    211     g_signal_connect(G_OBJECT(GTK_VSCROLLBUTTON(vscrollbutton)->adjustment),
    212 		       "changed",
    213 		       G_CALLBACK
    214 		       (gtk_vscrollbutton_set_sensitivity), vscrollbutton);
    215     return vscrollbutton;
    216 }
    217 
    218 
    219 void gtk_vscrollbutton_set_adjustment(GtkVScrollbutton *scrollbutton,
    220 				      GtkAdjustment *adjustment)
    221 {
    222     cm_return_if_fail(scrollbutton != NULL);
    223     cm_return_if_fail(GTK_IS_VSCROLLBUTTON(scrollbutton));
    224 
    225     if (!adjustment)
    226 	    adjustment =
    227 	    GTK_ADJUSTMENT(gtk_adjustment_new(0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
    228     else
    229 	cm_return_if_fail(GTK_IS_ADJUSTMENT(adjustment));
    230 
    231     if (scrollbutton->adjustment != adjustment) {
    232 	if (scrollbutton->adjustment) {
    233 		g_signal_handlers_disconnect_matched(scrollbutton->adjustment, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, (gpointer) scrollbutton);
    234 		g_object_unref(G_OBJECT(scrollbutton->adjustment));
    235 	}
    236 
    237 	scrollbutton->adjustment = adjustment;
    238 	g_object_ref(G_OBJECT(adjustment));
    239 	g_object_ref_sink (G_OBJECT(adjustment));
    240     }
    241 }
    242 
    243 static gint gtk_vscrollbutton_button_press(GtkWidget *widget,
    244 					   GdkEventButton *event,
    245 					   GtkVScrollbutton *scrollbutton)
    246 {
    247     if (!gtk_widget_has_focus(widget))
    248 	gtk_widget_grab_focus(widget);
    249 
    250     if (scrollbutton->button == 0) {
    251 	gtk_grab_add(widget);
    252 	scrollbutton->button = event->button;
    253 
    254 	if (widget == scrollbutton->downbutton)
    255 	    scrollbutton->scroll_type = GTK_SCROLL_STEP_FORWARD;
    256 	else
    257 	    scrollbutton->scroll_type = GTK_SCROLL_STEP_BACKWARD;
    258 	gtk_vscrollbutton_scroll(scrollbutton);
    259 	gtk_vscrollbutton_add_timer(scrollbutton);
    260     }
    261     return TRUE;
    262 }
    263 
    264 
    265 static gint gtk_vscrollbutton_button_release(GtkWidget *widget,
    266 					     GdkEventButton *event,
    267 					     GtkVScrollbutton *scrollbutton)
    268 {
    269     if (!gtk_widget_has_focus(widget))
    270 	gtk_widget_grab_focus(widget);
    271 
    272     if (scrollbutton->button == event->button) {
    273 	gtk_grab_remove(widget);
    274 
    275 	scrollbutton->button = 0;
    276 	gtk_vscrollbutton_remove_timer(scrollbutton);
    277 	gtk_vscrollbutton_set_sensitivity(scrollbutton->adjustment, scrollbutton);
    278     }
    279     return TRUE;
    280 }
    281 
    282 gboolean gtk_vscrollbutton_scroll(GtkVScrollbutton *scrollbutton)
    283 {
    284     gfloat bound;
    285     gfloat new_value;
    286     gfloat page_size;
    287     gfloat value;
    288     gboolean return_val;
    289 
    290     cm_return_val_if_fail(scrollbutton != NULL, FALSE);
    291     cm_return_val_if_fail(GTK_IS_VSCROLLBUTTON(scrollbutton), FALSE);
    292 
    293     new_value = value = gtk_adjustment_get_value(scrollbutton->adjustment);
    294     return_val = TRUE;
    295 
    296     switch (scrollbutton->scroll_type) {
    297 
    298     case GTK_SCROLL_STEP_BACKWARD:
    299 	new_value = value - gtk_adjustment_get_step_increment(scrollbutton->adjustment);
    300 	bound = gtk_adjustment_get_lower(scrollbutton->adjustment);
    301 	if (new_value <= bound) {
    302 	    new_value = bound;
    303 	    return_val = FALSE;
    304 	    scrollbutton->timer = 0;
    305 	}
    306 	break;
    307 
    308     case GTK_SCROLL_STEP_FORWARD:
    309 	new_value = value + gtk_adjustment_get_step_increment(scrollbutton->adjustment);
    310 	bound = gtk_adjustment_get_upper(scrollbutton->adjustment);
    311 	page_size = gtk_adjustment_get_page_size(scrollbutton->adjustment);
    312 	if (new_value >= (bound - page_size)) {
    313 	    new_value = bound - page_size;
    314 	    return_val = FALSE;
    315 	    scrollbutton->timer = 0;
    316 	}
    317 	break;
    318 
    319     default:
    320 	break;
    321 
    322     }
    323 
    324 	if (new_value != value) {
    325 	gtk_adjustment_set_value(scrollbutton->adjustment, new_value);
    326 	g_signal_emit_by_name(G_OBJECT
    327 				(scrollbutton->adjustment),
    328 				"value_changed");
    329 	gtk_widget_queue_resize(GTK_WIDGET(scrollbutton)); /* ensure resize */
    330     }
    331 
    332     return return_val;
    333 }
    334 
    335 static gboolean
    336 gtk_vscrollbutton_timer_1st_time(GtkVScrollbutton *scrollbutton)
    337 {
    338     /*
    339      * If the real timeout function succeeds and the timeout is still set,
    340      * replace it with a quicker one so successive scrolling goes faster.
    341      */
    342     g_object_ref(G_OBJECT(scrollbutton));
    343     if (scrollbutton->timer) {
    344 	/* We explicitely remove ourselves here in the paranoia
    345 	 * that due to things happening above in the callback
    346 	 * above, we might have been removed, and another added.
    347 	 */
    348 	g_source_remove(scrollbutton->timer);
    349 	scrollbutton->timer = g_timeout_add(SCROLL_LATER_DELAY,
    350 					    (GSourceFunc)
    351 					    gtk_real_vscrollbutton_timer,
    352 					    scrollbutton);
    353     }
    354     g_object_unref(G_OBJECT(scrollbutton));
    355     return FALSE;		/* don't keep calling this function */
    356 }
    357 
    358 
    359 static void gtk_vscrollbutton_add_timer(GtkVScrollbutton *scrollbutton)
    360 {
    361     cm_return_if_fail(scrollbutton != NULL);
    362     cm_return_if_fail(GTK_IS_VSCROLLBUTTON(scrollbutton));
    363 
    364     if (!scrollbutton->timer) {
    365 	scrollbutton->need_timer = TRUE;
    366 	scrollbutton->timer = g_timeout_add(SCROLL_INITIAL_DELAY,
    367 					    (GSourceFunc)
    368 					    gtk_vscrollbutton_timer_1st_time,
    369 					    scrollbutton);
    370     }
    371 }
    372 
    373 static void gtk_vscrollbutton_remove_timer(GtkVScrollbutton *scrollbutton)
    374 {
    375     cm_return_if_fail(scrollbutton != NULL);
    376     cm_return_if_fail(GTK_IS_VSCROLLBUTTON(scrollbutton));
    377 
    378     if (scrollbutton->timer) {
    379 	g_source_remove(scrollbutton->timer);
    380 	scrollbutton->timer = 0;
    381     }
    382     scrollbutton->need_timer = FALSE;
    383 }
    384 
    385 static gboolean gtk_real_vscrollbutton_timer(GtkVScrollbutton *scrollbutton)
    386 {
    387     gboolean return_val;
    388 
    389     GDK_THREADS_ENTER();
    390 
    391     return_val = TRUE;
    392     if (!scrollbutton->timer) {
    393 	return_val = FALSE;
    394 	if (scrollbutton->need_timer)
    395 	    scrollbutton->timer =
    396 		g_timeout_add(SCROLL_TIMER_LENGTH,
    397 			      (GSourceFunc) gtk_real_vscrollbutton_timer,
    398 			      (gpointer) scrollbutton);
    399 	else {
    400 	    GDK_THREADS_LEAVE();
    401 	    return FALSE;
    402 	}
    403 	scrollbutton->need_timer = FALSE;
    404     }
    405     GDK_THREADS_LEAVE();
    406     return_val = gtk_vscrollbutton_scroll(scrollbutton);
    407     return return_val;
    408 }
    409 
    410 static void gtk_vscrollbutton_set_sensitivity   (GtkAdjustment    *adjustment,
    411 						 GtkVScrollbutton *scrollbutton)
    412 {
    413 	gfloat value;
    414 	if (!gtk_widget_get_realized(GTK_WIDGET(scrollbutton))) return;
    415 	if (scrollbutton->button != 0) return; /* not while something is pressed */
    416 
    417 	value = gtk_adjustment_get_value(adjustment);
    418 	gtk_widget_set_sensitive(scrollbutton->upbutton,
    419 				 (value > gtk_adjustment_get_lower(adjustment)));
    420 	gtk_widget_set_sensitive(scrollbutton->downbutton,
    421 				 (value < (gtk_adjustment_get_upper(adjustment) -
    422                            gtk_adjustment_get_page_size(adjustment))));
    423 }