Logo Search packages:      
Sourcecode: galeon version File versions

gul-file-preview.c

/*
 *  Copyright (C) 2004  Tommi Komulainen
 *
 *  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.
 */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "gul-file-preview.h"
#include "gul-gui.h"
#include "galeon-debug.h"

#include <libgnomeui/gnome-thumbnail.h>

#include <libgnomevfs/gnome-vfs-mime-handlers.h>
#include <libgnomevfs/gnome-vfs-mime-utils.h>
#include <libgnomevfs/gnome-vfs-ops.h>
#include <libgnomevfs/gnome-vfs-utils.h>

#include <gtk/gtkiconfactory.h>
#include <gtk/gtkimage.h>
#include <gtk/gtklabel.h>
#include <gtk/gtkstock.h>
#include <gtk/gtktable.h>

#include <glib/gi18n.h>

#include <string.h>


static GtkIconSize GUL_FILE_PREVIEW_ICON_SIZE_NORMAL;
static GtkIconSize GUL_FILE_PREVIEW_ICON_SIZE_LARGE;

#define GUL_FILE_PREVIEW_GET_PRIVATE(o) \
   (G_TYPE_INSTANCE_GET_PRIVATE ((o), GUL_TYPE_FILE_PREVIEW, GulFilePreviewPrivate))

typedef struct _GulFilePreviewPrivate GulFilePreviewPrivate;
struct _GulFilePreviewPrivate {
      GtkWidget   *table;
      GtkImage    *image;
      GtkLabel    *size_label;
      GtkLabel    *type_label;
      GtkLabel    *modified_label;
      GtkLabel    *created_label;

      GnomeThumbnailFactory *factory;
      GtkIconSize            icon_size;

      guint serial;
};

G_DEFINE_TYPE(GulFilePreview, gul_file_preview, GTK_TYPE_CONTAINER)


GtkWidget *
gul_file_preview_new (void)
{
      GulFilePreview *self;

      self = GUL_FILE_PREVIEW (g_object_new (GUL_TYPE_FILE_PREVIEW, 0));
      return GTK_WIDGET (self);
}

static void
set_preview_size (GulFilePreview *self, GnomeVFSFileSize size)
{
      GulFilePreviewPrivate *priv = GUL_FILE_PREVIEW_GET_PRIVATE (self);
      char *size_str;

      size_str = gnome_vfs_format_file_size_for_display (size);
      gtk_label_set_text (priv->size_label, size_str);
      g_free (size_str);
}

static void
set_preview_date (GtkLabel *label, time_t stamp)
{
      GDate mtime;
      char buf[256];

      g_date_set_time (&mtime, (GTime) stamp);

      if (g_date_strftime (buf, sizeof (buf), "%x", &mtime) == 0)
            strcpy (buf, _("Unknown"));

      gtk_label_set_text (label, buf);
}

static GdkPixbuf *
gul_file_preview_lookup (GulFilePreview *self, const char *uri, GnomeVFSFileInfo *file_info)
{
      GulFilePreviewPrivate *priv = GUL_FILE_PREVIEW_GET_PRIVATE (self);
      GdkPixbuf *pixbuf;
      char      *path;

      path = gnome_thumbnail_factory_lookup (priv->factory, uri, file_info->mtime);
      if (path == NULL) return NULL;

      LOG ("thumbnail path: %s", path);

      pixbuf = gdk_pixbuf_new_from_file (path, NULL);
      g_free (path);

      if (pixbuf != NULL)
      {
            if (gnome_thumbnail_is_valid (pixbuf, uri, file_info->mtime))
                  return pixbuf;

            g_object_unref (pixbuf);
      }

      return NULL;
}

typedef struct {
      GulFilePreview   *self;
      char             *uri;
      GnomeVFSFileInfo *file_info;
      GdkPixbuf        *pixbuf;
      guint         serial;
      GnomeThumbnailFactory *factory;
} ThreadData;

static void
thread_data_free (gpointer user_data)
{
      ThreadData *data = user_data;

      g_free (data->uri);
      gnome_vfs_file_info_unref (data->file_info);
      g_object_unref (data->self);
      g_object_unref (data->factory);
      g_free (data);
}

static gboolean
thumbnail_idle_cb (gpointer user_data)
{
      ThreadData *data = user_data;
      GulFilePreview *self = data->self;
      GulFilePreviewPrivate *priv = GUL_FILE_PREVIEW_GET_PRIVATE (self);

      if (priv->serial == data->serial)
      {
            gtk_image_set_from_pixbuf (priv->image, data->pixbuf);
      }

      return FALSE;
}

