diff options
Diffstat (limited to 'src/eom-thumb-view.c')
-rw-r--r-- | src/eom-thumb-view.c | 918 |
1 files changed, 918 insertions, 0 deletions
diff --git a/src/eom-thumb-view.c b/src/eom-thumb-view.c new file mode 100644 index 0000000..f0444a3 --- /dev/null +++ b/src/eom-thumb-view.c @@ -0,0 +1,918 @@ +/* Eye Of Mate - Thumbnail View + * + * Copyright (C) 2006-2008 The Free Software Foundation + * + * Author: Claudio Saavedra <[email protected]> + * + * 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 "config.h" +#endif + +#include "eom-thumb-view.h" +#include "eom-list-store.h" +#include "eom-image.h" +#include "eom-job-queue.h" + +#ifdef HAVE_EXIF +#include "eom-exif-util.h" +#include <libexif/exif-data.h> +#endif + +#include <gtk/gtk.h> +#include <glib/gi18n.h> +#include <stdlib.h> +#include <string.h> + +#define EOM_THUMB_VIEW_SPACING 0 + +#define EOM_THUMB_VIEW_GET_PRIVATE(object) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((object), EOM_TYPE_THUMB_VIEW, EomThumbViewPrivate)) + +G_DEFINE_TYPE (EomThumbView, eom_thumb_view, GTK_TYPE_ICON_VIEW); + +static EomImage* eom_thumb_view_get_image_from_path (EomThumbView *thumbview, + GtkTreePath *path); + +static void eom_thumb_view_popup_menu (EomThumbView *widget, + GdkEventButton *event); + +struct _EomThumbViewPrivate { + gint start_thumb; /* the first visible thumbnail */ + gint end_thumb; /* the last visible thumbnail */ + GtkWidget *menu; /* a contextual menu for thumbnails */ + GtkCellRenderer *pixbuf_cell; +}; + +/* Drag 'n Drop */ + +static void +eom_thumb_view_finalize (GObject *object) +{ + EomThumbView *thumbview; + g_return_if_fail (EOM_IS_THUMB_VIEW (object)); + thumbview = EOM_THUMB_VIEW (object); + + G_OBJECT_CLASS (eom_thumb_view_parent_class)->finalize (object); +} + +static void +eom_thumb_view_destroy (GtkObject *object) +{ + EomThumbView *thumbview; + g_return_if_fail (EOM_IS_THUMB_VIEW (object)); + thumbview = EOM_THUMB_VIEW (object); + + GTK_OBJECT_CLASS (eom_thumb_view_parent_class)->destroy (object); +} + +static void +eom_thumb_view_class_init (EomThumbViewClass *class) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (class); + GtkObjectClass *object_class = GTK_OBJECT_CLASS (class); + + gobject_class->finalize = eom_thumb_view_finalize; + object_class->destroy = eom_thumb_view_destroy; + + g_type_class_add_private (class, sizeof (EomThumbViewPrivate)); +} + +static void +eom_thumb_view_clear_range (EomThumbView *thumbview, + const gint start_thumb, + const gint end_thumb) +{ + GtkTreePath *path; + GtkTreeIter iter; + EomListStore *store = EOM_LIST_STORE (gtk_icon_view_get_model (GTK_ICON_VIEW (thumbview))); + gint thumb = start_thumb; + gboolean result; + + g_assert (start_thumb <= end_thumb); + + path = gtk_tree_path_new_from_indices (start_thumb, -1); + for (result = gtk_tree_model_get_iter (GTK_TREE_MODEL (store), &iter, path); + result && thumb <= end_thumb; + result = gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &iter), thumb++) { + eom_list_store_thumbnail_unset (store, &iter); + } + gtk_tree_path_free (path); +} + +static void +eom_thumb_view_add_range (EomThumbView *thumbview, + const gint start_thumb, + const gint end_thumb) +{ + GtkTreePath *path; + GtkTreeIter iter; + EomListStore *store = EOM_LIST_STORE (gtk_icon_view_get_model (GTK_ICON_VIEW (thumbview))); + gint thumb = start_thumb; + gboolean result; + + g_assert (start_thumb <= end_thumb); + + path = gtk_tree_path_new_from_indices (start_thumb, -1); + for (result = gtk_tree_model_get_iter (GTK_TREE_MODEL (store), &iter, path); + result && thumb <= end_thumb; + result = gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &iter), thumb++) { + eom_list_store_thumbnail_set (store, &iter); + } + gtk_tree_path_free (path); +} + +static void +eom_thumb_view_update_visible_range (EomThumbView *thumbview, + const gint start_thumb, + const gint end_thumb) +{ + EomThumbViewPrivate *priv = thumbview->priv; + int old_start_thumb, old_end_thumb; + + old_start_thumb= priv->start_thumb; + old_end_thumb = priv->end_thumb; + + if (start_thumb == old_start_thumb && + end_thumb == old_end_thumb) { + return; + } + + if (old_start_thumb < start_thumb) + eom_thumb_view_clear_range (thumbview, old_start_thumb, MIN (start_thumb - 1, old_end_thumb)); + + if (old_end_thumb > end_thumb) + eom_thumb_view_clear_range (thumbview, MAX (end_thumb + 1, old_start_thumb), old_end_thumb); + + eom_thumb_view_add_range (thumbview, start_thumb, end_thumb); + + priv->start_thumb = start_thumb; + priv->end_thumb = end_thumb; +} + +static void +thumbview_on_visible_range_changed_cb (EomThumbView *thumbview, + gpointer user_data) +{ + GtkTreePath *path1, *path2; + + if (!gtk_icon_view_get_visible_range (GTK_ICON_VIEW (thumbview), &path1, &path2)) { + return; + } + + if (path1 == NULL) { + path1 = gtk_tree_path_new_first (); + } + if (path2 == NULL) { + gint n_items = gtk_tree_model_iter_n_children (gtk_icon_view_get_model (GTK_ICON_VIEW (thumbview)), NULL); + path2 = gtk_tree_path_new_from_indices (n_items - 1 , -1); + } + + eom_thumb_view_update_visible_range (thumbview, gtk_tree_path_get_indices (path1) [0], + gtk_tree_path_get_indices (path2) [0]); + + gtk_tree_path_free (path1); + gtk_tree_path_free (path2); +} + +static void +thumbview_on_adjustment_changed_cb (EomThumbView *thumbview, + gpointer user_data) +{ + GtkTreePath *path1, *path2; + gint start_thumb, end_thumb; + + if (!gtk_icon_view_get_visible_range (GTK_ICON_VIEW (thumbview), &path1, &path2)) { + return; + } + + if (path1 == NULL) { + path1 = gtk_tree_path_new_first (); + } + if (path2 == NULL) { + gint n_items = gtk_tree_model_iter_n_children (gtk_icon_view_get_model (GTK_ICON_VIEW (thumbview)), NULL); + path2 = gtk_tree_path_new_from_indices (n_items - 1 , -1); + } + + start_thumb = gtk_tree_path_get_indices (path1) [0]; + end_thumb = gtk_tree_path_get_indices (path2) [0]; + + eom_thumb_view_add_range (thumbview, start_thumb, end_thumb); + + /* case we added an image, we need to make sure that the shifted thumbnail is cleared */ + eom_thumb_view_clear_range (thumbview, end_thumb + 1, end_thumb + 1); + + thumbview->priv->start_thumb = start_thumb; + thumbview->priv->end_thumb = end_thumb; + + gtk_tree_path_free (path1); + gtk_tree_path_free (path2); +} + +static void +thumbview_on_parent_set_cb (GtkWidget *widget, + GtkObject *old_parent, + gpointer user_data) +{ + EomThumbView *thumbview = EOM_THUMB_VIEW (widget); + GtkScrolledWindow *sw; + GtkAdjustment *hadjustment; + GtkAdjustment *vadjustment; + + GtkWidget *parent = gtk_widget_get_parent (GTK_WIDGET (thumbview)); + if (!GTK_IS_SCROLLED_WINDOW (parent)) { + return; + } + + /* if we have been set to a ScrolledWindow, we connect to the callback + to set and unset thumbnails. */ + sw = GTK_SCROLLED_WINDOW (parent); + hadjustment = gtk_scrolled_window_get_hadjustment (GTK_SCROLLED_WINDOW (sw)); + vadjustment = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (sw)); + + /* when scrolling */ + g_signal_connect_data (G_OBJECT (hadjustment), "value-changed", + G_CALLBACK (thumbview_on_visible_range_changed_cb), + thumbview, NULL, G_CONNECT_SWAPPED | G_CONNECT_AFTER); + g_signal_connect_data (G_OBJECT (vadjustment), "value-changed", + G_CALLBACK (thumbview_on_visible_range_changed_cb), + thumbview, NULL, G_CONNECT_SWAPPED | G_CONNECT_AFTER); + + /* when the adjustment is changed, ie. probably we have new images added. */ + g_signal_connect_data (G_OBJECT (hadjustment), "changed", + G_CALLBACK (thumbview_on_adjustment_changed_cb), + thumbview, NULL, G_CONNECT_SWAPPED | G_CONNECT_AFTER); + g_signal_connect_data (G_OBJECT (vadjustment), "changed", + G_CALLBACK (thumbview_on_adjustment_changed_cb), + thumbview, NULL, G_CONNECT_SWAPPED | G_CONNECT_AFTER); + + /* when resizing the scrolled window */ + g_signal_connect_swapped (G_OBJECT (sw), "size-allocate", + G_CALLBACK (thumbview_on_visible_range_changed_cb), + thumbview); +} + +static gboolean +thumbview_on_button_press_event_cb (GtkWidget *thumbview, GdkEventButton *event, + gpointer user_data) +{ + GtkTreePath *path; + + /* Ignore double-clicks and triple-clicks */ + if (event->button == 3 && event->type == GDK_BUTTON_PRESS) + { + path = gtk_icon_view_get_path_at_pos (GTK_ICON_VIEW (thumbview), + (gint) event->x, (gint) event->y); + if (path == NULL) { + return FALSE; + } + + if (!gtk_icon_view_path_is_selected (GTK_ICON_VIEW (thumbview), path) || + eom_thumb_view_get_n_selected (EOM_THUMB_VIEW (thumbview)) != 1) { + gtk_icon_view_unselect_all (GTK_ICON_VIEW (thumbview)); + gtk_icon_view_select_path (GTK_ICON_VIEW (thumbview), path); + gtk_icon_view_set_cursor (GTK_ICON_VIEW (thumbview), path, NULL, FALSE); + } + eom_thumb_view_popup_menu (EOM_THUMB_VIEW (thumbview), event); + + gtk_tree_path_free (path); + + return TRUE; + } + + return FALSE; +} + +static void +thumbview_on_drag_data_get_cb (GtkWidget *widget, + GdkDragContext *drag_context, + GtkSelectionData *data, + guint info, + guint time, + gpointer user_data) +{ + GList *list; + GList *node; + EomImage *image; + GFile *file; + gchar **uris = NULL; + gint i = 0, n_images; + + list = eom_thumb_view_get_selected_images (EOM_THUMB_VIEW (widget)); + n_images = eom_thumb_view_get_n_selected (EOM_THUMB_VIEW (widget)); + + uris = g_new (gchar *, n_images + 1); + + for (node = list; node != NULL; node = node->next, i++) { + image = EOM_IMAGE (node->data); + file = eom_image_get_file (image); + uris[i] = g_file_get_uri (file); + g_object_unref (image); + g_object_unref (file); + } + uris[i] = NULL; + + gtk_selection_data_set_uris (data, uris); + g_strfreev (uris); + g_list_free (list); +} + +static gchar * +thumbview_get_tooltip_string (EomImage *image) +{ + gchar *bytes; + char *type_str; + gint width, height; + GFile *file; + GFileInfo *file_info; + const char *mime_str; + gchar *tooltip_string; +#ifdef HAVE_EXIF + ExifData *exif_data; +#endif + + bytes = g_format_size_for_display (eom_image_get_bytes (image)); + + eom_image_get_size (image, &width, &height); + + file = eom_image_get_file (image); + file_info = g_file_query_info (file, + G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE, + 0, NULL, NULL); + g_object_unref (file); + if (file_info == NULL) { + return NULL; + } + + mime_str = g_file_info_get_content_type (file_info); + + if (G_UNLIKELY (mime_str == NULL)) { + g_free (bytes); + g_object_unref (image); + return NULL; + } + + type_str = g_content_type_get_description (mime_str); + g_object_unref (file_info); + + if (width > -1 && height > -1) { + tooltip_string = g_markup_printf_escaped ("<b><big>%s</big></b>\n" + "%i x %i %s\n" + "%s\n" + "%s", + eom_image_get_caption (image), + width, + height, + ngettext ("pixel", + "pixels", + height), + bytes, + type_str); + } else { + tooltip_string = g_markup_printf_escaped ("<b><big>%s</big></b>\n" + "%s\n" + "%s", + eom_image_get_caption (image), + bytes, + type_str); + + } + +#ifdef HAVE_EXIF + exif_data = (ExifData *) eom_image_get_exif_info (image); + + if (exif_data) { + gchar *extra_info, *tmp, *date; + /* The EXIF standard says that the DATE_TIME tag is + * 20 bytes long. A 32-byte buffer should be large enough. */ + gchar time_buffer[32]; + + date = eom_exif_util_format_date ( + eom_exif_util_get_value (exif_data, EXIF_TAG_DATE_TIME_ORIGINAL, time_buffer, 32)); + + if (date) { + extra_info = g_strdup_printf ("\n%s %s", _("Taken on"), date); + + tmp = g_strconcat (tooltip_string, extra_info, NULL); + + g_free (date); + g_free (extra_info); + g_free (tooltip_string); + + tooltip_string = tmp; + } + exif_data_unref (exif_data); + } +#endif + + g_free (type_str); + g_free (bytes); + + return tooltip_string; +} + +static void +on_data_loaded_cb (EomJob *job, gpointer data) +{ + if (!job->error) { + gtk_tooltip_trigger_tooltip_query (gdk_display_get_default()); + } +} + +static gboolean +thumbview_on_query_tooltip_cb (GtkWidget *widget, + gint x, + gint y, + gboolean keyboard_mode, + GtkTooltip *tooltip, + gpointer user_data) +{ + GtkTreePath *path; + EomImage *image; + gchar *tooltip_string; + EomImageData data = 0; + + if (!gtk_icon_view_get_tooltip_context (GTK_ICON_VIEW (widget), + &x, &y, keyboard_mode, + NULL, &path, NULL)) { + return FALSE; + } + + image = eom_thumb_view_get_image_from_path (EOM_THUMB_VIEW (widget), + path); + gtk_tree_path_free (path); + + if (image == NULL) { + return FALSE; + } + + if (!eom_image_has_data (image, EOM_IMAGE_DATA_EXIF) && + eom_image_get_metadata_status (image) == EOM_IMAGE_METADATA_NOT_READ) { + data = EOM_IMAGE_DATA_EXIF; + } + + if (!eom_image_has_data (image, EOM_IMAGE_DATA_DIMENSION)) { + data |= EOM_IMAGE_DATA_DIMENSION; + } + + if (data) { + EomJob *job; + + job = eom_job_load_new (image, data); + g_signal_connect (G_OBJECT (job), "finished", + G_CALLBACK (on_data_loaded_cb), + widget); + eom_job_queue_add_job (job); + g_object_unref (image); + g_object_unref (job); + return FALSE; + } + + tooltip_string = thumbview_get_tooltip_string (image); + g_object_unref (image); + + if (tooltip_string == NULL) { + return FALSE; + } + + gtk_tooltip_set_markup (tooltip, tooltip_string); + g_free (tooltip_string); + + return TRUE; +} + +static void +eom_thumb_view_init (EomThumbView *thumbview) +{ + thumbview->priv = EOM_THUMB_VIEW_GET_PRIVATE (thumbview); + + thumbview->priv->pixbuf_cell = gtk_cell_renderer_pixbuf_new (); + + gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (thumbview), + thumbview->priv->pixbuf_cell, + FALSE); + + g_object_set (thumbview->priv->pixbuf_cell, + "follow-state", FALSE, + "height", 100, + "width", 115, + "yalign", 0.5, + "xalign", 0.5, + NULL); + + gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (thumbview), + thumbview->priv->pixbuf_cell, + "pixbuf", EOM_LIST_STORE_THUMBNAIL, + NULL); + + gtk_icon_view_set_selection_mode (GTK_ICON_VIEW (thumbview), + GTK_SELECTION_MULTIPLE); + + gtk_icon_view_set_column_spacing (GTK_ICON_VIEW (thumbview), + EOM_THUMB_VIEW_SPACING); + + gtk_icon_view_set_row_spacing (GTK_ICON_VIEW (thumbview), + EOM_THUMB_VIEW_SPACING); + + g_object_set (thumbview, "has-tooltip", TRUE, NULL); + + g_signal_connect (thumbview, + "query-tooltip", + G_CALLBACK (thumbview_on_query_tooltip_cb), + NULL); + + thumbview->priv->start_thumb = 0; + thumbview->priv->end_thumb = 0; + thumbview->priv->menu = NULL; + + g_signal_connect (G_OBJECT (thumbview), "parent-set", + G_CALLBACK (thumbview_on_parent_set_cb), NULL); + + gtk_icon_view_enable_model_drag_source (GTK_ICON_VIEW (thumbview), 0, + NULL, 0, + GDK_ACTION_COPY); + gtk_drag_source_add_uri_targets (GTK_WIDGET (thumbview)); + + g_signal_connect (G_OBJECT (thumbview), "drag-data-get", + G_CALLBACK (thumbview_on_drag_data_get_cb), NULL); +} + +/** + * eom_thumb_view_new: + * + * Creates a new #EomThumbView object. + * + * Returns: a newly created #EomThumbView. + **/ +GtkWidget * +eom_thumb_view_new (void) +{ + EomThumbView *thumbview; + + thumbview = g_object_new (EOM_TYPE_THUMB_VIEW, NULL); + + return GTK_WIDGET (thumbview); +} + +/** + * eom_thumb_view_set_model: + * @thumbview: A #EomThumbView. + * @store: A #EomListStore. + * + * Sets the #EomListStore to be used with @thumbview. If an initial image + * was set during @store creation, its thumbnail will be selected and visible. + * + **/ +void +eom_thumb_view_set_model (EomThumbView *thumbview, EomListStore *store) +{ + gint index; + + g_return_if_fail (EOM_IS_THUMB_VIEW (thumbview)); + g_return_if_fail (EOM_IS_LIST_STORE (store)); + + index = eom_list_store_get_initial_pos (store); + + gtk_icon_view_set_model (GTK_ICON_VIEW (thumbview), GTK_TREE_MODEL (store)); + + if (index >= 0) { + GtkTreePath *path = gtk_tree_path_new_from_indices (index, -1); + gtk_icon_view_select_path (GTK_ICON_VIEW (thumbview), path); + gtk_icon_view_set_cursor (GTK_ICON_VIEW (thumbview), path, NULL, FALSE); + gtk_icon_view_scroll_to_path (GTK_ICON_VIEW (thumbview), path, FALSE, 0, 0); + gtk_tree_path_free (path); + } +} + +/** + * eom_thumb_view_set_item_height: + * @thumbview: A #EomThumbView. + * @height: The desired height. + * + * Sets the height of each thumbnail in @thumbview. + * + **/ +void +eom_thumb_view_set_item_height (EomThumbView *thumbview, gint height) +{ + g_return_if_fail (EOM_IS_THUMB_VIEW (thumbview)); + + g_object_set (thumbview->priv->pixbuf_cell, + "height", height, + NULL); +} + +static void +eom_thumb_view_get_n_selected_helper (GtkIconView *thumbview, + GtkTreePath *path, + gpointer data) +{ + /* data is of type (guint *) */ + (*(guint *) data) ++; +} + +/** + * eom_thumb_view_get_n_selected: + * @thumbview: An #EomThumbView. + * + * Gets the number of images that are currently selected in @thumbview. + * + * Returns: the number of selected images in @thumbview. + **/ +guint +eom_thumb_view_get_n_selected (EomThumbView *thumbview) +{ + guint count = 0; + gtk_icon_view_selected_foreach (GTK_ICON_VIEW (thumbview), + eom_thumb_view_get_n_selected_helper, + (&count)); + return count; +} + +/** + * eom_thumb_view_get_image_from_path: + * @thumbview: A #EomThumbView. + * @path: A #GtkTreePath pointing to a #EomImage in the model for @thumbview. + * + * Gets the #EomImage stored in @thumbview's #EomListStore at the position indicated + * by @path. + * + * Returns: A #EomImage. + **/ +static EomImage * +eom_thumb_view_get_image_from_path (EomThumbView *thumbview, GtkTreePath *path) +{ + GtkTreeModel *model; + GtkTreeIter iter; + EomImage *image; + + model = gtk_icon_view_get_model (GTK_ICON_VIEW (thumbview)); + gtk_tree_model_get_iter (model, &iter, path); + + gtk_tree_model_get (model, &iter, + EOM_LIST_STORE_EOM_IMAGE, &image, + -1); + + return image; +} + +/** + * eom_thumb_view_get_first_selected_image: + * @thumbview: A #EomThumbView. + * + * Returns the first selected image. Note that the returned #EomImage + * is not ensured to be really the first selected image in @thumbview, but + * generally, it will be. + * + * Returns: A #EomImage. + **/ +EomImage * +eom_thumb_view_get_first_selected_image (EomThumbView *thumbview) +{ + /* The returned list is not sorted! We need to find the + smaller tree path value => tricky and expensive. Do we really need this? + */ + EomImage *image; + GtkTreePath *path; + GList *list = gtk_icon_view_get_selected_items (GTK_ICON_VIEW (thumbview)); + + if (list == NULL) { + return NULL; + } + + path = (GtkTreePath *) (list->data); + + image = eom_thumb_view_get_image_from_path (thumbview, path); + + g_list_foreach (list, (GFunc) gtk_tree_path_free , NULL); + g_list_free (list); + + return image; +} + +/** + * eom_thumb_view_get_selected_images: + * @thumbview: A #EomThumbView. + * + * Gets a list with the currently selected images. Note that a new reference is + * hold for each image and the list must be freed with g_list_free(). + * + * Returns: A newly allocated list of #EomImage's. + **/ +GList * +eom_thumb_view_get_selected_images (EomThumbView *thumbview) +{ + GList *l, *item; + GList *list = NULL; + + GtkTreePath *path; + + l = gtk_icon_view_get_selected_items (GTK_ICON_VIEW (thumbview)); + + for (item = l; item != NULL; item = item->next) { + path = (GtkTreePath *) item->data; + list = g_list_prepend (list, eom_thumb_view_get_image_from_path (thumbview, path)); + gtk_tree_path_free (path); + } + + g_list_free (l); + list = g_list_reverse (list); + + return list; +} + +/** + * eom_thumb_view_set_current_image: + * @thumbview: A #EomThumbView. + * @image: The image to be selected. + * @deselect_other: Whether to deselect currently selected images. + * + * Changes the status of a image, marking it as currently selected. + * If @deselect_other is %TRUE, all other selected images will be + * deselected. + * + **/ +void +eom_thumb_view_set_current_image (EomThumbView *thumbview, EomImage *image, + gboolean deselect_other) +{ + GtkTreePath *path; + EomListStore *store; + gint pos; + + store = EOM_LIST_STORE (gtk_icon_view_get_model (GTK_ICON_VIEW (thumbview))); + pos = eom_list_store_get_pos_by_image (store, image); + path = gtk_tree_path_new_from_indices (pos, -1); + + if (path == NULL) { + return; + } + + if (deselect_other) { + gtk_icon_view_unselect_all (GTK_ICON_VIEW (thumbview)); + } + + gtk_icon_view_select_path (GTK_ICON_VIEW (thumbview), path); + gtk_icon_view_set_cursor (GTK_ICON_VIEW (thumbview), path, NULL, FALSE); + gtk_icon_view_scroll_to_path (GTK_ICON_VIEW (thumbview), path, FALSE, 0, 0); + + gtk_tree_path_free (path); +} + +/** + * eom_thumb_view_select_single: + * @thumbview: A #EomThumbView. + * @change: A #EomThumbViewSelectionChange, describing the + * desired selection change. + * + * Changes the current selection according to a single movement + * described by #EomThumbViewSelectionChange. If there are no + * thumbnails currently selected, one is selected according to the + * natural selection according to the #EomThumbViewSelectionChange + * used, p.g., when %EOM_THUMB_VIEW_SELECT_RIGHT is the selected change, + * the first thumbnail will be selected. + * + **/ +void +eom_thumb_view_select_single (EomThumbView *thumbview, + EomThumbViewSelectionChange change) +{ + GtkTreePath *path = NULL; + GtkTreeModel *model; + GList *list; + gint n_items; + + g_return_if_fail (EOM_IS_THUMB_VIEW (thumbview)); + + model = gtk_icon_view_get_model (GTK_ICON_VIEW (thumbview)); + + n_items = eom_list_store_length (EOM_LIST_STORE (model)); + + if (n_items == 0) { + return; + } + + if (eom_thumb_view_get_n_selected (thumbview) == 0) { + switch (change) { + case EOM_THUMB_VIEW_SELECT_CURRENT: + break; + case EOM_THUMB_VIEW_SELECT_RIGHT: + case EOM_THUMB_VIEW_SELECT_FIRST: + path = gtk_tree_path_new_first (); + break; + case EOM_THUMB_VIEW_SELECT_LEFT: + case EOM_THUMB_VIEW_SELECT_LAST: + path = gtk_tree_path_new_from_indices (n_items - 1, -1); + break; + case EOM_THUMB_VIEW_SELECT_RANDOM: + path = gtk_tree_path_new_from_indices ((int)(((float)(n_items - 1) * rand()) / (float)(RAND_MAX + 1.f)), -1); + break; + } + } else { + list = gtk_icon_view_get_selected_items (GTK_ICON_VIEW (thumbview)); + path = gtk_tree_path_copy ((GtkTreePath *) list->data); + g_list_foreach (list, (GFunc) gtk_tree_path_free , NULL); + g_list_free (list); + + gtk_icon_view_unselect_all (GTK_ICON_VIEW (thumbview)); + + switch (change) { + case EOM_THUMB_VIEW_SELECT_CURRENT: + break; + case EOM_THUMB_VIEW_SELECT_LEFT: + if (!gtk_tree_path_prev (path)) { + gtk_tree_path_free (path); + path = gtk_tree_path_new_from_indices (n_items - 1, -1); + } + break; + case EOM_THUMB_VIEW_SELECT_RIGHT: + if (gtk_tree_path_get_indices (path) [0] == n_items - 1) { + gtk_tree_path_free (path); + path = gtk_tree_path_new_first (); + } else { + gtk_tree_path_next (path); + } + break; + case EOM_THUMB_VIEW_SELECT_FIRST: + gtk_tree_path_free (path); + path = gtk_tree_path_new_first (); + break; + case EOM_THUMB_VIEW_SELECT_LAST: + gtk_tree_path_free (path); + path = gtk_tree_path_new_from_indices (n_items - 1, -1); + break; + case EOM_THUMB_VIEW_SELECT_RANDOM: + gtk_tree_path_free (path); + path = gtk_tree_path_new_from_indices ((int)(((float)(n_items - 1) * rand()) / (float)(RAND_MAX + 1.f)), -1); + break; + } + } + + gtk_icon_view_select_path (GTK_ICON_VIEW (thumbview), path); + gtk_icon_view_set_cursor (GTK_ICON_VIEW (thumbview), path, NULL, FALSE); + gtk_icon_view_scroll_to_path (GTK_ICON_VIEW (thumbview), path, FALSE, 0, 0); + gtk_tree_path_free (path); +} + + +/** + * eom_thumb_view_set_thumbnail_popup: + * @thumbview: An #EomThumbView. + * @menu: A #GtkMenu. + * + * Set the contextual menu to be used with the thumbnails in the + * widget. This can be done only once. + * + **/ +void +eom_thumb_view_set_thumbnail_popup (EomThumbView *thumbview, + GtkMenu *menu) +{ + g_return_if_fail (EOM_IS_THUMB_VIEW (thumbview)); + g_return_if_fail (thumbview->priv->menu == NULL); + + thumbview->priv->menu = g_object_ref (menu); + + gtk_menu_attach_to_widget (GTK_MENU (thumbview->priv->menu), + GTK_WIDGET (thumbview), + NULL); + + g_signal_connect (G_OBJECT (thumbview), "button_press_event", + G_CALLBACK (thumbview_on_button_press_event_cb), NULL); + +} + + +static void +eom_thumb_view_popup_menu (EomThumbView *thumbview, GdkEventButton *event) +{ + GtkWidget *popup; + int button, event_time; + + popup = thumbview->priv->menu; + + if (event) { + button = event->button; + event_time = event->time; + } else { + button = 0; + event_time = gtk_get_current_event_time (); + } + + gtk_menu_popup (GTK_MENU (popup), NULL, NULL, NULL, NULL, + button, event_time); +} |