Evolution consists of entirely too many small utility libraries, which increases linking and loading time, places a burden on higher layers of the application (e.g. modules) which has to remember to link to all the small in-tree utility libraries, and makes it difficult to generate API documentation for these utility libraries in one Gtk-Doc module. Merge the following utility libraries under the umbrella of libeutil, and enforce a single-include policy on libeutil so we can reorganize the files as desired without disrupting its pseudo-public API. libemail-utils/libemail-utils.la libevolution-utils/libevolution-utils.la filter/libfilter.la widgets/e-timezone-dialog/libetimezonedialog.la widgets/menus/libmenus.la widgets/misc/libemiscwidgets.la widgets/table/libetable.la widgets/text/libetext.la This also merges libedataserverui from the Evolution-Data-Server module, since Evolution is its only consumer nowadays, and I'd like to make some improvements to those APIs without concern for backward-compatibility. And finally, start a Gtk-Doc module for libeutil. It's going to be a project just getting all the symbols _listed_ much less _documented_. But the skeletal structure is in place and I'm off to a good start.
438 lines
11 KiB
C
438 lines
11 KiB
C
/*
|
|
* 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)
|
|
*
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include <config.h>
|
|
#endif
|
|
|
|
#include "e-picture-gallery.h"
|
|
|
|
#include "e-icon-factory.h"
|
|
|
|
#define E_PICTURE_GALLERY_GET_PRIVATE(obj) \
|
|
(G_TYPE_INSTANCE_GET_PRIVATE \
|
|
((obj), E_TYPE_PICTURE_GALLERY, EPictureGalleryPrivate))
|
|
|
|
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 ((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 (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 (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)
|
|
{
|
|
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);
|
|
|
|
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 = E_PICTURE_GALLERY_GET_PRIVATE (gallery);
|
|
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);
|
|
}
|