/*
* 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);
}