aboutsummaryrefslogblamecommitdiffstats
path: root/widgets/misc/e-picture-gallery.c
blob: b4eabe48f3b2bd40ce5da0ea34309666ab2d80df (plain) (tree)
























                                                                             











































































































































































































































































































                                                                                                                                              





                                 

                                                                              
 




















                                                                                           

















                                                                                    

                                                                          




























                                                                          

                                                                         


                                                 






                                                                         
/*
 * e-picture-gallery.c
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) version 3.
 *
 * 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with the program; if not, see <http://www.gnu.org/licenses/>
 *
 *
 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
 *
 */

#include "e-util/e-icon-factory.h"

#include "e-picture-gallery.h"

struct _EPictureGalleryPrivate {
    gboolean initialized;
    gchar *path;
    GFileMonitor *monitor;
};

enum {
    PROP_0,
    PROP_PATH
};

enum {
    COL_PIXBUF = 0,
    COL_URI,
    COL_FILENAME_TEXT
};

G_DEFINE_TYPE (EPictureGallery, e_picture_gallery, GTK_TYPE_ICON_VIEW)

static gboolean
update_file_iter (GtkListStore *list_store, GtkTreeIter *iter, GFile *file, gboolean force_thumbnail_update)
{
    GFileInfo *file_info;
    gchar *uri;
    gboolean res = FALSE;

    g_return_val_if_fail (list_store != NULL, FALSE);
    g_return_val_if_fail (iter != NULL, FALSE);
    g_return_val_if_fail (file != NULL, FALSE);

    uri = g_file_get_uri (file);

    file_info = g_file_query_info (file,
        G_FILE_ATTRIBUTE_THUMBNAIL_PATH ","
        G_FILE_ATTRIBUTE_THUMBNAILING_FAILED ","
        G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME ","
        G_FILE_ATTRIBUTE_STANDARD_SIZE,
        G_FILE_QUERY_INFO_NONE,
        NULL,
        NULL);

    if (file_info != NULL) {
        const gchar *existing_thumb = g_file_info_get_attribute_byte_string (file_info, G_FILE_ATTRIBUTE_THUMBNAIL_PATH);
        gchar *new_thumb = NULL;

        if (!existing_thumb || force_thumbnail_update) {
            gchar *filename;

            filename = g_file_get_path (file);
            if (filename) {
                new_thumb = e_icon_factory_create_thumbnail (filename);
                if (new_thumb)
                    existing_thumb = new_thumb;
                g_free (filename);
            }
        }

        if (existing_thumb && !g_file_info_get_attribute_boolean (file_info, G_FILE_ATTRIBUTE_THUMBNAILING_FAILED)) {
            GdkPixbuf* pixbuf;

            pixbuf = gdk_pixbuf_new_from_file (existing_thumb, NULL);

            if (pixbuf) {
                const gchar *filename;
                gchar *filename_text = NULL;
                guint64 filesize;

                filename = g_file_info_get_attribute_string (file_info, G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME);
                if (filename) {
                    filesize = g_file_info_get_attribute_uint64 (file_info, G_FILE_ATTRIBUTE_STANDARD_SIZE);
                    if (filesize) {
                        gchar *tmp = g_format_size_for_display ((goffset) filesize);
                        filename_text = g_strdup_printf ("%s (%s)", filename, tmp);
                        g_free (tmp);
                    }

                    res = TRUE;
                    gtk_list_store_set (list_store, iter,
                        COL_PIXBUF, pixbuf,
                        COL_URI, uri,
                        COL_FILENAME_TEXT, filename_text ? filename_text : filename,
                        -1);
                }

                g_object_unref (pixbuf);
                g_free (filename_text);
            }
        }

        g_free (new_thumb);
    }

    g_free (uri);

    return res;
}

static void
add_file (GtkListStore *list_store, GFile *file)
{
    GtkTreeIter iter;

    g_return_if_fail (list_store != NULL);
    g_return_if_fail (file != NULL);

    gtk_list_store_append (list_store, &iter);
    if (!update_file_iter (list_store, &iter, file, FALSE))
        gtk_list_store_remove (list_store, &iter);
}