static gpointer
thumbnail_thread_func (gpointer user_data)
{
      ThreadData *data = user_data;
      GdkPixbuf *pixbuf;

      START_PROFILER ("generating thumbnail");
      pixbuf = gnome_thumbnail_factory_generate_thumbnail (data->factory, data->uri, data->file_info->mime_type);
      STOP_PROFILER ("generating thumbnail");
      if (pixbuf)
      {
            START_PROFILER ("saving thumbnail");
            gnome_thumbnail_factory_save_thumbnail (data->factory, pixbuf, data->uri, data->file_info->mtime);
            STOP_PROFILER ("saving thumbnail");

            data->pixbuf = pixbuf;
            g_idle_add_full (G_PRIORITY_DEFAULT_IDLE, thumbnail_idle_cb, data,
                         thread_data_free);
            return NULL;
      }

      thread_data_free (data);
      return NULL;
}

void
gul_file_preview_set_file_info (GulFilePreview *self, const char *uri, GnomeVFSFileInfo *file_info)
{
      GulFilePreviewPrivate *priv = GUL_FILE_PREVIEW_GET_PRIVATE (self);
      const char *mime_desc;
      GdkPixbuf  *pixbuf;

      mime_desc = gnome_vfs_mime_get_description (file_info->mime_type);
      if (mime_desc == NULL || *mime_desc == '\0')
            mime_desc = file_info->mime_type;

      set_preview_size (self, file_info->size);
      gtk_label_set_text (priv->type_label, mime_desc);
      set_preview_date (priv->created_label,  file_info->ctime);
      set_preview_date (priv->modified_label, file_info->mtime);

      /* Try 1. Load existing thumbnail.  This is fast. */
      pixbuf = gul_file_preview_lookup (self, uri, file_info);
      if (pixbuf != NULL)
      {
            gtk_image_set_from_pixbuf (priv->image, pixbuf);
            g_object_unref (pixbuf);
            return;
      }

      /* Step 2. Generate new thumbnail if we can.  This can be considerably
       * slower as we might end up running an external program.  To provide
       * better feedback we first display a dummy thumbnail as soon as
       * possible and generate the real thumbnail in another thread.  (Using
       * idle callback would stall the file chooser somewhat.)
       *
       * This works nicely when generating the thumbnail does take long
       * enough, but otherwise you get just a quick glimpse of the dummy
       * thumbnail before the real one replaces it.  Shouldn't be a problem,
       * though, as once we've generated the thumbnail once we can get it
       * directly the next time.
       */
      if (gnome_thumbnail_factory_can_thumbnail (priv->factory, uri,
                                           file_info->mime_type,
                                       file_info->mtime))
      {
            ThreadData *data;

            gul_gui_image_set_from_mime_type (GTK_WIDGET (priv->image),
                                          file_info->mime_type,
                                      priv->icon_size);

            data = g_new (ThreadData, 1);
            data->self = g_object_ref (self);
            data->uri  = g_strdup (uri);
            data->file_info = file_info;
            gnome_vfs_file_info_ref (file_info);
            data->pixbuf = NULL;
            data->serial = ++priv->serial;
            data->factory = g_object_ref (priv->factory);

            /* if we can't get the thread running, we'll still have the
             * dummy thumbnail */
            if (!g_thread_create (thumbnail_thread_func, data, FALSE, NULL))
                  thread_data_free (data);
      }
      else
      {
            gtk_image_set_from_pixbuf (priv->image, NULL);
      }
}

/* GtkContainer */

static void
gul_file_preview_forall (GtkContainer *container,
                     gboolean      include_internals,
                   GtkCallback   callback,
                   gpointer      callback_data)
{
      GulFilePreview        *self = GUL_FILE_PREVIEW (container);
      GulFilePreviewPrivate *priv = GUL_FILE_PREVIEW_GET_PRIVATE (self);

      g_return_if_fail (callback != NULL);

      if (include_internals)
      {
            (* callback) (priv->table, callback_data);
      }
}

/* GtkWidget */

static void
gul_file_preview_size_request (GtkWidget *widget, GtkRequisition *requisition)
{
      GulFilePreview        *self = GUL_FILE_PREVIEW (widget);
      GulFilePreviewPrivate *priv = GUL_FILE_PREVIEW_GET_PRIVATE (self);
      GtkRequisition child_requisition;
      int            border_width;

      gtk_widget_size_request (priv->table, &child_requisition);

      border_width = GTK_CONTAINER (self)->border_width;
      requisition->width  = child_requisition.width  + 2*border_width;
      requisition->height = child_requisition.height + 2*border_width;
}

static void
gul_file_preview_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
{
      GulFilePreview        *self = GUL_FILE_PREVIEW (widget);
      GulFilePreviewPrivate *priv = GUL_FILE_PREVIEW_GET_PRIVATE (self);
      GtkAllocation child_allocation;
      int           border_width;

      widget->allocation = *allocation;
      border_width = GTK_CONTAINER (self)->border_width;

      child_allocation.x = allocation->x + border_width;
      child_allocation.y = allocation->y + border_width;
      child_allocation.width  = allocation->width  - 2*border_width;
      child_allocation.height = allocation->height - 2*border_width;
      gtk_widget_size_allocate (priv->table, &child_allocation);
}

