Logo Search packages:      
Sourcecode: kazehakase version File versions

kz-autoscroller.c

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */

/*
 *  Copyright (C) 2004  Hidetaka Iwai
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2, or (at your option)
 *  any later version.
 *
 *  This program 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 General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

/*
 * The original code is  src/galeon-embed-autoscroller.c in galeon-1.3.18
 *  Copyright (C) 2002  Ricardo Fernández Pascual
 */


#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "kz-autoscroller.h"

#include <gtk/gtkimage.h>
#include <gtk/gtkwindow.h>
#include <gtk/gtkmain.h>
#include <stdlib.h>
#include <math.h>
#include <sys/time.h>
#include <unistd.h>

#include "gobject-utils.h"

typedef struct _KzAutoscrollerPriv KzAutoscrollerPriv;
struct _KzAutoscrollerPriv {
      KzEmbed *embed;
      GtkWidget *widget;
      guint start_x;
      guint start_y;
      gfloat step_x;
      gfloat step_y;
      gfloat roundoff_error_x;
      gfloat roundoff_error_y;
      gint msecs;
      guint timeout_id;
      gboolean active;
};

#define KZ_AUTOSCROLLER_GET_PRIVATE(object) (G_TYPE_INSTANCE_GET_PRIVATE ((object), KZ_TYPE_AUTOSCROLLER, KzAutoscrollerPriv))

static void     kz_autoscroller_class_init     (KzAutoscrollerClass *klass);
static void     kz_autoscroller_init           (KzAutoscroller *autoscroller);
static void     kz_autoscroller_dispose        (GObject *object);

static gboolean kz_autoscroller_motion_cb      (GtkWidget *widget,
                                    GdkEventMotion *e,
                                    KzAutoscroller *as);
static gboolean kz_autoscroller_mouse_press_cb (GtkWidget *widget,
                                    GdkEventButton *e,
                                    KzAutoscroller *as);
static gboolean kz_autoscroller_key_press_cb   (GtkWidget *widget,
                                    GdkEventKey *e,
                                    KzAutoscroller *as);
static gint     kz_autoscroller_timeout_cb     (gpointer data);
static void     kz_autoscroller_stop           (KzAutoscroller *as);

static GObjectClass *parent_class = NULL;
static GtkWidget *autoscroll_icon = NULL;

KZ_OBJECT_GET_TYPE(kz_autoscroller, "KzAutoscroller", KzAutoscroller,
               kz_autoscroller_class_init, kz_autoscroller_init,
               G_TYPE_OBJECT)

static void
kz_autoscroller_class_init (KzAutoscrollerClass *klass)
{
      GObjectClass *object_class;
      GdkPixbuf *icon_pixbuf = NULL;
      GdkPixmap *icon_pixmap = NULL;
      GdkBitmap *icon_bitmap = NULL;
      GtkWidget *icon_img;

      parent_class = g_type_class_peek_parent (klass);
      object_class = (GObjectClass *) klass;

      object_class->dispose = kz_autoscroller_dispose;

      /* initialize the autoscroll icon */

      icon_pixbuf = gdk_pixbuf_new_from_file (KZ_DATADIR "/icons/autoscroll.xpm", NULL);
      if(icon_pixbuf)
      {
            gdk_pixbuf_render_pixmap_and_mask (icon_pixbuf, &icon_pixmap,
                                 &icon_bitmap, 128);
            g_object_unref (icon_pixbuf);

      }
      else
      {
            g_warning("Fail to load an autoscroll icon\n");
      }

      /*
        gtk_widget_push_visual (gdk_rgb_get_visual ());
        gtk_widget_push_colormap (gdk_rgb_get_cmap ());
      */
      if(icon_pixmap && icon_bitmap)
      {
            icon_img = gtk_image_new_from_pixmap (icon_pixmap, icon_bitmap);

            autoscroll_icon = gtk_window_new (GTK_WINDOW_POPUP);
            gtk_widget_realize (autoscroll_icon);
            gtk_container_add (GTK_CONTAINER (autoscroll_icon), icon_img);
            gtk_widget_shape_combine_mask (autoscroll_icon, icon_bitmap, 0, 0);

      /*
        gtk_widget_pop_visual ();
        gtk_widget_pop_colormap ();
      */
            
            g_object_unref (icon_pixmap);
            g_object_unref (icon_bitmap);

            gtk_widget_show_all (icon_img);
      }
      else
      {
            g_warning("Fail to make an autoscroll cursor\n");
      }
      g_type_class_add_private (klass, sizeof(KzAutoscrollerPriv));
}

static void 
kz_autoscroller_init (KzAutoscroller *as)
{
      KzAutoscrollerPriv *priv = KZ_AUTOSCROLLER_GET_PRIVATE (as);

      priv->embed  = NULL;
      priv->widget = NULL;
      priv->active = FALSE;
      priv->msecs  = 33;
}