static gboolean
find_file_uri (GtkListStore *list_store, const gchar *uri, GtkTreeIter *iter)
{
    GtkTreeModel *model;

    g_return_val_if_fail (list_store != NULL, FALSE);
    g_return_val_if_fail (uri != NULL, FALSE);
    g_return_val_if_fail (iter != NULL, FALSE);

    model = GTK_TREE_MODEL (list_store);
    g_return_val_if_fail (model != NULL, FALSE);

    if (!gtk_tree_model_get_iter_first (model, iter))
        return FALSE;

    do {
        gchar *iter_uri = NULL;

        gtk_tree_model_get (model, iter,
            COL_URI, &iter_uri,
            -1);

        if (iter_uri && g_ascii_strcasecmp (uri, iter_uri) == 0) {
            g_free (iter_uri);
            return TRUE;
        }

        g_free (iter_uri);
    } while (gtk_tree_model_iter_next (model, iter));

    return FALSE;
}

static void
picture_gallery_dir_changed_cb (GFileMonitor *monitor, GFile *file, GFile *other_file, GFileMonitorEvent event_type, EPictureGallery *gallery)
{
    gchar *uri;
    GtkListStore *list_store;
    GtkTreeIter iter;

    g_return_if_fail (gallery != NULL);
    g_return_if_fail (gallery->priv != NULL);
    g_return_if_fail (file != NULL);

    list_store = GTK_LIST_STORE (gtk_icon_view_get_model (GTK_ICON_VIEW (gallery)));
    g_return_if_fail (list_store != NULL);

    uri = g_file_get_uri (file);
    if (!uri)
        return;

    switch (event_type) {
    case G_FILE_MONITOR_EVENT_CREATED:
        if (find_file_uri (list_store, uri, &iter)) {
            if (!update_file_iter (list_store, &iter, file, TRUE))
                gtk_list_store_remove (list_store, &iter);
        } else {
            add_file (list_store, file);
        }
        break;
    case G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT:
        if (find_file_uri (list_store, uri, &iter)) {
            if (!update_file_iter (list_store, &iter, file, TRUE))
                gtk_list_store_remove (list_store, &iter);
        }
        break;
    case G_FILE_MONITOR_EVENT_DELETED:
        if (find_file_uri (list_store, uri, &iter))
            gtk_list_store_remove (list_store, &iter);
        break;
    default:
        break;
    }

    g_free (uri);
}

static gboolean
picture_gallery_start_loading_cb (EPictureGallery *gallery)
{
    GtkIconView *icon_view;
    GtkListStore *list_store;
    GDir *dir;
    const gchar *dirname;

    icon_view = GTK_ICON_VIEW (gallery);
    list_store = GTK_LIST_STORE (gtk_icon_view_get_model (icon_view));
    g_return_val_if_fail (list_store != NULL, FALSE);

    dirname = e_picture_gallery_get_path (gallery);
    if (!dirname)
        return FALSE;

    dir = g_dir_open (dirname, 0, NULL);
    if (dir) {
        GFile *file;
        const gchar *basename;

        while ((basename = g_dir_read_name (dir)) != NULL) {
            gchar *filename;

            filename = g_build_filename (dirname, basename, NULL);
            file = g_file_new_for_path (filename);

            add_file (list_store, file);

            g_free (filename);
            g_object_unref (file);
        }

        g_dir_close (dir);

        file = g_file_new_for_path (dirname);
        gallery->priv->monitor = g_file_monitor_directory (file, G_FILE_MONITOR_NONE, NULL, NULL);
        g_object_unref (file);

        if (gallery->priv->monitor)
            g_signal_connect (gallery->priv->monitor, "changed", G_CALLBACK (picture_gallery_dir_changed_cb), gallery);
    }

    g_object_unref (icon_view);

    return FALSE;
}

const gchar *
e_picture_gallery_get_path (EPictureGallery *gallery)
{
    g_return_val_if_fail (gallery != NULL, NULL);
    g_return_val_if_fail (E_IS_PICTURE_GALLERY (gallery), NULL);
    g_return_val_if_fail (gallery->priv != NULL, NULL);

    return gallery->priv->path;
}

