Logo Search packages:      
Sourcecode: kazehakase version File versions

kz-popup-preview.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 gtk2/bbs_thread_ui.c in ochusha-0.5.7
 * Copyright (c) 2003-2004 The Ochusha Project.
 */

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

#include <string.h>

#include "kazehakase.h"
#include "kz-popup-preview.h"
#include "kz-downloader.h"
#include "kz-profile.h"
#include "kz-thumbnail.h"
#include "gobject-utils.h"
#include "glib-utils.h"
#include "utils.h"
#include "eggmd5.h"
#include "egg-pixbuf-thumbnail.h"

typedef struct _KzPopupPreviewPriv KzPopupPreviewPriv;
struct _KzPopupPreviewPriv
{
      GtkWidget *popup_window;
      GtkContainer *popup_frame;
      GtkWidget *image;

      gchar *uri;
      guint delay_id;
      guint close_delay_id;
      int x;
      int y;

      gboolean now_shown;
      gboolean now_pointed;
};

#define KZ_POPUP_PREVIEW_GET_PRIVATE(object) (G_TYPE_INSTANCE_GET_PRIVATE ((object), KZ_TYPE_POPUP_PREVIEW, KzPopupPreviewPriv))

#define DEFAULT_WIDTH 160
#define DEFAULT_HEIGHT 120
#define X_OFFSET 10
#define Y_OFFSET 10
#define DEFAULT_POPUP_RESPONSE_DELAY 100
#define DEFAULT_POPUP_CLOSE_DELAY 100

static void       kz_popup_preview_class_init             (KzPopupPreviewClass *klass);
static void       kz_popup_preview_init                   (KzPopupPreview *popup);
static void       kz_popup_preview_dispose                (GObject *object);

static gboolean   cb_enter_notify_event                   (GtkWidget *widget,
                                             GdkEventCrossing *event,
                                             KzPopupPreview* popup);
static gboolean   cb_leave_notify_event                   (GtkWidget *widget,
                                             GdkEventCrossing *event,
                                             KzPopupPreview *popup);
static gboolean   cb_button_press                         (GtkWidget *widget,
                                             GdkEventButton *event,
                                             KzPopupPreview *popup);

static void       kz_popup_preview_setup_popup            (KzPopupPreview* popup, 
                                             GtkWidget* image);
static void       kz_popup_preview_show_popup             (KzPopupPreview* popup);
static void       kz_popup_preview_show_popup_real        (KzPopupPreview* popup);
static gboolean   cb_delay_timeout                        (KzPopupPreview* popup);
static void       kz_popup_preview_hide_popup             (KzPopupPreview* popup);
static void       kz_popup_preview_hide_popup_real        (KzPopupPreview *popup);
static gboolean   cb_close_delay_timeout                  (KzPopupPreview *popup);

static GtkWidget* kz_popup_preview_get_image              (KzPopupPreview *popup,
                                             const gchar *uri);
static GtkWidget* kz_popup_preview_get_thumbnail          (KzPopupPreview *popup,
                                             const gchar *uri);

static void       kz_downloader_disconnect_signal         (KzDownloader *dl,
                                             const gchar *uri);
static gboolean   idle_unref_dl                           (gpointer data);
static void       cb_downloader_load_complete             (KzDownloader *dl,
                                             const gchar *uri);
static void       cb_downloader_load_error                (KzDownloader *dl,
                                             const gchar *uri);

static GdkPixbuf* gdk_pixbuf_scale_keeping_aspect_ratio   (const GdkPixbuf *src,
                                             int              max_width,
                                             int              max_height,
                                             GdkInterpType    interp_type);

static GObjectClass *parent_class = NULL;
static KzPopupPreview      *kz_popup_preview_single = NULL;

KZ_OBJECT_GET_TYPE(kz_popup_preview, "KzPopupPreview", KzPopupPreview,
               kz_popup_preview_class_init, kz_popup_preview_init,
               G_TYPE_OBJECT)

static void
kz_popup_preview_class_init (KzPopupPreviewClass *klass)
{
      GObjectClass *object_class;

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

      object_class->dispose = kz_popup_preview_dispose;

      g_type_class_add_private (klass, sizeof(KzPopupPreviewPriv));
}