static void
kz_autoscroller_dispose (GObject *object)
{
      KzAutoscroller *as = KZ_AUTOSCROLLER (object);
      KzAutoscrollerPriv *priv = KZ_AUTOSCROLLER_GET_PRIVATE (as);

      if (priv->embed)
      {
            g_object_unref (priv->embed);
            priv->embed = NULL;
      }

      if (priv->timeout_id)
      {
            g_source_remove (priv->timeout_id);
            priv->timeout_id = 0;
      }

      if (G_OBJECT_CLASS (parent_class)->dispose)
            G_OBJECT_CLASS (parent_class)->dispose(object);
}

KzAutoscroller *
kz_autoscroller_new (void)
{
      KzAutoscroller *as = g_object_new (KZ_TYPE_AUTOSCROLLER, NULL);
      return as;
}

void
kz_autoscroller_set_embed (KzAutoscroller *as,
                     KzEmbed *embed)
{
      KzAutoscrollerPriv *priv = KZ_AUTOSCROLLER_GET_PRIVATE (as);

      if (priv->embed)
      {
            g_object_unref (priv->embed);
      }
      
      priv->embed = g_object_ref (embed);
}

void
kz_autoscroller_start_scroll (KzAutoscroller *as,
                        GtkWidget *widget, gint x, gint y)
{
      static GdkCursor *cursor = NULL;
      KzAutoscrollerPriv *priv = KZ_AUTOSCROLLER_GET_PRIVATE (as);

      g_return_if_fail (priv->embed);
      g_return_if_fail (autoscroll_icon);
      
      if (priv->active)
      {
            return;
      }
      priv->active = TRUE;
      
      g_object_ref (as);

      priv->widget = g_object_ref (widget);

      /* get a new cursor, if necessary */
      if (!cursor) cursor = gdk_cursor_new (GDK_FLEUR);

      /* show icon */
      gtk_window_move (GTK_WINDOW (autoscroll_icon), x - 12, y - 12);
      gtk_widget_show (autoscroll_icon);

      /* set positions */
      priv->start_x = x;
      priv->start_y = y;
      priv->step_x = 0;
      priv->step_y = 0;
      priv->roundoff_error_x = 0;
      priv->roundoff_error_y = 0;

      /* attach signals */
      g_signal_connect (widget, "motion_notify_event",
                      G_CALLBACK (kz_autoscroller_motion_cb), as);
      g_signal_connect (widget, "button_press_event",
                    G_CALLBACK (kz_autoscroller_mouse_press_cb), as);
      g_signal_connect (widget, "key_press_event",
                    G_CALLBACK (kz_autoscroller_key_press_cb), as);
      priv->timeout_id =
            g_timeout_add (priv->msecs,
                         kz_autoscroller_timeout_cb, as);

      /* grab the pointer */
      gtk_grab_add (widget);
      gdk_pointer_grab (widget->window, FALSE,
                    GDK_POINTER_MOTION_MASK |
                    GDK_BUTTON_PRESS_MASK,
                    NULL, cursor, GDK_CURRENT_TIME);
      gdk_keyboard_grab (widget->window, FALSE, GDK_CURRENT_TIME);
}

static gboolean
kz_autoscroller_motion_cb (GtkWidget *widget, GdkEventMotion *e,
                     KzAutoscroller *as)
{
      KzAutoscrollerPriv *priv = KZ_AUTOSCROLLER_GET_PRIVATE (as);
      gint x_dist, x_dist_abs, y_dist, y_dist_abs;

      if (!priv->active)
      {
            return FALSE;
      }

      /* get distance between scroll center and cursor */
      x_dist = e->x_root - priv->start_x;
      x_dist_abs = abs (x_dist);
      y_dist = e->y_root - priv->start_y;
      y_dist_abs = abs (y_dist);

      /* calculate scroll step */
      if (y_dist_abs <= 48.0)
      {
            priv->step_y = (float) (y_dist / 4) / 6.0;
      }
      else if (y_dist > 0)
      {
            priv->step_y = (y_dist - 48.0) / 2.0 + 2.0;
      }
      else 
      {
            priv->step_y = (y_dist + 48.0) / 2.0 - 2.0;
      }

      if (x_dist_abs <= 48.0)
      {
            priv->step_x = (float) (x_dist / 4) / 6.0;
      }
      else if (x_dist > 0)
      {
            priv->step_x = (x_dist - 48.0) / 2.0 + 2.0;
      }
      else 
      {
            priv->step_x = (x_dist + 48.0) / 2.0 - 2.0;
      }

      return TRUE;
}