static void
picture_gallery_set_path (EPictureGallery *gallery, const gchar *path)
{
    g_return_if_fail (gallery != NULL);
    g_return_if_fail (E_IS_PICTURE_GALLERY (gallery));
    g_return_if_fail (gallery->priv != NULL);

    g_free (gallery->priv->path);

    if (!path || !*path || !g_file_test (path, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR))
        gallery->priv->path = g_strdup (g_get_user_special_dir (G_USER_DIRECTORY_PICTURES));
    else
        gallery->priv->path = g_strdup (path);
}

static void
picture_gallery_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec)
{
    switch (property_id) {
    case PROP_PATH:
        g_value_set_string (value, e_picture_gallery_get_path (E_PICTURE_GALLERY (object)));
        return;
    }

    G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}

static void
picture_gallery_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec)
{
    switch (property_id) {
    case PROP_PATH:
        picture_gallery_set_path (E_PICTURE_GALLERY (object), g_value_get_string (value));
        return;
    }

    G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}

static void
visible_cb (EPictureGallery *gallery)
{
    g_return_if_fail (gallery != NULL);
    g_return_if_fail (gallery->priv != NULL);

    if (!gallery->priv->initialized && gtk_widget_get_visible (GTK_WIDGET (gallery))) {
        gallery->priv->initialized = TRUE;

        g_idle_add ((GSourceFunc) picture_gallery_start_loading_cb, gallery);
    }
}

static void
picture_gallery_constructed (GObject *object)
{
    GtkIconView *icon_view;
    GtkListStore *list_store;
    GtkTargetEntry *targets;
    GtkTargetList *list;
    gint n_targets;

    /* Chain up to parent's constructed() method. */
    G_OBJECT_CLASS (e_picture_gallery_parent_class)->constructed (object);

    icon_view = GTK_ICON_VIEW (object);

    list_store = gtk_list_store_new (3, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING);
    gtk_icon_view_set_model (icon_view, GTK_TREE_MODEL (list_store));
    g_object_unref (list_store);

    gtk_icon_view_set_pixbuf_column (icon_view, COL_PIXBUF);
    gtk_icon_view_set_text_column (icon_view, COL_FILENAME_TEXT);
    gtk_icon_view_set_tooltip_column (icon_view, -1);

    list = gtk_target_list_new (NULL, 0);
    gtk_target_list_add_uri_targets (list, 0);
    targets = gtk_target_table_new_from_list (list, &n_targets);

    gtk_icon_view_enable_model_drag_source (
        icon_view, GDK_BUTTON1_MASK,
        targets, n_targets, GDK_ACTION_COPY);

    gtk_target_table_free (targets, n_targets);
    gtk_target_list_unref (list);

    g_signal_connect (object, "notify::visible", G_CALLBACK (visible_cb), NULL);
}

static void
picture_gallery_dispose (GObject *object)
{
    EPictureGallery *gallery;

    gallery = E_PICTURE_GALLERY (object);

    g_return_if_fail (gallery != NULL);
    g_return_if_fail (gallery->priv != NULL);

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

    /* Chain up to parent's dispose() method. */
    G_OBJECT_CLASS (e_picture_gallery_parent_class)->dispose (object);
}

static void
e_picture_gallery_class_init (EPictureGalleryClass *class)
{
    GObjectClass *object_class;

    g_type_class_add_private (class, sizeof (EPictureGalleryPrivate));

    object_class = G_OBJECT_CLASS (class);
    object_class->get_property = picture_gallery_get_property;
    object_class->set_property = picture_gallery_set_property;
    object_class->constructed = picture_gallery_constructed;
    object_class->dispose = picture_gallery_dispose;

    g_object_class_install_property (
        object_class,
        PROP_PATH,
        g_param_spec_string (
            "path",
            "Gallery path",
            NULL,
            NULL,
            G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
}

static void
e_picture_gallery_init (EPictureGallery *gallery)
{
    gallery->priv = G_TYPE_INSTANCE_GET_PRIVATE (
        gallery, E_TYPE_PICTURE_GALLERY, EPictureGalleryPrivate);
    gallery->priv->initialized = FALSE;
    gallery->priv->monitor = NULL;
    picture_gallery_set_path (gallery, NULL);
}

GtkWidget *
e_picture_gallery_new (const gchar *path)
{
    return g_object_new (E_TYPE_PICTURE_GALLERY, "path", path, NULL);
}