diff options
author | daniel g. siegel <dgsiegel@gnome.org> | 2010-07-01 00:00:05 +0200 |
---|---|---|
committer | daniel g. siegel <dgsiegel@gnome.org> | 2010-07-01 00:00:05 +0200 |
commit | d8b9a6ff39d12037b2fd070d8627c12dbb580b44 (patch) | |
tree | 2bdc43f0764d8bf7f5c859bf7633d28f2eeee89b /src/thumbview | |
parent | 524133d85036778a0cb8b5ddf01ae140e4d339ef (diff) | |
download | cheese-d8b9a6ff39d12037b2fd070d8627c12dbb580b44.tar.gz |
move valasrc to src directory
Diffstat (limited to 'src/thumbview')
-rw-r--r-- | src/thumbview/cheese-gconf.c | 506 | ||||
-rw-r--r-- | src/thumbview/cheese-gconf.h | 67 | ||||
-rw-r--r-- | src/thumbview/cheese-thumb-view.c | 674 | ||||
-rw-r--r-- | src/thumbview/cheese-thumb-view.h | 60 | ||||
-rw-r--r-- | src/thumbview/eog-thumb-nav.c | 750 | ||||
-rw-r--r-- | src/thumbview/eog-thumb-nav.h | 77 | ||||
-rw-r--r-- | src/thumbview/eog-thumbnail.c | 293 | ||||
-rw-r--r-- | src/thumbview/eog-thumbnail.h | 39 |
8 files changed, 2466 insertions, 0 deletions
diff --git a/src/thumbview/cheese-gconf.c b/src/thumbview/cheese-gconf.c new file mode 100644 index 00000000..984e0a7f --- /dev/null +++ b/src/thumbview/cheese-gconf.c @@ -0,0 +1,506 @@ +/* + * Copyright © 2007-2009 daniel g. siegel <dgsiegel@gnome.org> + * + * Licensed under the GNU General Public License Version 2 + * + * 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 of the License, 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, see <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H + #include <cheese-config.h> +#endif + +#include <glib.h> +#include <stdlib.h> + +#include <gconf/gconf.h> +#include <gconf/gconf-client.h> + +#include "cheese-gconf.h" + +#define CHEESE_GCONF_PREFIX "/apps/cheese" + +G_DEFINE_TYPE (CheeseGConf, cheese_gconf, G_TYPE_OBJECT) + +#define CHEESE_GCONF_GET_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((o), CHEESE_TYPE_GCONF, CheeseGConfPrivate)) + +typedef struct +{ + GConfClient *client; +} CheeseGConfPrivate; + +static void +cheese_gconf_get_property (GObject *object, guint prop_id, GValue *value, + GParamSpec *pspec) +{ + CheeseGConf *self; + + self = CHEESE_GCONF (object); + CheeseGConfPrivate *priv = CHEESE_GCONF_GET_PRIVATE (self); + + char *effects; + GSList *list, *tmp; + + switch (prop_id) + { + case GCONF_PROP_COUNTDOWN: + g_value_set_boolean (value, gconf_client_get_bool (priv->client, + CHEESE_GCONF_PREFIX "/countdown", + NULL)); + break; + case GCONF_PROP_CAMERA: + g_value_set_string (value, gconf_client_get_string (priv->client, + CHEESE_GCONF_PREFIX "/camera", + NULL)); + break; + case GCONF_PROP_SELECTED_EFFECTS: + effects = NULL; + list = gconf_client_get_list (priv->client, + CHEESE_GCONF_PREFIX "/selected_effects", + GCONF_VALUE_STRING, + NULL); + tmp = list; + while (tmp != NULL) + { + char *str = tmp->data; + int j; + str[0] = g_ascii_toupper (str[0]); + for (j = 1; j < g_utf8_strlen (str, -1); j++) + { + if (str[j] == '-') + { + str[j] = ' '; + str[j + 1] = g_ascii_toupper (str[j + 1]); + } + else if (str[j] == '_') + { + str[j] = '/'; + str[j + 1] = g_ascii_toupper (str[j + 1]); + } + } + if (effects == NULL) + effects = g_strdup (str); + else + { + gchar *dummy = effects; + effects = g_strjoin (",", effects, str, NULL); + g_free (dummy); + } + + g_free (tmp->data); + tmp = g_slist_next (tmp); + } + g_value_set_string (value, effects); + + g_slist_free (list); + g_slist_free (tmp); + break; + case GCONF_PROP_X_RESOLUTION: + g_value_set_int (value, gconf_client_get_int (priv->client, + CHEESE_GCONF_PREFIX "/x_resolution", + NULL)); + break; + case GCONF_PROP_Y_RESOLUTION: + g_value_set_int (value, gconf_client_get_int (priv->client, + CHEESE_GCONF_PREFIX "/y_resolution", + NULL)); + break; + case GCONF_PROP_BRIGHTNESS: + if (!gconf_client_get (priv->client, + CHEESE_GCONF_PREFIX "/brightness", + NULL)) + g_value_set_double (value, G_PARAM_SPEC_DOUBLE (pspec)->default_value); + else + g_value_set_double (value, gconf_client_get_float (priv->client, + CHEESE_GCONF_PREFIX "/brightness", + NULL)); + break; + case GCONF_PROP_CONTRAST: + if (!gconf_client_get (priv->client, + CHEESE_GCONF_PREFIX "/contrast", + NULL)) + g_value_set_double (value, G_PARAM_SPEC_DOUBLE (pspec)->default_value); + else + g_value_set_double (value, gconf_client_get_float (priv->client, + CHEESE_GCONF_PREFIX "/contrast", + NULL)); + break; + case GCONF_PROP_SATURATION: + if (!gconf_client_get (priv->client, + CHEESE_GCONF_PREFIX "/saturation", + NULL)) + g_value_set_double (value, G_PARAM_SPEC_DOUBLE (pspec)->default_value); + else + g_value_set_double (value, gconf_client_get_float (priv->client, + CHEESE_GCONF_PREFIX "/saturation", + NULL)); + break; + case GCONF_PROP_HUE: + if (!gconf_client_get (priv->client, + CHEESE_GCONF_PREFIX "/hue", + NULL)) + g_value_set_double (value, G_PARAM_SPEC_DOUBLE (pspec)->default_value); + else + g_value_set_double (value, gconf_client_get_float (priv->client, + CHEESE_GCONF_PREFIX "/hue", + NULL)); + break; + case GCONF_PROP_VIDEO_PATH: + g_value_set_string (value, gconf_client_get_string (priv->client, + CHEESE_GCONF_PREFIX "/video_path", + NULL)); + break; + case GCONF_PROP_PHOTO_PATH: + g_value_set_string (value, gconf_client_get_string (priv->client, + CHEESE_GCONF_PREFIX "/photo_path", + NULL)); + break; + case GCONF_PROP_ENABLE_DELETE: + g_value_set_boolean (value, gconf_client_get_bool (priv->client, + CHEESE_GCONF_PREFIX "/enable_delete", + NULL)); + break; + case GCONF_PROP_WIDE_MODE: + g_value_set_boolean (value, gconf_client_get_bool (priv->client, + CHEESE_GCONF_PREFIX "/wide_mode", + NULL)); + break; + case GCONF_PROP_BURST_DELAY: + g_value_set_int (value, gconf_client_get_int (priv->client, + CHEESE_GCONF_PREFIX "/burst_delay", + NULL)); + break; + case GCONF_PROP_BURST_REPEAT: + g_value_set_int (value, gconf_client_get_int (priv->client, + CHEESE_GCONF_PREFIX "/burst_repeat", + NULL)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +cheese_gconf_set_property (GObject *object, guint prop_id, const GValue *value, + GParamSpec *pspec) +{ + CheeseGConf *self; + + self = CHEESE_GCONF (object); + CheeseGConfPrivate *priv = CHEESE_GCONF_GET_PRIVATE (self); + + gchar **effects = NULL; + GSList *list = NULL; + int i; + + switch (prop_id) + { + case GCONF_PROP_COUNTDOWN: + gconf_client_set_bool (priv->client, + CHEESE_GCONF_PREFIX "/countdown", + g_value_get_boolean (value), + NULL); + break; + case GCONF_PROP_CAMERA: + gconf_client_set_string (priv->client, + CHEESE_GCONF_PREFIX "/camera", + g_value_get_string (value), + NULL); + break; + case GCONF_PROP_SELECTED_EFFECTS: + + if (g_value_get_string (value) == NULL) + { + list = NULL; + } + else + { + char *str = g_value_dup_string (value); + int j; + for (j = 0; j < g_utf8_strlen (str, -1); j++) + { + if (g_ascii_isupper (str[j])) + { + str[j] = g_ascii_tolower (str[j]); + } + else if (str[j] == ' ') + { + str[j] = '-'; + } + else if (str[j] == '/') + { + str[j] = '_'; + } + } + effects = g_strsplit (str, ",", 12); + for (i = 0; effects[i] != NULL; i++) + { + list = g_slist_append (list, effects[i]); + } + g_free (str); + } + gconf_client_set_list (priv->client, + CHEESE_GCONF_PREFIX "/selected_effects", + GCONF_VALUE_STRING, + list, + NULL); + g_slist_free (list); + g_strfreev (effects); + break; + case GCONF_PROP_X_RESOLUTION: + gconf_client_set_int (priv->client, + CHEESE_GCONF_PREFIX "/x_resolution", + g_value_get_int (value), + NULL); + break; + case GCONF_PROP_Y_RESOLUTION: + gconf_client_set_int (priv->client, + CHEESE_GCONF_PREFIX "/y_resolution", + g_value_get_int (value), + NULL); + break; + case GCONF_PROP_BRIGHTNESS: + gconf_client_set_float (priv->client, + CHEESE_GCONF_PREFIX "/brightness", + g_value_get_double (value), + NULL); + break; + case GCONF_PROP_CONTRAST: + gconf_client_set_float (priv->client, + CHEESE_GCONF_PREFIX "/contrast", + g_value_get_double (value), + NULL); + break; + case GCONF_PROP_SATURATION: + gconf_client_set_float (priv->client, + CHEESE_GCONF_PREFIX "/saturation", + g_value_get_double (value), + NULL); + break; + case GCONF_PROP_HUE: + gconf_client_set_float (priv->client, + CHEESE_GCONF_PREFIX "/hue", + g_value_get_double (value), + NULL); + break; + case GCONF_PROP_VIDEO_PATH: + gconf_client_set_string (priv->client, + CHEESE_GCONF_PREFIX "/video_path", + g_value_get_string (value), + NULL); + break; + case GCONF_PROP_PHOTO_PATH: + gconf_client_set_string (priv->client, + CHEESE_GCONF_PREFIX "/photo_path", + g_value_get_string (value), + NULL); + break; + case GCONF_PROP_ENABLE_DELETE: + gconf_client_set_bool (priv->client, + CHEESE_GCONF_PREFIX "/enable_delete", + g_value_get_boolean (value), + NULL); + break; + case GCONF_PROP_WIDE_MODE: + gconf_client_set_bool (priv->client, + CHEESE_GCONF_PREFIX "/wide_mode", + g_value_get_boolean (value), + NULL); + break; + case GCONF_PROP_BURST_DELAY: + gconf_client_set_int (priv->client, + CHEESE_GCONF_PREFIX "/burst_delay", + g_value_get_int (value), + NULL); + break; + case GCONF_PROP_BURST_REPEAT: + gconf_client_set_int (priv->client, + CHEESE_GCONF_PREFIX "/burst_repeat", + g_value_get_int (value), + NULL); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +cheese_gconf_finalize (GObject *object) +{ + CheeseGConf *gconf; + + gconf = CHEESE_GCONF (object); + CheeseGConfPrivate *priv = CHEESE_GCONF_GET_PRIVATE (gconf); + + g_object_unref (priv->client); + G_OBJECT_CLASS (cheese_gconf_parent_class)->finalize (object); +} + +static void +cheese_gconf_class_init (CheeseGConfClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = cheese_gconf_finalize; + + object_class->get_property = cheese_gconf_get_property; + object_class->set_property = cheese_gconf_set_property; + + g_object_class_install_property (object_class, GCONF_PROP_COUNTDOWN, + g_param_spec_boolean ("gconf_prop_countdown", + NULL, + NULL, + FALSE, + G_PARAM_READWRITE)); + g_object_class_install_property (object_class, GCONF_PROP_CAMERA, + g_param_spec_string ("gconf_prop_camera", + NULL, + NULL, + "", + G_PARAM_READWRITE)); + g_object_class_install_property (object_class, GCONF_PROP_SELECTED_EFFECTS, + g_param_spec_string ("gconf_prop_selected_effects", + NULL, + NULL, + "", + G_PARAM_READWRITE)); + + g_object_class_install_property (object_class, GCONF_PROP_X_RESOLUTION, + g_param_spec_int ("gconf_prop_x_resolution", + NULL, + NULL, + 0, + G_MAXINT, + 0, + G_PARAM_READWRITE)); + + g_object_class_install_property (object_class, GCONF_PROP_Y_RESOLUTION, + g_param_spec_int ("gconf_prop_y_resolution", + NULL, + NULL, + 0, + G_MAXINT, + 0, + G_PARAM_READWRITE)); + + g_object_class_install_property (object_class, GCONF_PROP_BRIGHTNESS, + g_param_spec_double ("gconf_prop_brightness", + NULL, + NULL, + -G_MAXFLOAT, + G_MAXFLOAT, + 0.0, + G_PARAM_READWRITE)); + + g_object_class_install_property (object_class, GCONF_PROP_CONTRAST, + g_param_spec_double ("gconf_prop_contrast", + NULL, + NULL, + 0, + G_MAXFLOAT, + 1.0, + G_PARAM_READWRITE)); + + g_object_class_install_property (object_class, GCONF_PROP_SATURATION, + g_param_spec_double ("gconf_prop_saturation", + NULL, + NULL, + 0, + G_MAXFLOAT, + 1.0, + G_PARAM_READWRITE)); + + g_object_class_install_property (object_class, GCONF_PROP_HUE, + g_param_spec_double ("gconf_prop_hue", + NULL, + NULL, + -G_MAXFLOAT, + G_MAXFLOAT, + 0.0, + G_PARAM_READWRITE)); + + g_object_class_install_property (object_class, GCONF_PROP_VIDEO_PATH, + g_param_spec_string ("gconf_prop_video_path", + NULL, + NULL, + "", + G_PARAM_READWRITE)); + + g_object_class_install_property (object_class, GCONF_PROP_PHOTO_PATH, + g_param_spec_string ("gconf_prop_photo_path", + NULL, + NULL, + "", + G_PARAM_READWRITE)); + + g_object_class_install_property (object_class, GCONF_PROP_ENABLE_DELETE, + g_param_spec_boolean ("gconf_prop_enable_delete", + NULL, + NULL, + FALSE, + G_PARAM_READWRITE)); + + g_object_class_install_property (object_class, GCONF_PROP_WIDE_MODE, + g_param_spec_boolean ("gconf_prop_wide_mode", + NULL, + NULL, + FALSE, + G_PARAM_READWRITE)); + + g_object_class_install_property (object_class, GCONF_PROP_BURST_DELAY, + g_param_spec_int ("gconf_prop_burst_delay", + NULL, + NULL, + 200, /* based on some experiments */ + G_MAXINT, + 1000, + G_PARAM_READWRITE)); + + g_object_class_install_property (object_class, GCONF_PROP_BURST_REPEAT, + g_param_spec_int ("gconf_prop_burst_repeat", + NULL, + NULL, + 1, + G_MAXINT, + 4, + G_PARAM_READWRITE)); + + g_type_class_add_private (klass, sizeof (CheeseGConfPrivate)); +} + +static void +cheese_gconf_init (CheeseGConf *gconf) +{ + CheeseGConfPrivate *priv = CHEESE_GCONF_GET_PRIVATE (gconf); + + priv->client = gconf_client_get_default (); +} + +CheeseGConf * +cheese_gconf_new () +{ + static CheeseGConf *gconf = NULL; + + if (gconf != NULL) + return g_object_ref (gconf); + + gconf = g_object_new (CHEESE_TYPE_GCONF, NULL); + g_object_add_weak_pointer (G_OBJECT (gconf), + (gpointer) & gconf); + + return gconf; +} diff --git a/src/thumbview/cheese-gconf.h b/src/thumbview/cheese-gconf.h new file mode 100644 index 00000000..9b9b308f --- /dev/null +++ b/src/thumbview/cheese-gconf.h @@ -0,0 +1,67 @@ +/* + * Copyright © 2007-2009 daniel g. siegel <dgsiegel@gnome.org> + * + * Licensed under the GNU General Public License Version 2 + * + * 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 of the License, 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, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __CHEESE_GCONF_H__ +#define __CHEESE_GCONF_H__ + +G_BEGIN_DECLS + +#define CHEESE_TYPE_GCONF (cheese_gconf_get_type ()) +#define CHEESE_GCONF(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CHEESE_TYPE_GCONF, CheeseGConf)) +#define CHEESE_GCONF_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CHEESE_TYPE_GCONF, CheeseGConfClass)) +#define CHEESE_IS_GCONF(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CHEESE_TYPE_GCONF)) +#define CHEESE_IS_GCONF_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CHEESE_TYPE_GCONF)) +#define CHEESE_GCONF_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CHEESE_TYPE_GCONF, CheeseGConfClass)) + +typedef struct +{ + GObject parent; +} CheeseGConf; + +typedef struct +{ + GObjectClass parent_class; +} CheeseGConfClass; + +enum +{ + GCONF_PROP_0, + GCONF_PROP_COUNTDOWN, + GCONF_PROP_CAMERA, + GCONF_PROP_SELECTED_EFFECTS, + GCONF_PROP_X_RESOLUTION, + GCONF_PROP_Y_RESOLUTION, + GCONF_PROP_BRIGHTNESS, + GCONF_PROP_CONTRAST, + GCONF_PROP_SATURATION, + GCONF_PROP_HUE, + GCONF_PROP_VIDEO_PATH, + GCONF_PROP_PHOTO_PATH, + GCONF_PROP_ENABLE_DELETE, + GCONF_PROP_WIDE_MODE, + GCONF_PROP_BURST_DELAY, + GCONF_PROP_BURST_REPEAT +}; + +GType cheese_gconf_get_type (void); +CheeseGConf *cheese_gconf_new (); + +G_END_DECLS + +#endif /* __CHEESE_GCONF_H__ */ diff --git a/src/thumbview/cheese-thumb-view.c b/src/thumbview/cheese-thumb-view.c new file mode 100644 index 00000000..18c1fb78 --- /dev/null +++ b/src/thumbview/cheese-thumb-view.c @@ -0,0 +1,674 @@ +/* + * Copyright © 2007,2008 daniel g. siegel <dgsiegel@gnome.org> + * Copyright © 2007,2008 Jaap Haitsma <jaap@haitsma.org> + * Copyright © 2008 Filippo Argiolas <filippo.argiolas@gmail.com> + * + * Licensed under the GNU General Public License Version 2 + * + * 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 of the License, 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, see <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H + #include "cheese-config.h" +#endif + +#include <glib.h> +#include <gtk/gtk.h> +#include <libgnomeui/gnome-desktop-thumbnail.h> +#include <string.h> + +#include "cheese-fileutil.h" +#include "eog-thumbnail.h" + +#include "cheese-thumb-view.h" + +#define THUMB_VIEW_MINIMUM_WIDTH 140 +#define THUMB_VIEW_MINIMUM_HEIGHT 100 + +#define CHEESE_THUMB_VIEW_GET_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((o), CHEESE_TYPE_THUMB_VIEW, CheeseThumbViewPrivate)) + +G_DEFINE_TYPE (CheeseThumbView, cheese_thumb_view, GTK_TYPE_ICON_VIEW); + +typedef struct +{ + GtkListStore *store; + CheeseFileUtil *fileutil; + GFileMonitor *photo_file_monitor; + GFileMonitor *video_file_monitor; + GnomeDesktopThumbnailFactory *factory; + gboolean multiplex_thumbnail_generator; + guint n_items; +} CheeseThumbViewPrivate; + +enum +{ + THUMBNAIL_PIXBUF_COLUMN, + THUMBNAIL_URL_COLUMN, + THUMBNAIL_BASENAME_URL_COLUMN +}; + +/* Drag 'n Drop */ +enum +{ + TARGET_PLAIN, + TARGET_PLAIN_UTF8, + TARGET_URILIST, +}; + +static GtkTargetEntry target_table[] = { + {"text/uri-list", 0, TARGET_URILIST}, +}; + +typedef struct +{ + CheeseThumbView *thumb_view; + GFile *file; + GtkTreeIter iter; +} CheeseThumbViewThreadData; + +static void +cheese_thumb_view_thread_append_item (gpointer data) +{ + CheeseThumbViewThreadData *item = data; + CheeseThumbView *thumb_view = item->thumb_view; + CheeseThumbViewPrivate *priv = CHEESE_THUMB_VIEW_GET_PRIVATE (thumb_view); + + GnomeDesktopThumbnailFactory *factory = priv->factory; + GFile *file = item->file; + GtkTreeIter iter = item->iter; + GdkPixbuf *pixbuf = NULL; + GFileInfo *info; + char *thumb_loc; + GTimeVal mtime; + char *mime_type; + char *uri; + char *filename; + + info = g_file_query_info (file, "standard::content-type,time::modified", 0, NULL, NULL); + + if (!info) + { + g_warning ("Invalid filename\n"); + return; + } + g_file_info_get_modification_time (info, &mtime); + mime_type = g_strdup (g_file_info_get_content_type (info)); + + uri = g_file_get_uri (file); + filename = g_file_get_path (file); + + thumb_loc = gnome_desktop_thumbnail_factory_lookup (factory, uri, mtime.tv_sec); + + if (!thumb_loc) + { + pixbuf = gnome_desktop_thumbnail_factory_generate_thumbnail (factory, uri, mime_type); + if (!pixbuf) + { + g_warning ("could not generate thumbnail for %s (%s)\n", filename, mime_type); + } + else + { + gnome_desktop_thumbnail_factory_save_thumbnail (factory, pixbuf, uri, mtime.tv_sec); + } + } + else + { + pixbuf = gdk_pixbuf_new_from_file (thumb_loc, NULL); + if (!pixbuf) + { + g_warning ("could not load thumbnail %s (%s)\n", filename, mime_type); + } + } + g_object_unref (info); + g_free (thumb_loc); + g_free (uri); + + if (!pixbuf) + { + gchar *escape = NULL; + GError *error = NULL; + escape = g_strrstr (mime_type, "/"); + if (escape != NULL) *escape = '-'; + pixbuf = gtk_icon_theme_load_icon (gtk_icon_theme_get_default (), + mime_type, + 96, + GTK_ICON_LOOKUP_GENERIC_FALLBACK, + &error); + if (error) + { + g_warning ("%s", error->message); + return; + } + } + else + { + eog_thumbnail_add_frame (&pixbuf); + } + + gdk_threads_enter (); + + gtk_list_store_set (priv->store, &iter, + THUMBNAIL_PIXBUF_COLUMN, pixbuf, -1); + + gdk_threads_leave (); + + g_free (mime_type); + g_free (filename); + g_object_unref (pixbuf); + g_object_unref (file); + g_free (item); +} + +static void +cheese_thumb_view_append_item (CheeseThumbView *thumb_view, GFile *file) +{ + CheeseThumbViewPrivate *priv = CHEESE_THUMB_VIEW_GET_PRIVATE (thumb_view); + + GtkTreeIter iter; + GtkIconTheme *icon_theme; + GdkPixbuf *pixbuf = NULL; + GtkTreePath *path; + char *filename, *basename, *col_filename; + GError *error = NULL; + gboolean skip = FALSE; + + CheeseThumbViewThreadData *data; + + filename = g_file_get_path (file); + + if (!(g_str_has_suffix (filename, PHOTO_NAME_SUFFIX)) && !(g_str_has_suffix (filename, VIDEO_NAME_SUFFIX))) + { + g_free (filename); + return; + } + + if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (priv->store), &iter)) + { + /* check if the selected item is the first, else go through the store */ + gtk_tree_model_get (GTK_TREE_MODEL (priv->store), &iter, THUMBNAIL_URL_COLUMN, &col_filename, -1); + if (g_ascii_strcasecmp (col_filename, filename)) + { + while (gtk_tree_model_iter_next (GTK_TREE_MODEL (priv->store), &iter)) + { + gtk_tree_model_get (GTK_TREE_MODEL (priv->store), &iter, THUMBNAIL_URL_COLUMN, &col_filename, -1); + if (!g_ascii_strcasecmp (col_filename, filename)) + { + skip = TRUE; + break; + } + } + } + else + { + skip = TRUE; + } + g_free (col_filename); + g_free (filename); + + if (skip) return; + } + + if (priv->multiplex_thumbnail_generator) + { + char *f; + + f = g_strdup_printf ("%s/pixmaps/cheese-%i.svg", PACKAGE_DATADIR, g_random_int_range (1, 4)); + pixbuf = gdk_pixbuf_new_from_file (f, NULL); + g_free (f); + } + else + { + icon_theme = gtk_icon_theme_get_default (); + pixbuf = gtk_icon_theme_load_icon (icon_theme, + "image-loading", + 96, + GTK_ICON_LOOKUP_GENERIC_FALLBACK, + &error); + } + + if (!pixbuf) + { + g_warning ("Couldn't load icon: %s", error->message); + g_error_free (error); + error = NULL; + } + + filename = g_file_get_path (file); + basename = g_path_get_basename (filename); + + gtk_list_store_append (priv->store, &iter); + gtk_list_store_set (priv->store, &iter, + THUMBNAIL_PIXBUF_COLUMN, pixbuf, + THUMBNAIL_URL_COLUMN, filename, + THUMBNAIL_BASENAME_URL_COLUMN, basename, -1); + g_free (filename); + g_free (basename); + path = gtk_tree_model_get_path (GTK_TREE_MODEL (priv->store), &iter); + gtk_icon_view_scroll_to_path (GTK_ICON_VIEW (thumb_view), path, + TRUE, 1.0, 0.5); + + if (pixbuf) g_object_unref (pixbuf); + + if (!priv->multiplex_thumbnail_generator) + { + data = g_new0 (CheeseThumbViewThreadData, 1); + data->thumb_view = g_object_ref (thumb_view); + data->file = g_object_ref (file); + data->iter = iter; + + if (!g_thread_create ((GThreadFunc) cheese_thumb_view_thread_append_item, + data, FALSE, &error)) + { + g_error ("Failed to create thumbnail thread: %s\n", error->message); + g_error_free (error); + return; + } + } +} + +void +cheese_thumb_view_remove_item (CheeseThumbView *thumb_view, GFile *file) +{ + CheeseThumbViewPrivate *priv = CHEESE_THUMB_VIEW_GET_PRIVATE (thumb_view); + + char *path; + GtkTreeIter iter; + char *filename; + gboolean found = FALSE; + + filename = g_file_get_path (file); + + if (!gtk_tree_model_get_iter_first (GTK_TREE_MODEL (priv->store), &iter)) + { + /* a single item was on the thumbview but it's been already removed */ + return; + } + + /* check if the selected item is the first, else go through the store */ + gtk_tree_model_get (GTK_TREE_MODEL (priv->store), &iter, THUMBNAIL_URL_COLUMN, &path, -1); + if (g_ascii_strcasecmp (path, filename)) + { + while (gtk_tree_model_iter_next (GTK_TREE_MODEL (priv->store), &iter)) + { + gtk_tree_model_get (GTK_TREE_MODEL (priv->store), &iter, THUMBNAIL_URL_COLUMN, &path, -1); + if (!g_ascii_strcasecmp (path, filename)) + { + found = TRUE; + break; + } + } + } + else + { + found = TRUE; + } + g_free (path); + g_free (filename); + + if (!found) return; + + gboolean valid = gtk_list_store_remove (priv->store, &iter); + if (!valid) + { + int len = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (priv->store), NULL); + if (len <= 0) + return; + + valid = gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (priv->store), &iter, NULL, len - 1); + } + GtkTreePath *tree_path = gtk_tree_model_get_path (GTK_TREE_MODEL (priv->store), &iter); + gtk_icon_view_select_path (GTK_ICON_VIEW (thumb_view), tree_path); + gtk_tree_path_free (tree_path); +} + +static void +cheese_thumb_view_monitor_cb (GFileMonitor *file_monitor, + GFile *file, + GFile *other_file, + GFileMonitorEvent event_type, + CheeseThumbView *thumb_view) +{ + switch (event_type) + { + case G_FILE_MONITOR_EVENT_DELETED: + cheese_thumb_view_remove_item (thumb_view, file); + break; + case G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT: + cheese_thumb_view_append_item (thumb_view, file); + break; + default: + break; + } +} + +static void +cheese_thumb_view_on_drag_data_get_cb (GtkIconView *thumb_view, + GdkDragContext *drag_context, + GtkSelectionData *data, + guint info, + guint time, + gpointer user_data) +{ + GList *list, *l; + GtkTreeIter iter; + GtkTreeModel *model; + char *str; + char *uris = NULL; + char *tmp_str; + + list = gtk_icon_view_get_selected_items (thumb_view); + model = gtk_icon_view_get_model (thumb_view); + + for (l = list; l != NULL; l = l->next) + { + gtk_tree_model_get_iter (model, &iter, l->data); + gtk_tree_model_get (model, &iter, 1, &str, -1); + gtk_tree_path_free (l->data); + + /* we always store local paths in the model, but DnD + * needs URIs, so we must add file:// to the path. + */ + + /* build the "text/uri-list" string */ + if (uris) + { + tmp_str = g_strconcat (uris, "file://", str, "\r\n", NULL); + g_free (uris); + } + else + { + tmp_str = g_strconcat ("file://", str, "\r\n", NULL); + } + uris = tmp_str; + + g_free (str); + } + gtk_selection_data_set (data, gtk_selection_data_get_target (data), + 8, (guchar *) uris, strlen (uris)); + g_free (uris); + g_list_free (list); +} + +static char * +cheese_thumb_view_get_url_from_path (CheeseThumbView *thumb_view, GtkTreePath *path) +{ + GtkTreeModel *model; + GtkTreeIter iter; + char *file; + + model = gtk_icon_view_get_model (GTK_ICON_VIEW (thumb_view)); + gtk_tree_model_get_iter (model, &iter, path); + + gtk_tree_model_get (model, &iter, THUMBNAIL_URL_COLUMN, &file, -1); + + return file; +} + +char * +cheese_thumb_view_get_selected_image (CheeseThumbView *thumb_view) +{ + GList *list; + char *filename = NULL; + + list = gtk_icon_view_get_selected_items (GTK_ICON_VIEW (thumb_view)); + if (list) + { + filename = cheese_thumb_view_get_url_from_path (thumb_view, (GtkTreePath *) list->data); + g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL); + g_list_free (list); + } + + return filename; +} + +GList * +cheese_thumb_view_get_selected_images_list (CheeseThumbView *thumb_view) +{ + GList *l, *item; + GList *list = NULL; + GFile *file; + + GtkTreePath *path; + + l = gtk_icon_view_get_selected_items (GTK_ICON_VIEW (thumb_view)); + + for (item = l; item != NULL; item = item->next) + { + path = (GtkTreePath *) item->data; + file = g_file_new_for_path (cheese_thumb_view_get_url_from_path (thumb_view, path)); + list = g_list_prepend (list, file); + gtk_tree_path_free (path); + } + + g_list_free (l); + list = g_list_reverse (list); + + return list; +} + +static void +cheese_thumb_view_get_n_selected_helper (GtkIconView *thumbview, + GtkTreePath *path, + gpointer data) +{ + /* data is of type (guint *) */ + (*(guint *) data)++; +} + +guint +cheese_thumb_view_get_n_selected (CheeseThumbView *thumbview) +{ + guint count = 0; + + gtk_icon_view_selected_foreach (GTK_ICON_VIEW (thumbview), + cheese_thumb_view_get_n_selected_helper, + (&count)); + return count; +} + +static void +cheese_thumb_view_fill (CheeseThumbView *thumb_view) +{ + CheeseThumbViewPrivate *priv = CHEESE_THUMB_VIEW_GET_PRIVATE (thumb_view); + + GDir *dir_videos, *dir_photos; + char *path_videos, *path_photos; + const char *name; + char *filename; + GFile *file; + + gtk_list_store_clear (priv->store); + + path_videos = cheese_fileutil_get_video_path (priv->fileutil); + path_photos = cheese_fileutil_get_photo_path (priv->fileutil); + + dir_videos = g_dir_open (path_videos, 0, NULL); + dir_photos = g_dir_open (path_photos, 0, NULL); + + if (!dir_videos && !dir_photos) + return; + + priv->multiplex_thumbnail_generator = FALSE; + char *multiplex_file = g_build_filename (path_photos, "cheese, cheese, cheese! all i want is cheese", NULL); + if (g_file_test (multiplex_file, G_FILE_TEST_EXISTS)) + priv->multiplex_thumbnail_generator = !priv->multiplex_thumbnail_generator; + g_free (multiplex_file); + + /* read videos from the vid directory */ + while ((name = g_dir_read_name (dir_videos))) + { + if (!(g_str_has_suffix (name, VIDEO_NAME_SUFFIX))) + continue; + + filename = g_build_filename (path_videos, name, NULL); + file = g_file_new_for_path (filename); + cheese_thumb_view_append_item (thumb_view, file); + g_free (filename); + g_object_unref (file); + } + g_dir_close (dir_videos); + + /* read photos from the photo directory */ + while ((name = g_dir_read_name (dir_photos))) + { + if (!(g_str_has_suffix (name, PHOTO_NAME_SUFFIX))) + continue; + + filename = g_build_filename (path_photos, name, NULL); + file = g_file_new_for_path (filename); + cheese_thumb_view_append_item (thumb_view, file); + g_free (filename); + g_object_unref (file); + } + g_dir_close (dir_photos); +} + +static void +cheese_thumb_view_finalize (GObject *object) +{ + CheeseThumbView *thumb_view = CHEESE_THUMB_VIEW (object); + CheeseThumbViewPrivate *priv = CHEESE_THUMB_VIEW_GET_PRIVATE (thumb_view); + + g_object_unref (priv->store); + g_object_unref (priv->fileutil); + g_object_unref (priv->factory); + g_file_monitor_cancel (priv->photo_file_monitor); + g_file_monitor_cancel (priv->video_file_monitor); + + G_OBJECT_CLASS (cheese_thumb_view_parent_class)->finalize (object); +} + +static void +cheese_thumb_view_class_init (CheeseThumbViewClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = cheese_thumb_view_finalize; + + g_type_class_add_private (klass, sizeof (CheeseThumbViewPrivate)); +} + +static void +cheese_thumb_view_row_inserted_cb (GtkTreeModel *tree_model, + GtkTreePath *path, + GtkTreeIter *iter, + CheeseThumbView *thumb_view) +{ + CheeseThumbViewPrivate *priv = CHEESE_THUMB_VIEW_GET_PRIVATE (thumb_view); + + priv->n_items++; + gtk_widget_set_size_request (GTK_WIDGET (thumb_view), -1, -1); +} + +static void +cheese_thumb_view_row_deleted_cb (GtkTreeModel *tree_model, + GtkTreePath *path, + CheeseThumbView *thumb_view) +{ + CheeseThumbViewPrivate *priv = CHEESE_THUMB_VIEW_GET_PRIVATE (thumb_view); + + priv->n_items--; + if (priv->n_items == 0) + gtk_widget_set_size_request (GTK_WIDGET (thumb_view), + THUMB_VIEW_MINIMUM_WIDTH, + THUMB_VIEW_MINIMUM_HEIGHT); +} + +static void +cheese_thumb_view_init (CheeseThumbView *thumb_view) +{ + CheeseThumbViewPrivate *priv = CHEESE_THUMB_VIEW_GET_PRIVATE (thumb_view); + + char *path_videos = NULL, *path_photos = NULL; + + GFile *file; + + eog_thumbnail_init (); + + priv->store = gtk_list_store_new (3, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING); + priv->n_items = 0; + + g_signal_connect (G_OBJECT (priv->store), + "row-inserted", + G_CALLBACK (cheese_thumb_view_row_inserted_cb), + thumb_view); + g_signal_connect (G_OBJECT (priv->store), + "row-deleted", + G_CALLBACK (cheese_thumb_view_row_deleted_cb), + thumb_view); + + priv->fileutil = cheese_fileutil_new (); + + gtk_icon_view_set_model (GTK_ICON_VIEW (thumb_view), GTK_TREE_MODEL (priv->store)); + + gtk_widget_set_size_request (GTK_WIDGET (thumb_view), + THUMB_VIEW_MINIMUM_WIDTH, + THUMB_VIEW_MINIMUM_HEIGHT); + + gtk_icon_view_set_margin (GTK_ICON_VIEW (thumb_view), 0); + gtk_icon_view_set_row_spacing (GTK_ICON_VIEW (thumb_view), 0); + gtk_icon_view_set_column_spacing (GTK_ICON_VIEW (thumb_view), 0); + + path_videos = cheese_fileutil_get_video_path (priv->fileutil); + path_photos = cheese_fileutil_get_photo_path (priv->fileutil); + + g_mkdir_with_parents (path_videos, 0775); + g_mkdir_with_parents (path_photos, 0775); + + priv->factory = gnome_desktop_thumbnail_factory_new (GNOME_DESKTOP_THUMBNAIL_SIZE_NORMAL); + + /* connect signal to video path */ + file = g_file_new_for_path (path_videos); + priv->video_file_monitor = g_file_monitor_directory (file, 0, NULL, NULL); + g_signal_connect (priv->video_file_monitor, "changed", G_CALLBACK (cheese_thumb_view_monitor_cb), thumb_view); + + /* if both paths are the same, make only one file monitor and point twice to the file monitor (photo_file_monitor = video_file_monitor) */ + if (strcmp (path_videos, path_photos) != 0) + { + /* connect signal to photo path */ + file = g_file_new_for_path (path_photos); + priv->photo_file_monitor = g_file_monitor_directory (file, 0, NULL, NULL); + g_signal_connect (priv->photo_file_monitor, "changed", G_CALLBACK (cheese_thumb_view_monitor_cb), thumb_view); + } + else + { + priv->photo_file_monitor = priv->video_file_monitor; + } + + gtk_icon_view_set_pixbuf_column (GTK_ICON_VIEW (thumb_view), 0); + + gtk_icon_view_set_columns (GTK_ICON_VIEW (thumb_view), G_MAXINT); + + gtk_icon_view_enable_model_drag_source (GTK_ICON_VIEW (thumb_view), GDK_BUTTON1_MASK, + target_table, G_N_ELEMENTS (target_table), + GDK_ACTION_COPY); + gtk_icon_view_set_selection_mode (GTK_ICON_VIEW (thumb_view), GTK_SELECTION_MULTIPLE); + g_signal_connect (G_OBJECT (thumb_view), "drag-data-get", + G_CALLBACK (cheese_thumb_view_on_drag_data_get_cb), NULL); + + gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (priv->store), + THUMBNAIL_BASENAME_URL_COLUMN, GTK_SORT_ASCENDING); + + cheese_thumb_view_fill (thumb_view); +} + +GtkWidget * +cheese_thumb_view_new () +{ + CheeseThumbView *thumb_view; + + thumb_view = g_object_new (CHEESE_TYPE_THUMB_VIEW, NULL); + return GTK_WIDGET (thumb_view); +} diff --git a/src/thumbview/cheese-thumb-view.h b/src/thumbview/cheese-thumb-view.h new file mode 100644 index 00000000..875e073b --- /dev/null +++ b/src/thumbview/cheese-thumb-view.h @@ -0,0 +1,60 @@ +/* + * Copyright © 2007,2008 daniel g. siegel <dgsiegel@gnome.org> + * Copyright © 2007,2008 Jaap Haitsma <jaap@haitsma.org> + * + * Licensed under the GNU General Public License Version 2 + * + * 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 of the License, 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, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __CHEESE_THUMB_VIEW_H__ +#define __CHEESE_THUMB_VIEW_H__ + +#include <glib.h> +#include <gtk/gtk.h> +#include <gio/gio.h> + +G_BEGIN_DECLS + +#define CHEESE_TYPE_THUMB_VIEW (cheese_thumb_view_get_type ()) +#define CHEESE_THUMB_VIEW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CHEESE_TYPE_THUMB_VIEW, CheeseThumbView)) +#define CHEESE_THUMB_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CHEESE_TYPE_THUMB_VIEW, \ + CheeseThumbViewClass)) +#define CHEESE_IS_THUMB_VIEW(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CHEESE_TYPE_THUMB_VIEW)) +#define CHEESE_IS_THUMB_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CHEESE_TYPE_THUMB_VIEW)) +#define CHEESE_THUMB_VIEW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CHEESE_TYPE_THUMB_VIEW, \ + CheeseThumbViewClass)) + +typedef struct +{ + GtkIconView parent; +} CheeseThumbView; + +typedef struct +{ + GtkIconViewClass parent_class; +} CheeseThumbViewClass; + + +GType cheese_thumb_view_get_type (void); +GtkWidget *cheese_thumb_view_new (); + +GList *cheese_thumb_view_get_selected_images_list (CheeseThumbView *thumb_view); +char * cheese_thumb_view_get_selected_image (CheeseThumbView *thumb_view); +guint cheese_thumb_view_get_n_selected (CheeseThumbView *thumbview); +void cheese_thumb_view_remove_item (CheeseThumbView *thumb_view, GFile *file); + +G_END_DECLS + +#endif /* __CHEESE_THUMB_VIEW_H__ */ diff --git a/src/thumbview/eog-thumb-nav.c b/src/thumbview/eog-thumb-nav.c new file mode 100644 index 00000000..87b33053 --- /dev/null +++ b/src/thumbview/eog-thumb-nav.c @@ -0,0 +1,750 @@ +/* Eye Of Gnome - Thumbnail Navigator + * + * Copyright (C) 2006 The Free Software Foundation + * + * Author: Lucas Rocha <lucasr@gnome.org> + * + * 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 of the License, 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 <cheese-config.h> +#endif + +#include "eog-thumb-nav.h" +#include "cheese-thumb-view.h" + +#include <glib.h> +#include <glib/gi18n.h> +#include <glib-object.h> +#include <gtk/gtk.h> +#include <string.h> + +#define EOG_THUMB_NAV_GET_PRIVATE(object) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((object), EOG_TYPE_THUMB_NAV, EogThumbNavPrivate)) + +G_DEFINE_TYPE (EogThumbNav, eog_thumb_nav, GTK_TYPE_HBOX); + +#define EOG_THUMB_NAV_SCROLL_INC 20 +#define EOG_THUMB_NAV_SCROLL_MOVE 20 +#define EOG_THUMB_NAV_SCROLL_TIMEOUT 20 + +enum +{ + PROP_SHOW_BUTTONS = 1, + PROP_THUMB_VIEW, + PROP_MODE +}; + +struct _EogThumbNavPrivate +{ + gboolean show_buttons; + gboolean vertical; + gboolean scroll_dir; + gint scroll_pos; + gint scroll_id; + + GtkWidget *button_up; + GtkWidget *button_down; + GtkWidget *button_left; + GtkWidget *button_right; + GtkWidget *sw; + GtkWidget *thumbview; + GtkWidget *vbox; + GtkAdjustment *hadj; + GtkAdjustment *vadj; + GtkAdjustment *adj; +}; + +static gboolean +eog_thumb_nav_scroll_event (GtkWidget *widget, GdkEventScroll *event, gpointer user_data) +{ + EogThumbNav *nav = EOG_THUMB_NAV (user_data); + gint inc = EOG_THUMB_NAV_SCROLL_INC * 3; + gdouble value, upper, page_size; + + nav->priv->adj = nav->priv->vertical ? nav->priv->vadj : nav->priv->hadj; + + switch (event->direction) + { + case GDK_SCROLL_UP: + case GDK_SCROLL_LEFT: + inc *= -1; + break; + + case GDK_SCROLL_DOWN: + case GDK_SCROLL_RIGHT: + break; + + default: + g_assert_not_reached (); + return FALSE; + } + + value = gtk_adjustment_get_value (nav->priv->adj); + if (inc < 0) + gtk_adjustment_set_value (nav->priv->adj, MAX (0, value + inc)); + else + { + upper = gtk_adjustment_get_upper (nav->priv->adj); + page_size = gtk_adjustment_get_page_size (nav->priv->adj); + gtk_adjustment_set_value (nav->priv->adj, MIN (upper - page_size, value + inc)); + } + + gtk_adjustment_value_changed (nav->priv->adj); + + return TRUE; +} + +static void +eog_thumb_nav_vadj_changed (GtkAdjustment *vadj, gpointer user_data) +{ + EogThumbNav *nav; + EogThumbNavPrivate *priv; + gboolean ltr; + gdouble value, upper, page_size; + + nav = EOG_THUMB_NAV (user_data); + priv = EOG_THUMB_NAV_GET_PRIVATE (nav); + ltr = gtk_widget_get_direction (priv->sw) == GTK_TEXT_DIR_LTR; + + g_object_get (vadj, + "value", &value, + "upper", &upper, + "page_size", &page_size, + NULL); + gtk_widget_set_sensitive (priv->button_up, value > 0); + + gtk_widget_set_sensitive (priv->button_down, + value < upper - page_size); +} + +static void +eog_thumb_nav_hadj_changed (GtkAdjustment *hadj, gpointer user_data) +{ + EogThumbNav *nav; + EogThumbNavPrivate *priv; + gboolean ltr; + gdouble value, upper, page_size; + + nav = EOG_THUMB_NAV (user_data); + priv = EOG_THUMB_NAV_GET_PRIVATE (nav); + ltr = gtk_widget_get_direction (priv->sw) == GTK_TEXT_DIR_LTR; + + g_object_get (hadj, + "value", &value, + "upper", &upper, + "page_size", &page_size, + NULL); + + gtk_widget_set_sensitive (ltr ? priv->button_right : priv->button_left, + value < upper - page_size); +} + +static void +eog_thumb_nav_vadj_value_changed (GtkAdjustment *vadj, gpointer user_data) +{ + EogThumbNav *nav; + EogThumbNavPrivate *priv; + gboolean ltr; + gdouble value, upper, page_size; + + nav = EOG_THUMB_NAV (user_data); + priv = EOG_THUMB_NAV_GET_PRIVATE (nav); + ltr = gtk_widget_get_direction (priv->sw) == GTK_TEXT_DIR_LTR; + + g_object_get (vadj, + "value", &value, + "upper", &upper, + "page_size", &page_size, + NULL); + + gtk_widget_set_sensitive (priv->button_up, value > 0); + + gtk_widget_set_sensitive (priv->button_down, + value < upper - page_size); +} + +static void +eog_thumb_nav_hadj_value_changed (GtkAdjustment *hadj, gpointer user_data) +{ + EogThumbNav *nav; + EogThumbNavPrivate *priv; + gboolean ltr; + gdouble value, upper, page_size; + + nav = EOG_THUMB_NAV (user_data); + priv = EOG_THUMB_NAV_GET_PRIVATE (nav); + ltr = gtk_widget_get_direction (priv->sw) == GTK_TEXT_DIR_LTR; + + g_object_get (hadj, + "value", &value, + "upper", &upper, + "page_size", &page_size, + NULL); + + gtk_widget_set_sensitive (ltr ? priv->button_left : priv->button_right, value > 0); + + gtk_widget_set_sensitive (ltr ? priv->button_right : priv->button_left, + value < upper - page_size); +} + +static gboolean +eog_thumb_nav_scroll_step (gpointer user_data) +{ + EogThumbNav *nav = EOG_THUMB_NAV (user_data); + gint delta; + gdouble value, upper, page_size; + + if (nav->priv->scroll_pos < 10) + delta = EOG_THUMB_NAV_SCROLL_INC; + else if (nav->priv->scroll_pos < 20) + delta = EOG_THUMB_NAV_SCROLL_INC * 2; + else if (nav->priv->scroll_pos < 30) + delta = EOG_THUMB_NAV_SCROLL_INC * 2 + 5; + else + delta = EOG_THUMB_NAV_SCROLL_INC * 2 + 12; + + if (!nav->priv->scroll_dir) + delta *= -1; + + g_object_get (nav->priv->adj, + "value", &value, + "upper", &upper, + "page_size", &page_size, + NULL); + + if ((gint) (value + (gdouble) delta) >= 0 && + (gint) (value + (gdouble) delta) <= upper - page_size) + { + gtk_adjustment_set_value (nav->priv->adj, value + (gdouble) delta); + nav->priv->scroll_pos++; + gtk_adjustment_value_changed (nav->priv->adj); + } + else + { + if (delta > 0) + gtk_adjustment_set_value (nav->priv->adj, upper - page_size); + else + gtk_adjustment_set_value (nav->priv->adj, 0); + + nav->priv->scroll_pos = 0; + + gtk_adjustment_value_changed (nav->priv->adj); + + return FALSE; + } + + return TRUE; +} + +static void +eog_thumb_nav_button_clicked (GtkButton *button, EogThumbNav *nav) +{ + nav->priv->scroll_pos = 0; + + if ((GTK_WIDGET (button) == nav->priv->button_right) || + (GTK_WIDGET (button) == nav->priv->button_left)) + { + nav->priv->scroll_dir = gtk_widget_get_direction (GTK_WIDGET (button)) == GTK_TEXT_DIR_LTR ? + GTK_WIDGET (button) == nav->priv->button_right : + GTK_WIDGET (button) == nav->priv->button_left; + } + else + { + nav->priv->scroll_dir = (GTK_WIDGET (button) == nav->priv->button_down); + } + + nav->priv->adj = ((GTK_WIDGET (button) == nav->priv->button_right) || + (GTK_WIDGET (button) == nav->priv->button_left)) ? nav->priv->hadj : nav->priv->vadj; + + eog_thumb_nav_scroll_step (nav); +} + +static void +eog_thumb_nav_start_scroll (GtkButton *button, EogThumbNav *nav) +{ + if ((GTK_WIDGET (button) == nav->priv->button_right) || + (GTK_WIDGET (button) == nav->priv->button_left)) + { + nav->priv->scroll_dir = gtk_widget_get_direction (GTK_WIDGET (button)) == GTK_TEXT_DIR_LTR ? + GTK_WIDGET (button) == nav->priv->button_right : + GTK_WIDGET (button) == nav->priv->button_left; + } + else + { + nav->priv->scroll_dir = (GTK_WIDGET (button) == nav->priv->button_down); + } + + nav->priv->adj = ((GTK_WIDGET (button) == nav->priv->button_right) || + (GTK_WIDGET (button) == nav->priv->button_left)) ? nav->priv->hadj : nav->priv->vadj; + + nav->priv->scroll_id = g_timeout_add (EOG_THUMB_NAV_SCROLL_TIMEOUT, + eog_thumb_nav_scroll_step, + nav); +} + +static void +eog_thumb_nav_stop_scroll (GtkButton *button, EogThumbNav *nav) +{ + if (nav->priv->scroll_id > 0) + { + g_source_remove (nav->priv->scroll_id); + nav->priv->scroll_id = 0; + nav->priv->scroll_pos = 0; + } +} + +static void +eog_thumb_nav_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + EogThumbNav *nav = EOG_THUMB_NAV (object); + + switch (property_id) + { + case PROP_SHOW_BUTTONS: + g_value_set_boolean (value, + eog_thumb_nav_get_show_buttons (nav)); + break; + + case PROP_THUMB_VIEW: + g_value_set_object (value, nav->priv->thumbview); + break; + } +} + +static void +eog_thumb_nav_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + EogThumbNav *nav = EOG_THUMB_NAV (object); + + switch (property_id) + { + case PROP_SHOW_BUTTONS: + eog_thumb_nav_set_show_buttons (nav, + g_value_get_boolean (value)); + break; + + case PROP_THUMB_VIEW: + nav->priv->thumbview = + GTK_WIDGET (g_value_get_object (value)); + break; + } +} + +static GObject * +eog_thumb_nav_constructor (GType type, + guint n_construct_properties, + GObjectConstructParam *construct_params) +{ + GObject *object; + EogThumbNav *nav; + EogThumbNavPrivate *priv; + + object = G_OBJECT_CLASS (eog_thumb_nav_parent_class)->constructor + (type, n_construct_properties, construct_params); + + nav = EOG_THUMB_NAV (object); + priv = EOG_THUMB_NAV_GET_PRIVATE (object); + + if (priv->thumbview != NULL) + { + gtk_container_add (GTK_CONTAINER (priv->sw), priv->thumbview); + gtk_widget_show_all (priv->sw); + } + + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->sw), + GTK_POLICY_AUTOMATIC, + GTK_POLICY_NEVER); + + return object; +} + +static void +eog_thumb_nav_class_init (EogThumbNavClass *class) +{ + GObjectClass *g_object_class = (GObjectClass *) class; + + g_object_class->constructor = eog_thumb_nav_constructor; + g_object_class->get_property = eog_thumb_nav_get_property; + g_object_class->set_property = eog_thumb_nav_set_property; + + g_object_class_install_property (g_object_class, + PROP_SHOW_BUTTONS, + g_param_spec_boolean ("show-buttons", + "Show Buttons", + "Whether to show navigation buttons or not", + TRUE, + (G_PARAM_READABLE | G_PARAM_WRITABLE))); + + g_object_class_install_property (g_object_class, + PROP_THUMB_VIEW, + g_param_spec_object ("thumbview", + "Thumbnail View", + "The internal thumbnail viewer widget", + CHEESE_TYPE_THUMB_VIEW, + (G_PARAM_CONSTRUCT_ONLY | + G_PARAM_READABLE | + G_PARAM_WRITABLE))); + + g_type_class_add_private (g_object_class, sizeof (EogThumbNavPrivate)); +} + +static void +eog_thumb_nav_init (EogThumbNav *nav) +{ + EogThumbNavPrivate *priv; + GtkWidget *arrow; + + nav->priv = EOG_THUMB_NAV_GET_PRIVATE (nav); + + priv = nav->priv; + + priv->show_buttons = TRUE; + priv->vertical = FALSE; + + priv->button_left = gtk_button_new (); + gtk_button_set_relief (GTK_BUTTON (priv->button_left), GTK_RELIEF_NONE); + + arrow = gtk_arrow_new (GTK_ARROW_LEFT, GTK_SHADOW_ETCHED_IN); + gtk_container_add (GTK_CONTAINER (priv->button_left), arrow); + + gtk_widget_set_size_request (GTK_WIDGET (priv->button_left), 25, 0); + + g_signal_connect (priv->button_left, + "clicked", + G_CALLBACK (eog_thumb_nav_button_clicked), + nav); + + g_signal_connect (priv->button_left, + "pressed", + G_CALLBACK (eog_thumb_nav_start_scroll), + nav); + + g_signal_connect (priv->button_left, + "released", + G_CALLBACK (eog_thumb_nav_stop_scroll), + nav); + + priv->button_left = gtk_button_new (); + gtk_button_set_relief (GTK_BUTTON (priv->button_left), GTK_RELIEF_NONE); + + arrow = gtk_arrow_new (GTK_ARROW_LEFT, GTK_SHADOW_ETCHED_IN); + gtk_container_add (GTK_CONTAINER (priv->button_left), arrow); + + gtk_widget_set_size_request (GTK_WIDGET (priv->button_left), 25, 0); + + g_signal_connect (priv->button_left, + "clicked", + G_CALLBACK (eog_thumb_nav_button_clicked), + nav); + + g_signal_connect (priv->button_left, + "pressed", + G_CALLBACK (eog_thumb_nav_start_scroll), + nav); + + g_signal_connect (priv->button_left, + "released", + G_CALLBACK (eog_thumb_nav_stop_scroll), + nav); + + priv->vbox = gtk_vbox_new (FALSE, 0); + + priv->sw = gtk_scrolled_window_new (NULL, NULL); + + gtk_widget_set_name (gtk_scrolled_window_get_hscrollbar (GTK_SCROLLED_WINDOW (priv->sw)), + "eog-image-collection-scrollbar"); + + gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (priv->sw), + GTK_SHADOW_IN); + + + + g_signal_connect (priv->sw, + "scroll-event", + G_CALLBACK (eog_thumb_nav_scroll_event), + nav); + + priv->hadj = gtk_scrolled_window_get_hadjustment (GTK_SCROLLED_WINDOW (priv->sw)); + + g_signal_connect (priv->hadj, + "changed", + G_CALLBACK (eog_thumb_nav_hadj_changed), + nav); + + g_signal_connect (priv->hadj, + "value-changed", + G_CALLBACK (eog_thumb_nav_hadj_value_changed), + nav); + + priv->vadj = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (priv->sw)); + + g_signal_connect (priv->vadj, + "changed", + G_CALLBACK (eog_thumb_nav_vadj_changed), + nav); + + g_signal_connect (priv->vadj, + "value-changed", + G_CALLBACK (eog_thumb_nav_vadj_value_changed), + nav); + + priv->button_right = gtk_button_new (); + gtk_button_set_relief (GTK_BUTTON (priv->button_right), GTK_RELIEF_NONE); + + arrow = gtk_arrow_new (GTK_ARROW_RIGHT, GTK_SHADOW_NONE); + gtk_container_add (GTK_CONTAINER (priv->button_right), arrow); + + gtk_widget_set_size_request (GTK_WIDGET (priv->button_right), 25, 0); + + g_signal_connect (priv->button_right, + "clicked", + G_CALLBACK (eog_thumb_nav_button_clicked), + nav); + + g_signal_connect (priv->button_right, + "pressed", + G_CALLBACK (eog_thumb_nav_start_scroll), + nav); + + g_signal_connect (priv->button_right, + "released", + G_CALLBACK (eog_thumb_nav_stop_scroll), + nav); + + priv->button_down = gtk_button_new (); + gtk_button_set_relief (GTK_BUTTON (priv->button_down), GTK_RELIEF_NONE); + + arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE); + gtk_container_add (GTK_CONTAINER (priv->button_down), arrow); + + gtk_widget_set_size_request (GTK_WIDGET (priv->button_down), 0, 25); + + g_signal_connect (priv->button_down, + "clicked", + G_CALLBACK (eog_thumb_nav_button_clicked), + nav); + + g_signal_connect (priv->button_down, + "pressed", + G_CALLBACK (eog_thumb_nav_start_scroll), + nav); + + g_signal_connect (priv->button_down, + "released", + G_CALLBACK (eog_thumb_nav_stop_scroll), + nav); + + priv->button_up = gtk_button_new (); + gtk_button_set_relief (GTK_BUTTON (priv->button_up), GTK_RELIEF_NONE); + + arrow = gtk_arrow_new (GTK_ARROW_UP, GTK_SHADOW_NONE); + gtk_container_add (GTK_CONTAINER (priv->button_up), arrow); + + gtk_widget_set_size_request (GTK_WIDGET (priv->button_up), 0, 25); + + g_signal_connect (priv->button_up, + "clicked", + G_CALLBACK (eog_thumb_nav_button_clicked), + nav); + + g_signal_connect (priv->button_up, + "pressed", + G_CALLBACK (eog_thumb_nav_start_scroll), + nav); + + g_signal_connect (priv->button_up, + "released", + G_CALLBACK (eog_thumb_nav_stop_scroll), + nav); + + + g_object_ref (priv->button_up); + g_object_ref (priv->button_down); + gtk_box_pack_start (GTK_BOX (nav), priv->button_left, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX (nav), priv->vbox, TRUE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (nav), priv->button_right, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX (priv->vbox), priv->sw, TRUE, TRUE, 0); + + gtk_adjustment_value_changed (priv->hadj); +} + +/** + * eog_thumb_nav_new: + * @thumbview: a #CheeseThumbView to embed in the navigation widget. + * @mode: The navigation mode. + * @show_buttons: Whether to show the navigation buttons. + * + * Creates a new thumbnail navigation widget. + * + * Returns: a new #EogThumbNav object. + **/ +GtkWidget * +eog_thumb_nav_new (GtkWidget *thumbview, + gboolean show_buttons) +{ + EogThumbNav *nav; + EogThumbNavPrivate *priv; + + nav = g_object_new (EOG_TYPE_THUMB_NAV, + "show-buttons", show_buttons, + "thumbview", thumbview, + "homogeneous", FALSE, + "spacing", 0, + NULL); + + priv = nav->priv; + + return GTK_WIDGET (nav); +} + +/** + * eog_thumb_nav_get_show_buttons: + * @nav: an #EogThumbNav. + * + * Gets whether the navigation buttons are visible. + * + * Returns: %TRUE if the navigation buttons are visible, + * %FALSE otherwise. + **/ +gboolean +eog_thumb_nav_get_show_buttons (EogThumbNav *nav) +{ + g_return_val_if_fail (EOG_IS_THUMB_NAV (nav), FALSE); + + return nav->priv->show_buttons; +} + +/** + * eog_thumb_nav_set_show_buttons: + * @nav: an #EogThumbNav. + * @show_buttons: %TRUE to show the buttons, %FALSE to hide them. + * + * Sets whether the navigation buttons to the left and right of the + * widget should be visible. + **/ +void +eog_thumb_nav_set_show_buttons (EogThumbNav *nav, gboolean show_buttons) +{ + g_return_if_fail (EOG_IS_THUMB_NAV (nav)); + g_return_if_fail (nav->priv->button_left != NULL); + g_return_if_fail (nav->priv->button_right != NULL); + + nav->priv->show_buttons = show_buttons; + + if (show_buttons) + { + gtk_widget_show_all (nav->priv->button_left); + gtk_widget_show_all (nav->priv->button_right); + } + else + { + gtk_widget_hide_all (nav->priv->button_left); + gtk_widget_hide_all (nav->priv->button_right); + } +} + +void +eog_thumb_nav_set_policy (EogThumbNav *nav, + GtkPolicyType hscrollbar_policy, + GtkPolicyType vscrollbar_policy) +{ + EogThumbNavPrivate *priv = EOG_THUMB_NAV_GET_PRIVATE (nav); + + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->sw), + hscrollbar_policy, + vscrollbar_policy); +} + +gboolean +eog_thumb_nav_is_vertical (EogThumbNav *nav) +{ + EogThumbNavPrivate *priv = EOG_THUMB_NAV_GET_PRIVATE (nav); + + return priv->vertical; +} + +void +eog_thumb_nav_set_vertical (EogThumbNav *nav, gboolean vertical) +{ + EogThumbNavPrivate *priv = EOG_THUMB_NAV_GET_PRIVATE (nav); + + g_return_if_fail (EOG_IS_THUMB_NAV (nav)); + g_return_if_fail (priv->button_left != NULL); + g_return_if_fail (priv->button_right != NULL); + g_return_if_fail (priv->vbox != NULL); + g_return_if_fail (priv->sw != NULL); + + if (vertical == priv->vertical) return; + + /* show/hide doesn't work because of a mandatory show_all in cheese-window */ + + if (vertical) + { + g_return_if_fail (!gtk_widget_get_parent (priv->button_up)); + g_return_if_fail (!gtk_widget_get_parent (priv->button_down)); + g_return_if_fail (gtk_widget_get_parent (priv->button_left)); + g_return_if_fail (gtk_widget_get_parent (priv->button_right)); + + gtk_box_pack_start (GTK_BOX (priv->vbox), priv->button_up, FALSE, FALSE, 0); + gtk_box_reorder_child (GTK_BOX (priv->vbox), priv->button_up, 0); + gtk_box_pack_start (GTK_BOX (priv->vbox), priv->button_down, FALSE, FALSE, 0); + g_object_unref (priv->button_up); + g_object_unref (priv->button_down); + + g_object_ref (priv->button_left); + gtk_container_remove (GTK_CONTAINER (nav), priv->button_left); + g_object_ref (priv->button_right); + gtk_container_remove (GTK_CONTAINER (nav), priv->button_right); + gtk_adjustment_value_changed (priv->vadj); + + eog_thumb_nav_set_policy (nav, + GTK_POLICY_NEVER, + GTK_POLICY_AUTOMATIC); + priv->vertical = TRUE; + } + else + { + g_return_if_fail (!gtk_widget_get_parent (priv->button_left)); + g_return_if_fail (!gtk_widget_get_parent (priv->button_right)); + g_return_if_fail (gtk_widget_get_parent (priv->button_up)); + g_return_if_fail (gtk_widget_get_parent (priv->button_down)); + + gtk_box_pack_start (GTK_BOX (nav), priv->button_left, FALSE, FALSE, 0); + gtk_box_reorder_child (GTK_BOX (nav), priv->button_left, 0); + gtk_box_pack_start (GTK_BOX (nav), priv->button_right, FALSE, FALSE, 0); + g_object_unref (priv->button_left); + g_object_unref (priv->button_right); + + g_object_ref (priv->button_up); + gtk_container_remove (GTK_CONTAINER (priv->vbox), priv->button_up); + g_object_ref (priv->button_down); + gtk_container_remove (GTK_CONTAINER (priv->vbox), priv->button_down); + gtk_adjustment_value_changed (priv->hadj); + + eog_thumb_nav_set_policy (nav, + GTK_POLICY_AUTOMATIC, + GTK_POLICY_NEVER); + priv->vertical = FALSE; + } + gtk_widget_show_all (GTK_WIDGET (nav)); +} diff --git a/src/thumbview/eog-thumb-nav.h b/src/thumbview/eog-thumb-nav.h new file mode 100644 index 00000000..bb6ea2cd --- /dev/null +++ b/src/thumbview/eog-thumb-nav.h @@ -0,0 +1,77 @@ +/* Eye Of Gnome - Thumbnail Navigator + * + * Copyright (C) 2006 The Free Software Foundation + * + * Author: Lucas Rocha <lucasr@gnome.org> + * + * 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 of the License, 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. + */ + +#ifndef __EOG_THUMB_NAV_H__ +#define __EOG_THUMB_NAV_H__ + +#include "cheese-thumb-view.h" + +#include <gtk/gtk.h> +#include <glib.h> +#include <glib-object.h> + +G_BEGIN_DECLS + +typedef struct _EogThumbNav EogThumbNav; +typedef struct _EogThumbNavClass EogThumbNavClass; +typedef struct _EogThumbNavPrivate EogThumbNavPrivate; + +#define EOG_TYPE_THUMB_NAV (eog_thumb_nav_get_type ()) +#define EOG_THUMB_NAV(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EOG_TYPE_THUMB_NAV, EogThumbNav)) +#define EOG_THUMB_NAV_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), EOG_TYPE_THUMB_NAV, EogThumbNavClass)) +#define EOG_IS_THUMB_NAV(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EOG_TYPE_THUMB_NAV)) +#define EOG_IS_THUMB_NAV_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), EOG_TYPE_THUMB_NAV)) +#define EOG_THUMB_NAV_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EOG_TYPE_THUMB_NAV, EogThumbNavClass)) + +struct _EogThumbNav +{ + GtkHBox base_instance; + + EogThumbNavPrivate *priv; +}; + +struct _EogThumbNavClass +{ + GtkHBoxClass parent_class; +}; + +GType eog_thumb_nav_get_type (void) G_GNUC_CONST; + +GtkWidget *eog_thumb_nav_new (GtkWidget *thumbview, + gboolean show_buttons); + +gboolean eog_thumb_nav_get_show_buttons (EogThumbNav *nav); + +void eog_thumb_nav_set_show_buttons (EogThumbNav *nav, + gboolean show_buttons); + +gboolean eog_thumb_nav_is_vertical (EogThumbNav *nav); + +void eog_thumb_nav_set_vertical (EogThumbNav *nav, + gboolean vertical); + +void eog_thumb_nav_set_policy (EogThumbNav *nav, + GtkPolicyType hscrollbar_policy, + GtkPolicyType vscrollbar_policy); + +G_END_DECLS + +#endif /* __EOG_THUMB_NAV_H__ */ diff --git a/src/thumbview/eog-thumbnail.c b/src/thumbview/eog-thumbnail.c new file mode 100644 index 00000000..a19e3488 --- /dev/null +++ b/src/thumbview/eog-thumbnail.c @@ -0,0 +1,293 @@ +/* Eye Of Gnome - Thumbnailing functions + * + * Copyright (C) 2000-2007 The Free Software Foundation + * + * Author: Lucas Rocha <lucasr@gnome.org> + * + * Based on eel code (eel/eel-graphic-effects.c) by: + * - Andy Hertzfeld <andy@eazel.com> + * + * 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 of the License, 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. + */ + +/* NOTE this is a stripped version of eog-thumbnail which only contains the + * functions necessary for cheese + */ +#ifdef HAVE_CONFIG_H + #include <cheese-config.h> +#endif + +#include "eog-thumbnail.h" + + +static GdkPixbuf *frame = NULL; + + +static void +draw_frame_row (GdkPixbuf *frame_image, + gint target_width, + gint source_width, + gint source_v_position, + gint dest_v_position, + GdkPixbuf *result_pixbuf, + gint left_offset, + gint height) +{ + gint remaining_width, h_offset, slab_width; + + remaining_width = target_width; + h_offset = 0; + + while (remaining_width > 0) + { + slab_width = remaining_width > source_width ? + source_width : remaining_width; + + gdk_pixbuf_copy_area (frame_image, + left_offset, + source_v_position, + slab_width, + height, + result_pixbuf, + left_offset + h_offset, + dest_v_position); + + remaining_width -= slab_width; + h_offset += slab_width; + } +} + +static void +draw_frame_column (GdkPixbuf *frame_image, + gint target_height, + gint source_height, + gint source_h_position, + gint dest_h_position, + GdkPixbuf *result_pixbuf, + gint top_offset, + gint width) +{ + gint remaining_height, v_offset, slab_height; + + remaining_height = target_height; + v_offset = 0; + + while (remaining_height > 0) + { + slab_height = remaining_height > source_height ? + source_height : remaining_height; + + gdk_pixbuf_copy_area (frame_image, + source_h_position, + top_offset, + width, + slab_height, + result_pixbuf, + dest_h_position, + top_offset + v_offset); + + remaining_height -= slab_height; + v_offset += slab_height; + } +} + +/* copied from libart_lgpl/art_rgb.c */ + +static void +art_rgb_run_alpha (guint8 *buf, guint8 r, guint8 g, guint8 b, int alpha, int n) +{ + int i; + int v; + + for (i = 0; i < n; i++) + { + v = *buf; + *buf++ = v + (((r - v) * alpha + 0x80) >> 8); + v = *buf; + *buf++ = v + (((g - v) * alpha + 0x80) >> 8); + v = *buf; + *buf++ = v + (((b - v) * alpha + 0x80) >> 8); + } +} + +static GdkPixbuf * +eog_thumbnail_stretch_frame_image (GdkPixbuf *frame_image, + gint left_offset, + gint top_offset, + gint right_offset, + gint bottom_offset, + gint dest_width, + gint dest_height, + gboolean fill_flag) +{ + GdkPixbuf *result_pixbuf; + guchar *pixels_ptr; + gint frame_width, frame_height; + gint y, row_stride; + gint target_width, target_frame_width; + gint target_height, target_frame_height; + + frame_width = gdk_pixbuf_get_width (frame_image); + frame_height = gdk_pixbuf_get_height (frame_image); + + if (fill_flag) + { + result_pixbuf = gdk_pixbuf_scale_simple (frame_image, + dest_width, + dest_height, + GDK_INTERP_NEAREST); + } + else + { + result_pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, + TRUE, + 8, + dest_width, + dest_height); + } + + row_stride = gdk_pixbuf_get_rowstride (result_pixbuf); + pixels_ptr = gdk_pixbuf_get_pixels (result_pixbuf); + + if (!fill_flag) + { + for (y = 0; y < dest_height; y++) + { + art_rgb_run_alpha (pixels_ptr, + 255, 255, + 255, 255, + dest_width); + pixels_ptr += row_stride; + } + } + + target_width = dest_width - left_offset - right_offset; + target_frame_width = frame_width - left_offset - right_offset; + + target_height = dest_height - top_offset - bottom_offset; + target_frame_height = frame_height - top_offset - bottom_offset; + + /* Draw the left top corner and top row */ + gdk_pixbuf_copy_area (frame_image, + 0, 0, + left_offset, + top_offset, + result_pixbuf, + 0, 0); + + draw_frame_row (frame_image, + target_width, + target_frame_width, + 0, 0, + result_pixbuf, + left_offset, + top_offset); + + /* Draw the right top corner and left column */ + gdk_pixbuf_copy_area (frame_image, + frame_width - right_offset, + 0, + right_offset, + top_offset, + result_pixbuf, + dest_width - right_offset, + 0); + + draw_frame_column (frame_image, + target_height, + target_frame_height, + 0, 0, + result_pixbuf, + top_offset, + left_offset); + + /* Draw the bottom right corner and bottom row */ + gdk_pixbuf_copy_area (frame_image, + frame_width - right_offset, + frame_height - bottom_offset, + right_offset, + bottom_offset, + result_pixbuf, + dest_width - right_offset, + dest_height - bottom_offset); + + draw_frame_row (frame_image, + target_width, + target_frame_width, + frame_height - bottom_offset, + dest_height - bottom_offset, + result_pixbuf, + left_offset, bottom_offset); + + /* Draw the bottom left corner and the right column */ + gdk_pixbuf_copy_area (frame_image, + 0, + frame_height - bottom_offset, + left_offset, + bottom_offset, + result_pixbuf, + 0, + dest_height - bottom_offset); + + draw_frame_column (frame_image, + target_height, + target_frame_height, + frame_width - right_offset, + dest_width - right_offset, + result_pixbuf, top_offset, + right_offset); + + return result_pixbuf; +} + +void +eog_thumbnail_add_frame (GdkPixbuf **thumbnail) +{ + GdkPixbuf *result_pixbuf; + gint source_width, source_height; + gint dest_width, dest_height; + + source_width = gdk_pixbuf_get_width (*thumbnail); + source_height = gdk_pixbuf_get_height (*thumbnail); + + dest_width = source_width + 9; + dest_height = source_height + 9; + + result_pixbuf = eog_thumbnail_stretch_frame_image (frame, + 3, 3, 6, 6, + dest_width, + dest_height, + FALSE); + + gdk_pixbuf_copy_area (*thumbnail, + 0, 0, + source_width, + source_height, + result_pixbuf, + 3, 3); + + g_object_unref (*thumbnail); + + *thumbnail = result_pixbuf; +} + +void +eog_thumbnail_init (void) +{ + if (frame == NULL) + { + frame = gdk_pixbuf_new_from_file (PACKAGE_DATADIR "/pixmaps/thumbnail-frame.png", NULL); + } +} diff --git a/src/thumbview/eog-thumbnail.h b/src/thumbview/eog-thumbnail.h new file mode 100644 index 00000000..9bfab106 --- /dev/null +++ b/src/thumbview/eog-thumbnail.h @@ -0,0 +1,39 @@ +/* Eye Of Gnome - Thumbnailing functions + * + * Copyright (C) 2000-2007 The Free Software Foundation + * + * Author: Lucas Rocha <lucasr@gnome.org> + * + * Based on nautilus code (libnautilus-private/nautilus-thumbnail.c) by: + * - Andy Hertzfeld <andy@eazel.com> + * + * 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 of the License, 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. + */ + +#ifndef _EOG_THUMBNAIL_H_ +#define _EOG_THUMBNAIL_H_ + +#include <gdk-pixbuf/gdk-pixbuf.h> + +G_BEGIN_DECLS + +void eog_thumbnail_init (void); + + +void eog_thumbnail_add_frame (GdkPixbuf **thumbnail); + +G_END_DECLS + +#endif /* _EOG_THUMBNAIL_H_ */ |