static gboolean
kz_autoscroller_mouse_press_cb (GtkWidget *widget, GdkEventButton *e,
                        KzAutoscroller *as)
{
        /* ungrab and disconnect */
      kz_autoscroller_stop (as);

      return TRUE;
}

static gboolean
kz_autoscroller_key_press_cb (GtkWidget *widget, GdkEventKey *e,
                        KzAutoscroller *as)
{
      /* ungrab and disconnect */
      kz_autoscroller_stop (as);

      return TRUE;
}

static gint
kz_autoscroller_timeout_cb (gpointer data)
{
      struct timeval start_time, finish_time;
      long elapsed_msecs;
      KzAutoscroller *as = data;
      KzAutoscrollerPriv *priv = KZ_AUTOSCROLLER_GET_PRIVATE (as);
      gfloat scroll_step_y_adj;
      gint scroll_step_y_int;
      gfloat scroll_step_x_adj;
      gint scroll_step_x_int;

      g_return_val_if_fail (KZ_IS_AUTOSCROLLER (as), FALSE);
      g_return_val_if_fail (KZ_IS_EMBED (priv->embed), FALSE);

        /* return if we're not supposed to scroll */
      if (!priv->step_y && !priv->step_x)
      {
            return TRUE;
      }

      /* calculate the number of pixels to scroll */
      scroll_step_y_adj = priv->step_y * priv->msecs / 33;
      scroll_step_y_int = scroll_step_y_adj;
      priv->roundoff_error_y += (scroll_step_y_adj - scroll_step_y_int);

      if (fabs (priv->roundoff_error_y) >= 1.0)
      {
            scroll_step_y_int += priv->roundoff_error_y;
            priv->roundoff_error_y -= (gint) priv->roundoff_error_y;
      }

      scroll_step_x_adj = priv->step_x * priv->msecs / 33;
      scroll_step_x_int = scroll_step_x_adj;
      priv->roundoff_error_x += (scroll_step_x_adj - scroll_step_x_int);

      if (fabs (priv->roundoff_error_x) >= 1.0)
      {
            scroll_step_x_int += priv->roundoff_error_x;
            priv->roundoff_error_x -= (gint) priv->roundoff_error_x;
      }

      /* exit if we're not supposed to scroll yet */
      if (!scroll_step_y_int && !scroll_step_x_int) return TRUE;
      
      /* get the time before we tell the embed to scroll */
      gettimeofday (&start_time, NULL);

      /* do scrolling, moving at a constart speed regardless of the
       * scrolling delay */

      /* FIXME: if mozilla is able to do diagonal scrolling in a
       * reasonable manner at some point, this should be changed to
       * calculate x and pass both values instead of just y */
      kz_embed_fine_scroll (priv->embed, scroll_step_x_int, scroll_step_y_int);

      /* find out how long the scroll took */
      gettimeofday (&finish_time, NULL);
      elapsed_msecs = (1000000L * finish_time.tv_sec + finish_time.tv_usec -
                   1000000L * start_time.tv_sec - start_time.tv_usec) /
                  1000;

      /* check if we should update the scroll delay */
      if ((elapsed_msecs >= priv->msecs + 5) ||
          ((priv->msecs > 20) && (elapsed_msecs < priv->msecs - 10)))
      {
            /* update the scrolling delay, with a
             * minimum delay of 20 ms */
            priv->msecs = (elapsed_msecs + 10 >= 20) ? elapsed_msecs + 10 : 20;

            /* create new timeout with adjusted delay */
            priv->timeout_id = g_timeout_add (priv->msecs, kz_autoscroller_timeout_cb, as);

            /* kill the old timeout */
            return FALSE;
      }

      /* don't kill timeout */
      return TRUE;
}

static void
kz_autoscroller_stop (KzAutoscroller *as)
{
      KzAutoscrollerPriv *priv = KZ_AUTOSCROLLER_GET_PRIVATE (as);

      g_return_if_fail (autoscroll_icon);

      /* ungrab the pointer if it's grabbed */
      if (gdk_pointer_is_grabbed ())
      {
            gdk_pointer_ungrab (GDK_CURRENT_TIME);
      }

      gdk_keyboard_ungrab (GDK_CURRENT_TIME);

      /* hide the icon */
      gtk_widget_hide (autoscroll_icon);

      g_return_if_fail (priv->widget);

      gtk_grab_remove (priv->widget);

      /* disconnect all of the signals */
      g_signal_handlers_disconnect_matched (priv->widget, G_SIGNAL_MATCH_DATA, 0, 0, 
                                    NULL, NULL, as);
      if (priv->timeout_id)
      {
            g_source_remove (priv->timeout_id);
            priv->timeout_id = 0;
      }

      g_object_unref (priv->widget);
      priv->widget = NULL;

      priv->active = FALSE;
      g_object_unref (as);
}


Generated by  Doxygen 1.6.0   Back to index