static void
gul_file_preview_set_icon_size (GulFilePreview *self, GtkIconSize icon_size)
{
      GulFilePreviewPrivate *priv = GUL_FILE_PREVIEW_GET_PRIVATE (self);

      if (priv->icon_size == icon_size) return;

      if (priv->factory) g_object_unref (priv->factory);

      priv->icon_size = icon_size;
      if (priv->icon_size == GUL_FILE_PREVIEW_ICON_SIZE_LARGE)
      {
            priv->factory = gnome_thumbnail_factory_new (GNOME_THUMBNAIL_SIZE_LARGE);
            gtk_widget_set_size_request (GTK_WIDGET (priv->image), 256, 256);
      }
      else
      {
            priv->factory = gnome_thumbnail_factory_new (GNOME_THUMBNAIL_SIZE_NORMAL);
            gtk_widget_set_size_request (GTK_WIDGET (priv->image), 128, 128);
      }
}

/* GObject */

static GtkLabel *
gul_file_preview_set_row (GtkTable *table, int row, const char *label_text)
{
      GtkWidget *label;

      label = gtk_label_new (label_text);
      g_object_set (label, "xalign", 1.0, NULL);
      gtk_table_attach (table, label, 0, 1, row, row+1, GTK_FILL|GTK_EXPAND, GTK_FILL, 0, 0);

      label = gtk_label_new ("");
      g_object_set (label, "xalign", 0.0, NULL);
      gtk_table_attach (table, label, 1, 2, row, row+1, GTK_FILL|GTK_EXPAND, GTK_FILL, 0, 0);

      return GTK_LABEL (label);
}

static void
gul_file_preview_init (GulFilePreview *self)
{
      GulFilePreviewPrivate *priv = GUL_FILE_PREVIEW_GET_PRIVATE (self);
      GtkTable *table;

      LOG ("gul_file_preview_init");

      GTK_WIDGET_SET_FLAGS (self, GTK_NO_WINDOW);

      priv->table = gtk_table_new (5, 2, FALSE);

      table = GTK_TABLE (priv->table);
      gtk_table_set_col_spacings (table, 6);
      gtk_table_set_row_spacings (table, 3);
      
      priv->image = g_object_new (GTK_TYPE_IMAGE, "xalign", 0.5, NULL);
      gtk_table_attach (table, GTK_WIDGET (priv->image), 0, 2, 0, 1, GTK_FILL, GTK_FILL, 0, 0);

      /* file size */
      priv->size_label     = gul_file_preview_set_row (table, 1, _("Size:"));
      /* file type, e.g. "PDF document" or "application/pdf" if no human
       * readable description available */
      priv->type_label     = gul_file_preview_set_row (table, 2, _("Type:"));
      /* file creation date */
      priv->created_label  = gul_file_preview_set_row (table, 3, _("Created:"));
      /* file modification date */
      priv->modified_label = gul_file_preview_set_row (table, 4, _("Modified:"));

      priv->icon_size = -1;
      gul_file_preview_set_icon_size (self, GUL_FILE_PREVIEW_ICON_SIZE_NORMAL);

      gtk_widget_show_all (priv->table);
      gtk_widget_set_parent (priv->table, GTK_WIDGET (self));
}

static void
gul_file_preview_dispose (GObject *object)
{
      GulFilePreview        *self = GUL_FILE_PREVIEW (object);
      GulFilePreviewPrivate *priv = GUL_FILE_PREVIEW_GET_PRIVATE (self);

      LOG ("gul_file_preview_dispose");

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

      if (G_OBJECT_CLASS (gul_file_preview_parent_class)->dispose)
            (* G_OBJECT_CLASS (gul_file_preview_parent_class)->dispose) (object);
}

static void
gul_file_preview_class_init (GulFilePreviewClass *klass)
{
      GObjectClass      *object_class = (GObjectClass *)klass;
      GtkWidgetClass    *widget_class = (GtkWidgetClass *)klass;
      GtkContainerClass *container_class = (GtkContainerClass *)klass;

      g_type_class_add_private (klass, sizeof (GulFilePreviewPrivate));

      GUL_FILE_PREVIEW_ICON_SIZE_NORMAL = gtk_icon_size_register ("gul-preview-normal", 128, 128);
      GUL_FILE_PREVIEW_ICON_SIZE_LARGE  = gtk_icon_size_register ("gul-preview-large",  256, 256);

      object_class->dispose = gul_file_preview_dispose;

      widget_class->size_request  = gul_file_preview_size_request;
      widget_class->size_allocate = gul_file_preview_size_allocate;

      container_class->forall = gul_file_preview_forall;
}

Generated by  Doxygen 1.6.0   Back to index