static void
kz_popup_preview_init (KzPopupPreview *popup)
{
      KzPopupPreviewPriv *priv = KZ_POPUP_PREVIEW_GET_PRIVATE (popup);
      GtkWidget *frame;

      priv->popup_window = gtk_window_new(GTK_WINDOW_POPUP);

      frame = gtk_frame_new(NULL);
      gtk_widget_show(frame);
      priv->popup_frame = GTK_CONTAINER(frame);
      gtk_container_add(GTK_CONTAINER(priv->popup_window), frame);

      priv->image = NULL;

      priv->uri = NULL;
      priv->delay_id = 0;
      priv->close_delay_id = 0;
      priv->x = 0;
      priv->y = 0;

      priv->now_shown = FALSE;
      priv->now_pointed = FALSE;
}

static void
kz_popup_preview_dispose (GObject *object)
{
      KzPopupPreview *popup = KZ_POPUP_PREVIEW (object);
      KzPopupPreviewPriv *priv = KZ_POPUP_PREVIEW_GET_PRIVATE (popup);

      if (priv->uri != NULL)
      {
            g_free(priv->uri);
            priv->uri = NULL;
      }

      if (priv->delay_id != 0)
      {
            g_source_remove(priv->delay_id);
            priv->delay_id = 0;
      }
      
      if (priv->close_delay_id != 0)
      {
            g_source_remove(priv->close_delay_id);
            priv->close_delay_id = 0;
      }

      if (priv->popup_window != NULL)
      {
            gtk_widget_destroy(priv->popup_window);
            priv->popup_window = NULL;
      }

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

KzPopupPreview *
kz_popup_preview_new (void)
{
      KzPopupPreview *popup = g_object_new (KZ_TYPE_POPUP_PREVIEW, NULL);
      return popup;
}

KzPopupPreview *
kz_popup_preview_get_instance (void)
{
      if (!kz_popup_preview_single)
            kz_popup_preview_single = kz_popup_preview_new();
      else
            g_object_ref(kz_popup_preview_single);

      return kz_popup_preview_single;
}


static gboolean
cb_enter_notify_event(GtkWidget *widget,
                  GdkEventCrossing *event,
                  KzPopupPreview* popup)
{
      KzPopupPreviewPriv *priv = KZ_POPUP_PREVIEW_GET_PRIVATE (popup);
      
      if (priv->close_delay_id != 0)
      {
            g_source_remove(priv->close_delay_id);
            priv->close_delay_id = 0;
      }
      
      priv->now_pointed = TRUE;
      
      return FALSE;
}

static gboolean
cb_leave_notify_event(GtkWidget *widget,
                  GdkEventCrossing *event,
                  KzPopupPreview *popup)
{
      KzPopupPreviewPriv *priv = KZ_POPUP_PREVIEW_GET_PRIVATE (popup);

      priv->now_pointed = FALSE;
      kz_popup_preview_hide_popup(popup);
      return FALSE;
}

static gboolean
cb_button_press (GtkWidget *widget,
             GdkEventButton *event,
             KzPopupPreview *popup)
{
      KzPopupPreviewPriv *priv = KZ_POPUP_PREVIEW_GET_PRIVATE (popup);

      priv->now_pointed = FALSE;
      kz_popup_preview_hide_popup(popup);
      return FALSE;
}

void
kz_popup_preview_reset(KzPopupPreview *popup)
{
      KzPopupPreviewPriv *priv = KZ_POPUP_PREVIEW_GET_PRIVATE (popup);

      if(priv->popup_window)
            gtk_widget_hide_all(priv->popup_window);

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

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

      if (priv->uri != NULL)
      {
            g_free(priv->uri);
            priv->uri = NULL;
      }

      priv->x = 0;
      priv->y = 0;

      if(priv->now_shown)
      {
            priv->now_shown = FALSE;
      }
      priv->now_pointed = FALSE;
}

void
kz_popup_preview_start(KzPopupPreview *popup, const gchar* uri,
                   const gchar* img, gint x, gint y)
{
      KzPopupPreviewPriv *priv = KZ_POPUP_PREVIEW_GET_PRIVATE (popup);
      GtkWidget *image = NULL;

      priv->x = x;
      priv->y = y;
      if (priv->uri != NULL)
            g_free(priv->uri);
      priv->uri = g_strdup(uri);

      if (g_str_has_suffix(uri, ".jpg") || g_str_has_suffix(uri, ".png")
          || g_str_has_suffix(uri, ".gif") || g_str_has_suffix(uri, ".jpeg")
          || g_str_has_suffix(uri, ".JPG") || g_str_has_suffix(uri, ".PNG")
          || g_str_has_suffix(uri, ".GIF") || g_str_has_suffix(uri, ".JPEG"))
      {
            if (!img) image = kz_popup_preview_get_image(popup, uri);
      }
      else
      {
            image = kz_popup_preview_get_thumbnail(popup, uri);
      }

      if (image != NULL)
      {
            kz_popup_preview_setup_popup(popup, image);
            kz_popup_preview_show_popup(popup);
      }
}

static void
kz_popup_preview_setup_popup(KzPopupPreview* popup, GtkWidget* image)
{
      KzPopupPreviewPriv *priv = KZ_POPUP_PREVIEW_GET_PRIVATE (popup);
      GtkWidget *event_box;

      if(!image) 
      {
            if (priv->image != NULL)
                  gtk_container_remove(priv->popup_frame,
                                   priv->image);
            priv->image = NULL;
            return;
      }

      gtk_widget_show(image);

      event_box = gtk_event_box_new();
      gtk_container_add(GTK_CONTAINER(event_box), image);
      gtk_widget_show(event_box);

      g_signal_connect(event_box, "enter_notify_event",
                   G_CALLBACK(cb_enter_notify_event),
                   popup);
      g_signal_connect(event_box, "leave_notify_event",
                   G_CALLBACK(cb_leave_notify_event),
                   popup);

      g_signal_connect(priv->popup_window, "button-press-event",
                   G_CALLBACK(cb_button_press), popup);
      
      if (priv->image != NULL)
            gtk_container_remove(priv->popup_frame,
                             priv->image);
      gtk_container_add(priv->popup_frame, event_box);
      gtk_widget_show(GTK_WIDGET(priv->popup_frame));
      priv->image = event_box;

      return;
}

static void
kz_popup_preview_show_popup(KzPopupPreview* popup)
{
      KzPopupPreviewPriv *priv = KZ_POPUP_PREVIEW_GET_PRIVATE (popup);
      gint popup_response_delay = DEFAULT_POPUP_RESPONSE_DELAY;

        /* if priv->uri == NULL, popup might be resetted by user*/
      if(!priv->uri || !priv->image) return;

      KZ_CONF_GET("Popup", "response_delay",  popup_response_delay, INT);
      if (popup_response_delay == 0)
            kz_popup_preview_show_popup_real(popup);
      else
      {
            if (priv->close_delay_id != 0)
            {
                  g_source_remove(priv->close_delay_id);
                  priv->close_delay_id = 0;
            }
            
            if (priv->delay_id != 0)
                  g_source_remove(priv->delay_id);

            priv->delay_id
                  = g_timeout_add(popup_response_delay,
                              (GSourceFunc)cb_delay_timeout, popup);
      }

}
static void
kz_popup_preview_show_popup_real(KzPopupPreview* popup)
{
      KzPopupPreviewPriv *priv = KZ_POPUP_PREVIEW_GET_PRIVATE (popup);

      if (priv->now_shown)
            return;

      if (priv->image != NULL)
      {
            gtk_window_move(GTK_WINDOW(priv->popup_window), priv->x + X_OFFSET, priv->y + Y_OFFSET);
            gtk_widget_show_all(priv->popup_window);
            priv->now_shown = TRUE;
      }
}

static gboolean
cb_delay_timeout(KzPopupPreview* popup)
{
      KzPopupPreviewPriv *priv = KZ_POPUP_PREVIEW_GET_PRIVATE (popup);
      gboolean result = TRUE;

        /* if priv->uri == NULL, popup might be resetted by user*/
      if (priv->uri != NULL)
      {
            if (priv->delay_id != 0)
                  priv->delay_id = 0;

            g_free(priv->uri);
            priv->uri = NULL;

            kz_popup_preview_show_popup_real(popup);
            result = FALSE;
      }

      return result;
}

static void
kz_popup_preview_hide_popup(KzPopupPreview* popup)
{
      gint popup_close_delay = DEFAULT_POPUP_CLOSE_DELAY;
      KzPopupPreviewPriv *priv = KZ_POPUP_PREVIEW_GET_PRIVATE (popup);

      KZ_CONF_GET("Popup", "close_delay",  popup_close_delay, INT);

      if (popup_close_delay == 0)
            kz_popup_preview_hide_popup_real(popup);
      else if (priv->delay_id == 0)
      {
            if (priv->close_delay_id != 0)
                  g_source_remove(priv->close_delay_id); /* reset waiting time */
            priv->close_delay_id
                  = g_timeout_add(popup_close_delay,
                              (GSourceFunc)cb_close_delay_timeout, popup);
      }
      else
      {
        /* reset waiting popup */
            kz_popup_preview_hide_popup_real(popup);
      }
}

static void
kz_popup_preview_hide_popup_real(KzPopupPreview *popup)
{
      KzPopupPreviewPriv *priv = KZ_POPUP_PREVIEW_GET_PRIVATE (popup);

      if (priv->uri != NULL)
      {
            g_free(priv->uri);
            priv->uri = NULL;
      }

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

      gtk_widget_hide_all(priv->popup_window);

      if (priv->image != NULL)
      {
            gtk_container_remove(priv->popup_frame,
                             priv->image);
            priv->image = NULL;
      }
      priv->now_shown = FALSE;
}

static gboolean
cb_close_delay_timeout(KzPopupPreview *popup)
{
      KzPopupPreviewPriv *priv = KZ_POPUP_PREVIEW_GET_PRIVATE (popup);

      if (!priv->now_pointed)
      {
            kz_popup_preview_hide_popup_real(popup);
      }

      priv->close_delay_id = 0;

      return FALSE;
}

static GtkWidget *
kz_popup_preview_get_image (KzPopupPreview *popup, const gchar *uri)
{
      GtkWidget *image = NULL;
      gchar *tmp_name, *image_name, *popup_file_name;

      if (uri == NULL)
            return NULL;

      tmp_name = egg_str_get_md5_str(uri);
      image_name = g_strconcat(tmp_name, ".png", NULL);
      popup_file_name = g_build_filename(g_get_home_dir(),
                                 "."PACKAGE,
                                 "popup",
                                 image_name,
                                 NULL);
      g_free(tmp_name);
      g_free(image_name);

      if (!g_file_test(popup_file_name, G_FILE_TEST_EXISTS))
      {
            KzDownloader *dl;

                /* start download */
            dl = kz_downloader_new_with_filename(uri, popup_file_name);
            if (dl)
            {
                  g_signal_connect(dl, "completed",
                               G_CALLBACK(cb_downloader_load_complete),
                               (gpointer)uri);
                  g_signal_connect(dl, "error",
                               G_CALLBACK(cb_downloader_load_error),
                               (gpointer)uri);
                  kz_downloader_to_file(dl);
            }
      }
      else
      {
            GdkPixbuf *scaled_pixbuf, *orig_pixbuf;
            gint width = DEFAULT_WIDTH;
            gint height = DEFAULT_HEIGHT;

            KZ_CONF_GET("Popup", "width",  width, INT);
            KZ_CONF_GET("Popup", "height", height, INT);


            orig_pixbuf = gdk_pixbuf_new_from_file(popup_file_name, NULL);
            if (!orig_pixbuf) return NULL;

            scaled_pixbuf = gdk_pixbuf_scale_keeping_aspect_ratio(orig_pixbuf,
                                                      width, height,
                                                      GDK_INTERP_BILINEAR);

            image = gtk_image_new_from_pixbuf(scaled_pixbuf);

            g_object_unref(orig_pixbuf);
            g_object_unref(scaled_pixbuf);
      }
      g_free(popup_file_name);

      return image;
}

static GtkWidget *
kz_popup_preview_get_thumbnail (KzPopupPreview *popup, const gchar *uri)
{
            
      GtkWidget *image = NULL;
      GdkPixbuf *scaled_pixbuf = NULL, *orig_pixbuf = NULL;
      gchar *filename = NULL;

      if (!uri) return NULL;

      filename = egg_pixbuf_get_thumb_filename(uri,
                                     EGG_PIXBUF_THUMB_LARGE);
      if(filename)
      {
            orig_pixbuf = egg_pixbuf_get_thumbnail_for_file(filename,
                                                EGG_PIXBUF_THUMB_NORMAL,
                                                NULL);
            g_free(filename);
      }

      if (orig_pixbuf)
      {
            gint width = DEFAULT_WIDTH;
            gint height = DEFAULT_HEIGHT;

            KZ_CONF_GET("Popup", "width",  width, INT);
            KZ_CONF_GET("Popup", "height", height, INT);

            scaled_pixbuf = gdk_pixbuf_scale_keeping_aspect_ratio(orig_pixbuf,
                                                      width, height,
                                                      GDK_INTERP_BILINEAR);
            g_object_unref(orig_pixbuf);
      }

      if (scaled_pixbuf)
      {
            image = gtk_image_new_from_pixbuf(scaled_pixbuf);
            g_object_unref(scaled_pixbuf);
      }

      return image;
}

static void
kz_downloader_disconnect_signal(KzDownloader *dl, const gchar *uri)
{
      g_signal_handlers_disconnect_by_func(dl,
                                   G_CALLBACK(cb_downloader_load_complete),
                                   (gpointer)uri);
      g_signal_handlers_disconnect_by_func(dl,
                                   G_CALLBACK(cb_downloader_load_error),
                                   (gpointer)uri);
}

static gboolean
idle_unref_dl(gpointer data)
{
      KzDownloader *dl = data;

      g_object_unref(G_OBJECT(dl));

      return FALSE;
}

static void
cb_downloader_load_complete (KzDownloader *dl, const gchar *uri)
{
      KzPopupPreview *popup = kz_popup_preview_single;
      GtkWidget *image = NULL;

      kz_downloader_disconnect_signal(dl, uri);
      g_idle_add(idle_unref_dl, dl);

      g_return_if_fail(popup);

      /* get_image again */
      image = kz_popup_preview_get_image(popup, uri);
      kz_popup_preview_setup_popup(popup, image);
      kz_popup_preview_show_popup(popup);

      return;
}

static void
cb_downloader_load_error (KzDownloader *dl, const gchar *uri)
{
      kz_downloader_disconnect_signal(dl, uri);
      g_idle_add(idle_unref_dl, dl);

      return;
}

/* The original code is gdk-pixbuf-scale.c in gtk+-2.4.13 */
static GdkPixbuf *
gdk_pixbuf_scale_keeping_aspect_ratio (const GdkPixbuf *src,
                               int              max_width,
                               int              max_height,
                               GdkInterpType    interp_type)
{
      GdkPixbuf *dest;
      gdouble ratio;
      int src_width, src_height;
      int dest_width, dest_height;

      g_return_val_if_fail (src != NULL, NULL);

      src_width = gdk_pixbuf_get_width(src);
      src_height = gdk_pixbuf_get_height(src);

      ratio = ((double)max_width / (double)src_width > (double)max_height / (double)src_height) ?
            (double)max_height / (double)src_height : (double)max_width / (double)src_width;
      dest_width = (int)(src_width * ratio);
      dest_height = (int)(src_height * ratio);

      dest = gdk_pixbuf_new (GDK_COLORSPACE_RGB, gdk_pixbuf_get_has_alpha(src), 8, dest_width, dest_height);
      if (!dest)
            return NULL;


      gdk_pixbuf_scale (src, dest,  0, 0, 
                    dest_width,
                    dest_height,
                    0, 0,
                    ratio,
                    ratio,
                    interp_type);

      return dest;
}

Generated by  Doxygen 1.6.0   Back to index