claws-mail/src/gtk/gtkvscrollbutton.c
2004-05-25 08:39:41 +00:00

379 lines
12 KiB
C

/* GTK - The GIMP Toolkit
* Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
/*
* Modified by the GTK+ Team and others 1997-1999. See the AUTHORS
* file for a list of people on the GTK+ Team. See the ChangeLog
* files for a list of changes. These files are distributed with
* GTK+ at ftp://ftp.gtk.org/pub/gtk/.
*/
/*
* Simple composite widget to provide vertical scrolling, based
* on GtkRange widget code.
* Modified by the Sylpheed Team and others 2003
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <gtk/gtksignal.h>
#include <gtk/gtkbutton.h>
#include <gtk/gtkmisc.h>
#include <gtk/gtk.h>
#include <gtk/gtkbox.h>
#include <gtk/gtkmain.h>
#include "gtkvscrollbutton.h"
#define SCROLL_TIMER_LENGTH 20
#define SCROLL_INITIAL_DELAY 100 /* must hold button this long before ... */
#define SCROLL_LATER_DELAY 20 /* ... it starts repeating at this rate */
#define SCROLL_DELAY_LENGTH 300
enum {
ARG_0,
ARG_ADJUSTMENT
};
static void gtk_vscrollbutton_class_init(GtkVScrollbuttonClass * klass);
static void gtk_vscrollbutton_init(GtkVScrollbutton * vscrollbutton);
GtkType gtk_vscrollbutton_get_type (void);
GtkWidget *gtk_vscrollbutton_new (GtkAdjustment *adjustment);
static gint gtk_vscrollbutton_button_release (GtkWidget *widget,
GdkEventButton *event,
GtkVScrollbutton *scrollbutton);
static void gtk_vscrollbutton_set_adjustment (GtkVScrollbutton *scrollbutton,
GtkAdjustment *adjustment);
static gint gtk_vscrollbutton_button_press (GtkWidget *widget,
GdkEventButton *event,
GtkVScrollbutton *scrollbutton);
static gint gtk_vscrollbutton_button_release (GtkWidget *widget,
GdkEventButton *event,
GtkVScrollbutton *scrollbutton);
static gint gtk_vscrollbutton_scroll (GtkVScrollbutton *scrollbutton);
static gboolean gtk_vscrollbutton_timer_1st_time(GtkVScrollbutton *scrollbutton);
static void gtk_vscrollbutton_add_timer (GtkVScrollbutton *scrollbutton);
static void gtk_vscrollbutton_remove_timer (GtkVScrollbutton *scrollbutton);
static gint gtk_real_vscrollbutton_timer (GtkVScrollbutton *scrollbutton);
static void gtk_vscrollbutton_set_sensitivity (GtkAdjustment *adjustment,
GtkVScrollbutton *scrollbutton);
GtkType gtk_vscrollbutton_get_type(void)
{
static GtkType vscrollbutton_type = 0;
if (!vscrollbutton_type) {
static const GtkTypeInfo vscrollbutton_info = {
"GtkVScrollbutton",
sizeof(GtkVScrollbutton),
sizeof(GtkVScrollbuttonClass),
(GtkClassInitFunc) gtk_vscrollbutton_class_init,
(GtkObjectInitFunc) gtk_vscrollbutton_init,
/* reserved_1 */ NULL,
/* reserved_2 */ NULL,
(GtkClassInitFunc) NULL,
};
vscrollbutton_type =
gtk_type_unique(GTK_TYPE_VBOX, &vscrollbutton_info);
}
return vscrollbutton_type;
}
static void gtk_vscrollbutton_class_init(GtkVScrollbuttonClass *class)
{
}
static void gtk_vscrollbutton_init(GtkVScrollbutton *scrollbutton)
{
GtkWidget *arrow;
scrollbutton->upbutton = gtk_button_new();
scrollbutton->downbutton = gtk_button_new();
arrow = gtk_arrow_new(GTK_ARROW_UP, GTK_SHADOW_NONE);
gtk_widget_show(arrow);
gtk_container_add(GTK_CONTAINER(scrollbutton->upbutton), arrow);
gtk_widget_set_usize(scrollbutton->upbutton, -1, 16);
arrow = gtk_arrow_new(GTK_ARROW_DOWN, GTK_SHADOW_NONE);
gtk_widget_show(arrow);
gtk_container_add(GTK_CONTAINER(scrollbutton->downbutton), arrow);
gtk_widget_set_usize(scrollbutton->downbutton, -1, 16);
GTK_WIDGET_UNSET_FLAGS(scrollbutton->upbutton, GTK_CAN_FOCUS);
GTK_WIDGET_UNSET_FLAGS(scrollbutton->downbutton, GTK_CAN_FOCUS);
gtk_widget_show(scrollbutton->downbutton);
gtk_widget_show(scrollbutton->upbutton);
g_signal_connect(G_OBJECT(scrollbutton->upbutton),
"button_press_event",
G_CALLBACK(gtk_vscrollbutton_button_press),
scrollbutton);
g_signal_connect(G_OBJECT(scrollbutton->downbutton),
"button_press_event",
G_CALLBACK(gtk_vscrollbutton_button_press),
scrollbutton);
g_signal_connect(G_OBJECT(scrollbutton->upbutton),
"button_release_event",
G_CALLBACK
(gtk_vscrollbutton_button_release), scrollbutton);
g_signal_connect(G_OBJECT(scrollbutton->downbutton),
"button_release_event",
G_CALLBACK
(gtk_vscrollbutton_button_release), scrollbutton);
gtk_box_pack_start(GTK_BOX(&scrollbutton->vbox),
scrollbutton->upbutton, TRUE, TRUE, 0);
gtk_box_pack_end(GTK_BOX(&scrollbutton->vbox),
scrollbutton->downbutton, TRUE, TRUE, 0);
scrollbutton->timer = 0;
}
GtkWidget *gtk_vscrollbutton_new(GtkAdjustment *adjustment)
{
GtkWidget *vscrollbutton;
vscrollbutton = GTK_WIDGET(gtk_type_new(gtk_vscrollbutton_get_type()));
gtk_vscrollbutton_set_adjustment(GTK_VSCROLLBUTTON(vscrollbutton),
adjustment);
g_signal_connect(G_OBJECT(GTK_VSCROLLBUTTON(vscrollbutton)->adjustment),
"value_changed",
G_CALLBACK
(gtk_vscrollbutton_set_sensitivity), vscrollbutton);
g_signal_connect(G_OBJECT(GTK_VSCROLLBUTTON(vscrollbutton)->adjustment),
"changed",
G_CALLBACK
(gtk_vscrollbutton_set_sensitivity), vscrollbutton);
return vscrollbutton;
}
void gtk_vscrollbutton_set_adjustment(GtkVScrollbutton *scrollbutton,
GtkAdjustment *adjustment)
{
g_return_if_fail(scrollbutton != NULL);
g_return_if_fail(GTK_IS_VSCROLLBUTTON(scrollbutton));
if (!adjustment)
adjustment =
GTK_ADJUSTMENT(gtk_adjustment_new(0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
else
g_return_if_fail(GTK_IS_ADJUSTMENT(adjustment));
if (scrollbutton->adjustment != adjustment) {
if (scrollbutton->adjustment) {
g_signal_handlers_disconnect_matched(scrollbutton->adjustment,
G_SIGNAL_MATCH_DATA,
0, 0, NULL, NULL,
(gpointer) scrollbutton);
g_object_unref(G_OBJECT(scrollbutton->adjustment));
}
scrollbutton->adjustment = adjustment;
g_object_ref(G_OBJECT(adjustment));
gtk_object_sink(GTK_OBJECT(adjustment));
}
}
static gint gtk_vscrollbutton_button_press(GtkWidget *widget,
GdkEventButton *event,
GtkVScrollbutton *scrollbutton)
{
if (!GTK_WIDGET_HAS_FOCUS(widget))
gtk_widget_grab_focus(widget);
if (scrollbutton->button == 0) {
gtk_grab_add(widget);
scrollbutton->button = event->button;
if (widget == scrollbutton->downbutton)
scrollbutton->scroll_type = GTK_SCROLL_STEP_FORWARD;
else
scrollbutton->scroll_type = GTK_SCROLL_STEP_BACKWARD;
gtk_vscrollbutton_scroll(scrollbutton);
gtk_vscrollbutton_add_timer(scrollbutton);
}
return TRUE;
}
static gint gtk_vscrollbutton_button_release(GtkWidget *widget,
GdkEventButton *event,
GtkVScrollbutton *scrollbutton)
{
if (!GTK_WIDGET_HAS_FOCUS(widget))
gtk_widget_grab_focus(widget);
if (scrollbutton->button == event->button) {
gtk_grab_remove(widget);
scrollbutton->button = 0;
gtk_vscrollbutton_remove_timer(scrollbutton);
gtk_vscrollbutton_set_sensitivity(scrollbutton->adjustment, scrollbutton);
}
return TRUE;
}
static gint gtk_vscrollbutton_scroll(GtkVScrollbutton *scrollbutton)
{
gfloat new_value;
gint return_val;
g_return_val_if_fail(scrollbutton != NULL, FALSE);
g_return_val_if_fail(GTK_IS_VSCROLLBUTTON(scrollbutton), FALSE);
new_value = scrollbutton->adjustment->value;
return_val = TRUE;
switch (scrollbutton->scroll_type) {
case GTK_SCROLL_STEP_BACKWARD:
new_value -= scrollbutton->adjustment->step_increment;
if (new_value <= scrollbutton->adjustment->lower) {
new_value = scrollbutton->adjustment->lower;
return_val = FALSE;
scrollbutton->timer = 0;
}
break;
case GTK_SCROLL_STEP_FORWARD:
new_value += scrollbutton->adjustment->step_increment;
if (new_value >=
(scrollbutton->adjustment->upper -
scrollbutton->adjustment->page_size)) {
new_value =
scrollbutton->adjustment->upper -
scrollbutton->adjustment->page_size;
return_val = FALSE;
scrollbutton->timer = 0;
}
break;
default:
break;
}
if (new_value != scrollbutton->adjustment->value) {
scrollbutton->adjustment->value = new_value;
g_signal_emit_by_name(G_OBJECT
(scrollbutton->adjustment),
"value_changed");
gtk_widget_queue_resize(GTK_WIDGET(scrollbutton)); /* ensure resize */
}
return return_val;
}
static gboolean
gtk_vscrollbutton_timer_1st_time(GtkVScrollbutton *scrollbutton)
{
/*
* If the real timeout function succeeds and the timeout is still set,
* replace it with a quicker one so successive scrolling goes faster.
*/
g_object_ref(G_OBJECT(scrollbutton));
if (scrollbutton->timer) {
/* We explicitely remove ourselves here in the paranoia
* that due to things happening above in the callback
* above, we might have been removed, and another added.
*/
g_source_remove(scrollbutton->timer);
scrollbutton->timer = gtk_timeout_add(SCROLL_LATER_DELAY,
(GtkFunction)
gtk_real_vscrollbutton_timer,
scrollbutton);
}
g_object_unref(G_OBJECT(scrollbutton));
return FALSE; /* don't keep calling this function */
}
static void gtk_vscrollbutton_add_timer(GtkVScrollbutton *scrollbutton)
{
g_return_if_fail(scrollbutton != NULL);
g_return_if_fail(GTK_IS_VSCROLLBUTTON(scrollbutton));
if (!scrollbutton->timer) {
scrollbutton->need_timer = TRUE;
scrollbutton->timer = gtk_timeout_add(SCROLL_INITIAL_DELAY,
(GtkFunction)
gtk_vscrollbutton_timer_1st_time,
scrollbutton);
}
}
static void gtk_vscrollbutton_remove_timer(GtkVScrollbutton *scrollbutton)
{
g_return_if_fail(scrollbutton != NULL);
g_return_if_fail(GTK_IS_VSCROLLBUTTON(scrollbutton));
if (scrollbutton->timer) {
gtk_timeout_remove(scrollbutton->timer);
scrollbutton->timer = 0;
}
scrollbutton->need_timer = FALSE;
}
static gint gtk_real_vscrollbutton_timer(GtkVScrollbutton *scrollbutton)
{
gint return_val;
GDK_THREADS_ENTER();
return_val = TRUE;
if (!scrollbutton->timer) {
return_val = FALSE;
if (scrollbutton->need_timer)
scrollbutton->timer =
gtk_timeout_add(SCROLL_TIMER_LENGTH,
(GtkFunction) gtk_real_vscrollbutton_timer,
(gpointer) scrollbutton);
else {
GDK_THREADS_LEAVE();
return FALSE;
}
scrollbutton->need_timer = FALSE;
}
GDK_THREADS_LEAVE();
return_val = gtk_vscrollbutton_scroll(scrollbutton);
return return_val;
}
static void gtk_vscrollbutton_set_sensitivity (GtkAdjustment *adjustment,
GtkVScrollbutton *scrollbutton)
{
if (!GTK_WIDGET_REALIZED(GTK_WIDGET(scrollbutton))) return;
if (scrollbutton->button != 0) return; /* not while something is pressed */
gtk_widget_set_sensitive(scrollbutton->upbutton,
(adjustment->value > adjustment->lower));
gtk_widget_set_sensitive(scrollbutton->downbutton,
(adjustment->value < (adjustment->upper - adjustment->page_size)));
}