[gthumb: 1/15] Added GthGridView, a custom widget to display the thumbnail list
- From: Paolo Bacchilega <paobac src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gthumb: 1/15] Added GthGridView, a custom widget to display the thumbnail list
- Date: Wed, 23 Nov 2011 12:49:56 +0000 (UTC)
commit bfc90bdd531eea39f8568da40351b18589c2e247
Author: Paolo Bacchilega <paobac src gnome org>
Date: Tue Nov 22 11:26:16 2011 +0100
Added GthGridView, a custom widget to display the thumbnail list
This new widget simplifies the code, improves the performance and allows
more personalization of the thumbnail list.
data/Makefile.am | 4 +
data/filmholes.png | Bin 0 -> 173 bytes
extensions/catalogs/gth-organize-task.c | 2 +-
extensions/facebook/dlg-export-to-facebook.c | 3 +-
extensions/find_duplicates/gth-find-duplicates.c | 2 +-
extensions/flicker_utils/dlg-export-to-flickr.c | 3 +-
extensions/flicker_utils/dlg-import-from-flickr.c | 3 +-
extensions/photo_importer/dlg-photo-importer.c | 2 +-
extensions/photobucket/dlg-export-to-photobucket.c | 3 +-
extensions/picasaweb/dlg-export-to-picasaweb.c | 3 +-
extensions/picasaweb/dlg-import-from-picasaweb.c | 3 +-
gthumb/Makefile.am | 18 +-
gthumb/cairo-utils.c | 12 +
gthumb/cairo-utils.h | 5 +-
gthumb/gth-browser-actions-entries.h | 2 +-
gthumb/gth-browser.c | 12 +-
gthumb/gth-cell-renderer-caption.c | 374 --
gthumb/gth-cell-renderer-caption.h | 55 -
gthumb/gth-cell-renderer-thumbnail.c | 483 ---
gthumb/gth-cell-renderer-thumbnail.h | 54 -
gthumb/gth-file-list.c | 212 +-
gthumb/gth-file-list.h | 4 +-
gthumb/gth-file-store.c | 45 +-
gthumb/gth-file-store.h | 4 +-
gthumb/gth-file-view.c | 175 +-
gthumb/gth-file-view.h | 48 +-
gthumb/gth-grid-view.c | 3698 ++++++++++++++++++++
gthumb/gth-grid-view.h | 94 +
gthumb/gth-icon-view.c | 939 -----
gthumb/gth-icon-view.h | 59 -
gthumb/gth-list-view.c | 605 ----
gthumb/gth-list-view.h | 59 -
gthumb/gth-marshal.list | 3 +
33 files changed, 4064 insertions(+), 2924 deletions(-)
---
diff --git a/data/Makefile.am b/data/Makefile.am
index 4d3effa..e87fd24 100644
--- a/data/Makefile.am
+++ b/data/Makefile.am
@@ -43,10 +43,14 @@ convert_DATA = gthumb.convert
pkgconfigdir = $(libdir)/pkgconfig
pkgconfig_DATA = gthumb-$(GTHUMB_API_VERSION).pc
+uidir = $(pkgdatadir)/ui
+ui_DATA = filmholes.png
+
EXTRA_DIST = \
$(desktop_in_in_files) \
$(gsettings_schema_in_files) \
$(pkgconfig_DATA) \
+ $(ui_DATA) \
gthumb.convert
DISTCLEANFILES = \
diff --git a/data/filmholes.png b/data/filmholes.png
new file mode 100644
index 0000000..84b7e1e
Binary files /dev/null and b/data/filmholes.png differ
diff --git a/extensions/catalogs/gth-organize-task.c b/extensions/catalogs/gth-organize-task.c
index 06e2579..c93cb97 100644
--- a/extensions/catalogs/gth-organize-task.c
+++ b/extensions/catalogs/gth-organize-task.c
@@ -800,7 +800,7 @@ gth_organize_task_init (GthOrganizeTask *self)
gtk_icon_theme_get_for_screen (gtk_widget_get_screen (GET_WIDGET ("organization_treeview"))));
g_object_unref (icon);
- self->priv->file_list = gth_file_list_new (gth_icon_view_new (), GTH_FILE_LIST_TYPE_NORMAL, FALSE);
+ self->priv->file_list = gth_file_list_new (gth_grid_view_new (), GTH_FILE_LIST_TYPE_NORMAL, FALSE);
gth_file_list_set_caption (GTH_FILE_LIST (self->priv->file_list), "standard::display-name");
gtk_widget_show (self->priv->file_list);
gtk_box_pack_start (GTK_BOX (GET_WIDGET ("preview_box")), self->priv->file_list, TRUE, TRUE, 0);
diff --git a/extensions/facebook/dlg-export-to-facebook.c b/extensions/facebook/dlg-export-to-facebook.c
index 4331d49..4dac146 100644
--- a/extensions/facebook/dlg-export-to-facebook.c
+++ b/extensions/facebook/dlg-export-to-facebook.c
@@ -576,9 +576,8 @@ dlg_export_to_facebook (GthBrowser *browser,
/* Set the widget data */
- data->list_view = gth_file_list_new (gth_icon_view_new (), GTH_FILE_LIST_TYPE_NO_SELECTION, FALSE);
+ data->list_view = gth_file_list_new (gth_grid_view_new (), GTH_FILE_LIST_TYPE_NO_SELECTION, FALSE);
gth_file_list_set_thumb_size (GTH_FILE_LIST (data->list_view), 112);
- gth_file_view_set_spacing (GTH_FILE_VIEW (gth_file_list_get_view (GTH_FILE_LIST (data->list_view))), 0);
gth_file_list_enable_thumbs (GTH_FILE_LIST (data->list_view), TRUE);
gth_file_list_set_ignore_hidden (GTH_FILE_LIST (data->list_view), TRUE);
gth_file_list_set_caption (GTH_FILE_LIST (data->list_view), "none");
diff --git a/extensions/find_duplicates/gth-find-duplicates.c b/extensions/find_duplicates/gth-find-duplicates.c
index 982fca9..7685865 100644
--- a/extensions/find_duplicates/gth-find-duplicates.c
+++ b/extensions/find_duplicates/gth-find-duplicates.c
@@ -985,7 +985,7 @@ gth_find_duplicates_exec (GthBrowser *browser,
}
self->priv->builder = _gtk_builder_new_from_file ("find-duplicates-dialog.ui", "find_duplicates");
- self->priv->duplicates_list = gth_file_list_new (gth_icon_view_new (), GTH_FILE_LIST_TYPE_NORMAL, FALSE);
+ self->priv->duplicates_list = gth_file_list_new (gth_grid_view_new (), GTH_FILE_LIST_TYPE_NORMAL, FALSE);
gth_file_selection_set_selection_mode (GTH_FILE_SELECTION (gth_file_list_get_view (GTH_FILE_LIST (self->priv->duplicates_list))), GTK_SELECTION_MULTIPLE);
gth_file_list_set_caption (GTH_FILE_LIST (self->priv->duplicates_list), "find-duplicates::n-duplicates,gth::file::display-size");
gth_file_list_set_thumb_size (GTH_FILE_LIST (self->priv->duplicates_list), 112);
diff --git a/extensions/flicker_utils/dlg-export-to-flickr.c b/extensions/flicker_utils/dlg-export-to-flickr.c
index 4650f8b..b30098b 100644
--- a/extensions/flicker_utils/dlg-export-to-flickr.c
+++ b/extensions/flicker_utils/dlg-export-to-flickr.c
@@ -572,9 +572,8 @@ dlg_export_to_flickr (FlickrServer *server,
/* Set the widget data */
- data->list_view = gth_file_list_new (gth_icon_view_new (), GTH_FILE_LIST_TYPE_NO_SELECTION, FALSE);
+ data->list_view = gth_file_list_new (gth_grid_view_new (), GTH_FILE_LIST_TYPE_NO_SELECTION, FALSE);
gth_file_list_set_thumb_size (GTH_FILE_LIST (data->list_view), 112);
- gth_file_view_set_spacing (GTH_FILE_VIEW (gth_file_list_get_view (GTH_FILE_LIST (data->list_view))), 0);
gth_file_list_enable_thumbs (GTH_FILE_LIST (data->list_view), TRUE);
gth_file_list_set_ignore_hidden (GTH_FILE_LIST (data->list_view), TRUE);
gth_file_list_set_caption (GTH_FILE_LIST (data->list_view), "none");
diff --git a/extensions/flicker_utils/dlg-import-from-flickr.c b/extensions/flicker_utils/dlg-import-from-flickr.c
index 09f6e3f..3bca374 100644
--- a/extensions/flicker_utils/dlg-import-from-flickr.c
+++ b/extensions/flicker_utils/dlg-import-from-flickr.c
@@ -549,12 +549,11 @@ dlg_import_from_flickr (FlickrServer *server,
/* Set the widget data */
- data->file_list = gth_file_list_new (gth_icon_view_new (), GTH_FILE_LIST_TYPE_NORMAL, FALSE);
+ data->file_list = gth_file_list_new (gth_grid_view_new (), GTH_FILE_LIST_TYPE_NORMAL, FALSE);
thumb_loader = gth_file_list_get_thumb_loader (GTH_FILE_LIST (data->file_list));
gth_thumb_loader_set_use_cache (thumb_loader, FALSE);
gth_thumb_loader_set_loader_func (thumb_loader, flickr_thumbnail_loader);
gth_file_list_set_thumb_size (GTH_FILE_LIST (data->file_list), FLICKR_SIZE_THUMBNAIL);
- gth_file_view_set_spacing (GTH_FILE_VIEW (gth_file_list_get_view (GTH_FILE_LIST (data->file_list))), 0);
gth_file_list_enable_thumbs (GTH_FILE_LIST (data->file_list), TRUE);
gth_file_list_set_ignore_hidden (GTH_FILE_LIST (data->file_list), TRUE);
gth_file_list_set_caption (GTH_FILE_LIST (data->file_list), "none");
diff --git a/extensions/photo_importer/dlg-photo-importer.c b/extensions/photo_importer/dlg-photo-importer.c
index 09250ee..4e70e02 100644
--- a/extensions/photo_importer/dlg-photo-importer.c
+++ b/extensions/photo_importer/dlg-photo-importer.c
@@ -625,7 +625,7 @@ dlg_photo_importer (GthBrowser *browser,
gtk_box_pack_start (GTK_BOX (GET_WIDGET ("source_box")), data->folder_chooser, TRUE, TRUE, 0);
}
- data->file_list = gth_file_list_new (gth_icon_view_new (), GTH_FILE_LIST_TYPE_NORMAL, FALSE);
+ data->file_list = gth_file_list_new (gth_grid_view_new (), GTH_FILE_LIST_TYPE_NORMAL, FALSE);
sort_type = gth_main_get_sort_type ("file::mtime");
gth_file_list_set_sort_func (GTH_FILE_LIST (data->file_list), sort_type->cmp_func, FALSE);
gth_file_list_enable_thumbs (GTH_FILE_LIST (data->file_list), TRUE);
diff --git a/extensions/photobucket/dlg-export-to-photobucket.c b/extensions/photobucket/dlg-export-to-photobucket.c
index 323e3ef..f69d632 100644
--- a/extensions/photobucket/dlg-export-to-photobucket.c
+++ b/extensions/photobucket/dlg-export-to-photobucket.c
@@ -627,9 +627,8 @@ dlg_export_to_photobucket (GthBrowser *browser,
/* Set the widget data */
- list_view = gth_file_list_new (gth_icon_view_new (), GTH_FILE_LIST_TYPE_NO_SELECTION, FALSE);
+ list_view = gth_file_list_new (gth_grid_view_new (), GTH_FILE_LIST_TYPE_NO_SELECTION, FALSE);
gth_file_list_set_thumb_size (GTH_FILE_LIST (list_view), 112);
- gth_file_view_set_spacing (GTH_FILE_VIEW (gth_file_list_get_view (GTH_FILE_LIST (list_view))), 0);
gth_file_list_enable_thumbs (GTH_FILE_LIST (list_view), TRUE);
gth_file_list_set_ignore_hidden (GTH_FILE_LIST (list_view), TRUE);
gth_file_list_set_caption (GTH_FILE_LIST (list_view), "none");
diff --git a/extensions/picasaweb/dlg-export-to-picasaweb.c b/extensions/picasaweb/dlg-export-to-picasaweb.c
index 5b1c454..4030b94 100644
--- a/extensions/picasaweb/dlg-export-to-picasaweb.c
+++ b/extensions/picasaweb/dlg-export-to-picasaweb.c
@@ -1014,9 +1014,8 @@ dlg_export_to_picasaweb (GthBrowser *browser,
/* Set the widget data */
- data->list_view = gth_file_list_new (gth_icon_view_new (), GTH_FILE_LIST_TYPE_NO_SELECTION, FALSE);
+ data->list_view = gth_file_list_new (gth_grid_view_new (), GTH_FILE_LIST_TYPE_NO_SELECTION, FALSE);
gth_file_list_set_thumb_size (GTH_FILE_LIST (data->list_view), 112);
- gth_file_view_set_spacing (GTH_FILE_VIEW (gth_file_list_get_view (GTH_FILE_LIST (data->list_view))), 0);
gth_file_list_enable_thumbs (GTH_FILE_LIST (data->list_view), TRUE);
gth_file_list_set_ignore_hidden (GTH_FILE_LIST (data->list_view), TRUE);
gth_file_list_set_caption (GTH_FILE_LIST (data->list_view), "none");
diff --git a/extensions/picasaweb/dlg-import-from-picasaweb.c b/extensions/picasaweb/dlg-import-from-picasaweb.c
index 989eba7..be1e4bc 100644
--- a/extensions/picasaweb/dlg-import-from-picasaweb.c
+++ b/extensions/picasaweb/dlg-import-from-picasaweb.c
@@ -1014,12 +1014,11 @@ dlg_import_from_picasaweb (GthBrowser *browser)
/* Set the widget data */
- data->file_list = gth_file_list_new (gth_icon_view_new (), GTH_FILE_LIST_TYPE_NORMAL, FALSE);
+ data->file_list = gth_file_list_new (gth_grid_view_new (), GTH_FILE_LIST_TYPE_NORMAL, FALSE);
thumb_loader = gth_file_list_get_thumb_loader (GTH_FILE_LIST (data->file_list));
gth_thumb_loader_set_use_cache (thumb_loader, FALSE);
gth_thumb_loader_set_loader_func (thumb_loader, picasa_web_thumbnail_loader);
gth_file_list_set_thumb_size (GTH_FILE_LIST (data->file_list), PICASA_WEB_THUMB_SIZE_SMALL);
- gth_file_view_set_spacing (GTH_FILE_VIEW (gth_file_list_get_view (GTH_FILE_LIST (data->file_list))), 0);
gth_file_list_enable_thumbs (GTH_FILE_LIST (data->file_list), TRUE);
gth_file_list_set_ignore_hidden (GTH_FILE_LIST (data->file_list), TRUE);
gth_file_list_set_caption (GTH_FILE_LIST (data->file_list), "none");
diff --git a/gthumb/Makefile.am b/gthumb/Makefile.am
index e200054..b0d72d7 100644
--- a/gthumb/Makefile.am
+++ b/gthumb/Makefile.am
@@ -37,8 +37,6 @@ PUBLIC_HEADER_FILES = \
gth-async-task.h \
gth-buffer-data.h \
gth-browser.h \
- gth-cell-renderer-caption.h \
- gth-cell-renderer-thumbnail.h \
gth-dumb-notebook.h \
gth-duplicable.h \
gth-embedded-dialog.h \
@@ -60,11 +58,11 @@ PUBLIC_HEADER_FILES = \
gth-filter-editor-dialog.h \
gth-filter-file.h \
gth-folder-tree.h \
+ gth-grid-view.h \
gth-histogram.h \
gth-histogram-view.h \
gth-hook.h \
gth-icon-cache.h \
- gth-icon-view.h \
gth-image.h \
gth-image-dragger.h \
gth-image-history.h \
@@ -75,7 +73,6 @@ PUBLIC_HEADER_FILES = \
gth-image-viewer.h \
gth-image-viewer-tool.h \
gth-info-bar.h \
- gth-list-view.h \
gth-load-file-data-task.h \
gth-location-chooser.h \
gth-main.h \
@@ -162,8 +159,6 @@ gthumb_SOURCES = \
gth-browser.c \
gth-browser-actions-callbacks.c \
gth-buffer-data.c \
- gth-cell-renderer-caption.c \
- gth-cell-renderer-thumbnail.c \
gth-dumb-notebook.c \
gth-duplicable.c \
gth-embedded-dialog.c \
@@ -185,11 +180,11 @@ gthumb_SOURCES = \
gth-filter-editor-dialog.c \
gth-filter-file.c \
gth-folder-tree.c \
+ gth-grid-view.c \
gth-histogram.c \
gth-histogram-view.c \
gth-hook.c \
gth-icon-cache.c \
- gth-icon-view.c \
gth-image.c \
gth-image-dragger.c \
gth-image-history.c \
@@ -200,7 +195,6 @@ gthumb_SOURCES = \
gth-image-viewer.c \
gth-image-viewer-tool.c \
gth-info-bar.c \
- gth-list-view.c \
gth-load-file-data-task.c \
gth-location-chooser.c \
gth-main.c \
@@ -282,12 +276,14 @@ gthumb_LDADD = \
$(NULL)
if RUN_IN_PLACE
-ui_dir = $(abs_top_srcdir)/data/ui
-icon_dir = $(abs_top_srcdir)/data/icons
+data_dir = $(abs_top_srcdir)/data
+ui_dir = $(data_dir)/ui
+icon_dir = $(data_dir)/icons
extensions_ui_dir = $(abs_top_srcdir)/extensions
extensions_dir = $(abs_top_builddir)/extensions
applications_dir = $(abs_top_builddir)/data
else
+data_dir = $(datadir)
ui_dir = $(pkgdatadir)/ui
icon_dir = $(pkgdatadir)/icons
extensions_ui_dir = $(pkgdatadir)/ui
@@ -313,7 +309,7 @@ gthumb_CFLAGS = \
-DGTHUMB_LOCALEDIR=\"$(datadir)/locale\" \
-DGTHUMB_PREFIX=\"$(prefix)\" \
-DGTHUMB_SYSCONFDIR=\"$(sysconfdir)\" \
- -DGTHUMB_DATADIR=\"$(datadir)\" \
+ -DGTHUMB_DATADIR=\"$(data_dir)\" \
-DGTHUMB_LIBDIR=\"$(libdir)\" \
-DGTHUMB_PKGDATADIR=\"$(pkgdatadir)\" \
-DGTHUMB_UI_DIR=\"$(ui_dir)\" \
diff --git a/gthumb/cairo-utils.c b/gthumb/cairo-utils.c
index a9b1182..c539824 100644
--- a/gthumb/cairo-utils.c
+++ b/gthumb/cairo-utils.c
@@ -56,6 +56,18 @@ _cairo_multiply_alpha (int color,
}
+gboolean
+_cairo_rectangle_contains_point (cairo_rectangle_int_t *rect,
+ int x,
+ int y)
+{
+ return ((x >= rect->x)
+ && (y >= rect->y)
+ && (x <= rect->x + rect->width)
+ && (y <= rect->y + rect->height));
+}
+
+
void
_gdk_color_to_cairo_color (GdkColor *g_color,
cairo_color_t *c_color)
diff --git a/gthumb/cairo-utils.h b/gthumb/cairo-utils.h
index 35868ef..e38fba2 100644
--- a/gthumb/cairo-utils.h
+++ b/gthumb/cairo-utils.h
@@ -134,6 +134,9 @@ extern const unsigned char cairo_channel[4];
int _cairo_multiply_alpha (int color,
int alpha);
+gboolean _cairo_rectangle_contains_point (cairo_rectangle_int_t *rect,
+ int x,
+ int y);
/* colors */
@@ -147,7 +150,7 @@ void _gdk_color_to_cairo_color_255 (GdkColor
void _cairo_clear_surface (cairo_surface_t **surface);
cairo_surface_metadata_t *
- _cairo_image_surface_get_metadata (cairo_surface_t *surface);
+ _cairo_image_surface_get_metadata (cairo_surface_t *surface);
gboolean _cairo_image_surface_get_has_alpha (cairo_surface_t *surface);
cairo_surface_t * _cairo_image_surface_copy (cairo_surface_t *surface);
cairo_surface_t * _cairo_image_surface_copy_subsurface (cairo_surface_t *surface,
diff --git a/gthumb/gth-browser-actions-entries.h b/gthumb/gth-browser-actions-entries.h
index 89e91e9..405e22f 100644
--- a/gthumb/gth-browser-actions-entries.h
+++ b/gthumb/gth-browser-actions-entries.h
@@ -86,7 +86,7 @@ static GthActionEntryExt gth_browser_action_entries[] = {
G_CALLBACK (gth_browser_activate_action_edit_preferences) },
{ "Edit_SelectAll", GTK_STOCK_SELECT_ALL,
- NULL, NULL,
+ NULL, "<control>A",
NULL,
GTH_ACTION_FLAG_NONE,
G_CALLBACK (gth_browser_activate_action_edit_select_all) },
diff --git a/gthumb/gth-browser.c b/gthumb/gth-browser.c
index a19b2fa..fe9bdfc 100644
--- a/gthumb/gth-browser.c
+++ b/gthumb/gth-browser.c
@@ -41,11 +41,10 @@
#include "gth-filter.h"
#include "gth-filterbar.h"
#include "gth-folder-tree.h"
+#include "gth-grid-view.h"
#include "gth-icon-cache.h"
-#include "gth-icon-view.h"
#include "gth-info-bar.h"
#include "gth-image-preloader.h"
-#include "gth-list-view.h"
#include "gth-location-chooser.h"
#include "gth-main.h"
#include "gth-marshal.h"
@@ -1651,6 +1650,7 @@ load_data_continue (LoadData *load_data,
file_view = gth_browser_get_file_list_view (browser);
gth_file_view_scroll_to (GTH_FILE_VIEW (file_view), pos, 0.5);
gth_file_selection_select (GTH_FILE_SELECTION (file_view), pos);
+ gth_file_view_set_cursor (GTH_FILE_VIEW (file_view), pos);
}
}
gth_browser_update_sensitivity (browser);
@@ -3406,6 +3406,7 @@ gth_file_list_button_press_cb (GtkWidget *widget,
if ((pos >= 0) && ! gth_file_selection_is_selected (GTH_FILE_SELECTION (file_view), pos)) {
gth_file_selection_unselect_all (GTH_FILE_SELECTION (file_view));
gth_file_selection_select (GTH_FILE_SELECTION (file_view), pos);
+ gth_file_view_set_cursor (GTH_FILE_VIEW (file_view), pos);
}
gth_file_list_popup_menu (browser, event);
@@ -3420,6 +3421,7 @@ gth_file_list_button_press_cb (GtkWidget *widget,
if ((pos >= 0) && ! gth_file_selection_is_selected (GTH_FILE_SELECTION (file_view), pos)) {
gth_file_selection_unselect_all (GTH_FILE_SELECTION (file_view));
gth_file_selection_select (GTH_FILE_SELECTION (file_view), pos);
+ gth_file_view_set_cursor (GTH_FILE_VIEW (file_view), pos);
}
return TRUE;
}
@@ -4274,9 +4276,9 @@ gth_browser_init (GthBrowser *browser)
gtk_widget_set_size_request (browser->priv->viewer_sidebar_alignment, g_settings_get_int (browser->priv->browser_settings, PREF_BROWSER_BROWSER_SIDEBAR_WIDTH), -1);
gtk_paned_pack2 (GTK_PANED (browser->priv->viewer_sidebar_pane), browser->priv->viewer_sidebar_alignment, FALSE, FALSE);
- browser->priv->thumbnail_list = gth_file_list_new (gth_icon_view_new (), (viewer_thumbnails_orientation == GTK_ORIENTATION_HORIZONTAL) ? GTH_FILE_LIST_TYPE_H_SIDEBAR : GTH_FILE_LIST_TYPE_V_SIDEBAR, TRUE);
+ browser->priv->thumbnail_list = gth_file_list_new (gth_grid_view_new (), (viewer_thumbnails_orientation == GTK_ORIENTATION_HORIZONTAL) ? GTH_FILE_LIST_TYPE_H_SIDEBAR : GTH_FILE_LIST_TYPE_V_SIDEBAR, TRUE);
gth_file_list_set_caption (GTH_FILE_LIST (browser->priv->thumbnail_list), "none");
- gth_file_view_set_spacing (GTH_FILE_VIEW (gth_file_list_get_view (GTH_FILE_LIST (browser->priv->thumbnail_list))), 0);
+ gth_grid_view_set_cell_spacing (GTH_GRID_VIEW (gth_file_list_get_view (GTH_FILE_LIST (browser->priv->thumbnail_list))), 0);
gth_file_list_set_thumb_size (GTH_FILE_LIST (browser->priv->thumbnail_list), 95);
if (viewer_thumbnails_orientation == GTK_ORIENTATION_HORIZONTAL)
gtk_paned_pack2 (GTK_PANED (browser->priv->viewer_thumbnails_pane), browser->priv->thumbnail_list, FALSE, FALSE);
@@ -4469,7 +4471,7 @@ gth_browser_init (GthBrowser *browser)
/* the file list */
- browser->priv->file_list = gth_file_list_new (gth_icon_view_new (), GTH_FILE_LIST_TYPE_NORMAL, TRUE);
+ browser->priv->file_list = gth_file_list_new (gth_grid_view_new (), GTH_FILE_LIST_TYPE_NORMAL, TRUE);
gth_browser_set_sort_order (browser,
gth_main_get_sort_type (g_settings_get_string (browser->priv->browser_settings, PREF_BROWSER_SORT_TYPE)),
g_settings_get_boolean (browser->priv->browser_settings, PREF_BROWSER_SORT_INVERSE));
diff --git a/gthumb/gth-file-list.c b/gthumb/gth-file-list.c
index 7a00be1..7554445 100644
--- a/gthumb/gth-file-list.c
+++ b/gthumb/gth-file-list.c
@@ -24,8 +24,6 @@
#include <glib/gi18n.h>
#include <gtk/gtk.h>
#include "glib-utils.h"
-#include "gth-cell-renderer-caption.h"
-#include "gth-cell-renderer-thumbnail.h"
#include "gth-dumb-notebook.h"
#include "gth-empty-list.h"
#include "gth-file-list.h"
@@ -127,9 +125,6 @@ struct _GthFileListPrivateData
guint restart_thumb_update;
GList *queue; /* list of GthFileListOp */
GList *jobs; /* list of ThumbnailJob */
- GtkCellRenderer *thumbnail_renderer;
- GtkCellRenderer *caption_renderer;
- GtkCellRenderer *checkbox_renderer;
gboolean cancelling;
guint update_event;
gboolean visibility_changed;
@@ -253,7 +248,7 @@ _gth_file_list_queue_op (GthFileList *file_list,
/* -- gth_file_list -- */
-G_DEFINE_TYPE (GthFileList, gth_file_list, GTK_TYPE_VBOX)
+G_DEFINE_TYPE (GthFileList, gth_file_list, GTK_TYPE_BOX)
static void
@@ -293,9 +288,13 @@ gth_file_list_get_preferred_width (GtkWidget *widget,
{
GthFileList *file_list = GTH_FILE_LIST (widget);
GtkWidget *vscrollbar;
+ const int border = 1;
- *minimum_width = file_list->priv->thumb_size + (THUMBNAIL_BORDER * 2);
- *natural_width = *minimum_width;
+ gtk_widget_get_preferred_width (file_list->priv->view, minimum_width, natural_width);
+ if (minimum_width)
+ *minimum_width += border * 2;
+ if (natural_width)
+ *natural_width += border * 2;
vscrollbar = gtk_scrolled_window_get_vscrollbar (GTK_SCROLLED_WINDOW (file_list->priv->scrolled_window));
if (gtk_widget_get_visible (vscrollbar)) {
@@ -320,9 +319,13 @@ gth_file_list_get_preferred_height (GtkWidget *widget,
int *natural_height)
{
GthFileList *file_list = GTH_FILE_LIST (widget);
+ const int border = 1;
- *minimum_height = file_list->priv->thumb_size + (THUMBNAIL_BORDER * 2);
- *natural_height = *minimum_height;
+ gtk_widget_get_preferred_height (file_list->priv->view, minimum_height, natural_height);
+ if (minimum_height)
+ *minimum_height += border * 2;
+ if (natural_height)
+ *natural_height += border * 2;
}
@@ -516,37 +519,6 @@ file_view_drag_data_get_cb (GtkWidget *widget,
static void
-checkbox_toggled_cb (GtkCellRendererToggle *cell_renderer,
- char *path,
- gpointer user_data)
-{
- GthFileList *file_list = user_data;
- GtkTreePath *tpath;
- GthFileStore *file_store;
- GtkTreeIter iter;
-
- tpath = gtk_tree_path_new_from_string (path);
- if (tpath == NULL)
- return;
-
- file_store = (GthFileStore*) gth_file_view_get_model (GTH_FILE_VIEW (file_list->priv->view));
- if (gtk_tree_model_get_iter (GTK_TREE_MODEL (file_store), &iter, tpath)) {
- gboolean checked;
-
- gtk_tree_model_get (GTK_TREE_MODEL (file_store), &iter,
- GTH_FILE_STORE_CHECKED_COLUMN, &checked,
- -1);
- gth_file_store_set (file_store,
- &iter,
- GTH_FILE_STORE_CHECKED_COLUMN, ! checked,
- -1);
- }
-
- gtk_tree_path_free (tpath);
-}
-
-
-static void
_gth_file_list_update_orientation (GthFileList *file_list)
{
GtkPolicyType hscrollbar_policy;
@@ -569,34 +541,6 @@ _gth_file_list_update_orientation (GthFileList *file_list)
static void
-_gth_file_list_set_type (GthFileList *file_list,
- GthFileListType list_type)
-{
- file_list->priv->type = list_type;
-
- if ((file_list->priv->type == GTH_FILE_LIST_TYPE_SELECTOR) || (file_list->priv->type == GTH_FILE_LIST_TYPE_NO_SELECTION))
- gth_file_selection_set_selection_mode (GTH_FILE_SELECTION (file_list->priv->view), GTK_SELECTION_NONE);
- else if ((file_list->priv->type == GTH_FILE_LIST_TYPE_H_SIDEBAR) || (file_list->priv->type == GTH_FILE_LIST_TYPE_V_SIDEBAR))
- gth_file_selection_set_selection_mode (GTH_FILE_SELECTION (file_list->priv->view), GTK_SELECTION_SINGLE);
- else
- gth_file_selection_set_selection_mode (GTH_FILE_SELECTION (file_list->priv->view), GTK_SELECTION_MULTIPLE);
-
- g_object_set (file_list->priv->checkbox_renderer,
- "visible", ((file_list->priv->type == GTH_FILE_LIST_TYPE_BROWSER) || (file_list->priv->type == GTH_FILE_LIST_TYPE_SELECTOR)),
- NULL);
-
- /* use the fixed size for horizontal sidebars to view a single row at
- * a time. */
-
- g_object_set (file_list->priv->thumbnail_renderer,
- "fixed_size", TRUE /* (file_list->priv->type == GTH_FILE_LIST_TYPE_H_SIDEBAR) */,
- NULL);
-
- _gth_file_list_update_orientation (file_list);
-}
-
-
-static void
file_store_visibility_changed_cb (GthFileStore *file_store,
GthFileList *file_list)
{
@@ -617,23 +561,12 @@ file_store_rows_reordered_cb (GtkTreeModel *tree_model,
static void
-file_store_thumbnail_changed_cb (GthFileStore *file_store,
- gpointer user_data)
-{
- GthFileList *file_list = user_data;
- gtk_widget_queue_draw (file_list->priv->view);
-}
-
-
-static void
gth_file_list_construct (GthFileList *file_list,
GtkWidget *file_view,
GthFileListType list_type,
gboolean enable_drag_drop)
{
- GtkCellRenderer *renderer;
- GthFileStore *model;
- GtkCellLayout *cell_layout;
+ GthFileStore *model;
file_list->priv->thumb_loader = gth_thumb_loader_new (file_list->priv->thumb_size);
file_list->priv->icon_cache = gth_icon_cache_new (gtk_icon_theme_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (file_list))), file_list->priv->thumb_size / 2);
@@ -674,10 +607,6 @@ gth_file_list_construct (GthFileList *file_list,
"rows-reordered",
G_CALLBACK (file_store_rows_reordered_cb),
file_list);
- g_signal_connect (model,
- "thumbnail-changed",
- G_CALLBACK (file_store_thumbnail_changed_cb),
- file_list);
if (enable_drag_drop) {
GtkTargetList *target_list;
@@ -703,65 +632,7 @@ gth_file_list_construct (GthFileList *file_list,
file_list);
}
- /* checkbox */
-
- file_list->priv->checkbox_renderer = renderer = gtk_cell_renderer_toggle_new ();
- g_signal_connect (file_list->priv->checkbox_renderer,
- "toggled",
- G_CALLBACK (checkbox_toggled_cb),
- file_list);
-
- cell_layout = gth_file_view_add_renderer (GTH_FILE_VIEW (file_list->priv->view),
- GTH_FILE_VIEW_RENDERER_CHECKBOX,
- file_list->priv->checkbox_renderer);
- gtk_cell_layout_set_attributes (cell_layout,
- renderer,
- "active", GTH_FILE_STORE_CHECKED_COLUMN,
- NULL);
-
- /* thumbnail */
-
- file_list->priv->thumbnail_renderer = renderer = gth_cell_renderer_thumbnail_new ();
-
- cell_layout = gth_file_view_add_renderer (GTH_FILE_VIEW (file_list->priv->view),
- GTH_FILE_VIEW_RENDERER_THUMBNAIL,
- file_list->priv->thumbnail_renderer);
- gtk_cell_layout_set_attributes (cell_layout,
- renderer,
- "thumbnail", GTH_FILE_STORE_THUMBNAIL_COLUMN,
- "is_icon", GTH_FILE_STORE_IS_ICON_COLUMN,
- "file", GTH_FILE_STORE_FILE_DATA_COLUMN,
- NULL);
- if (file_list->priv->type == GTH_FILE_LIST_TYPE_BROWSER)
- gtk_cell_layout_add_attribute (cell_layout,
- renderer,
- "selected",
- GTH_FILE_STORE_CHECKED_COLUMN);
- else if (file_list->priv->type == GTH_FILE_LIST_TYPE_SELECTOR)
- gtk_cell_layout_add_attribute (cell_layout,
- renderer,
- "checked",
- GTH_FILE_STORE_CHECKED_COLUMN);
-
- /* caption */
-
- file_list->priv->caption_renderer = renderer = gth_cell_renderer_caption_new ();
-
- cell_layout = gth_file_view_add_renderer (GTH_FILE_VIEW (file_list->priv->view),
- GTH_FILE_VIEW_RENDERER_TEXT,
- file_list->priv->caption_renderer);
- gtk_cell_layout_set_attributes (cell_layout,
- renderer,
- "file", GTH_FILE_STORE_FILE_DATA_COLUMN,
- NULL);
-
- gth_file_view_update_attributes (GTH_FILE_VIEW (file_list->priv->view),
- file_list->priv->checkbox_renderer,
- file_list->priv->thumbnail_renderer,
- file_list->priv->caption_renderer,
- file_list->priv->thumb_size);
-
- _gth_file_list_set_type (file_list, list_type);
+ gth_file_list_set_type (file_list, list_type);
/* pack the widgets together */
@@ -781,7 +652,7 @@ gth_file_list_construct (GthFileList *file_list,
}
-GtkWidget*
+GtkWidget *
gth_file_list_new (GtkWidget *file_view,
GthFileListType list_type,
gboolean enable_drag_drop)
@@ -799,7 +670,30 @@ void
gth_file_list_set_type (GthFileList *file_list,
GthFileListType list_type)
{
- _gth_file_list_set_type (file_list, list_type);
+ file_list->priv->type = list_type;
+
+ if ((file_list->priv->type == GTH_FILE_LIST_TYPE_SELECTOR) || (file_list->priv->type == GTH_FILE_LIST_TYPE_NO_SELECTION))
+ gth_file_selection_set_selection_mode (GTH_FILE_SELECTION (file_list->priv->view), GTK_SELECTION_NONE);
+ else if ((file_list->priv->type == GTH_FILE_LIST_TYPE_H_SIDEBAR) || (file_list->priv->type == GTH_FILE_LIST_TYPE_V_SIDEBAR))
+ gth_file_selection_set_selection_mode (GTH_FILE_SELECTION (file_list->priv->view), GTK_SELECTION_SINGLE);
+ else
+ gth_file_selection_set_selection_mode (GTH_FILE_SELECTION (file_list->priv->view), GTK_SELECTION_MULTIPLE);
+
+
+#if 0 /* FIXME */
+ g_object_set (file_list->priv->checkbox_renderer,
+ "visible", ((file_list->priv->type == GTH_FILE_LIST_TYPE_BROWSER) || (file_list->priv->type == GTH_FILE_LIST_TYPE_SELECTOR)),
+ NULL);
+
+ /* use the fixed size for horizontal sidebars to view a single row at
+ * a time. */
+
+ g_object_set (file_list->priv->thumbnail_renderer,
+ "fixed_size", TRUE /* (file_list->priv->type == GTH_FILE_LIST_TYPE_H_SIDEBAR) */,
+ NULL);
+#endif
+
+ _gth_file_list_update_orientation (file_list);
}
@@ -901,7 +795,6 @@ gfl_clear_list (GthFileList *file_list,
gth_file_selection_unselect_all (GTH_FILE_SELECTION (file_list->priv->view));
file_store = (GthFileStore*) gth_file_view_get_model (GTH_FILE_VIEW (file_list->priv->view));
- gth_cell_renderer_caption_clear_cache (GTH_CELL_RENDERER_CAPTION (file_list->priv->caption_renderer));
gth_file_store_clear (file_store);
g_hash_table_remove_all (file_list->priv->thumb_data);
@@ -1137,13 +1030,10 @@ static void
gfl_set_files (GthFileList *file_list,
GList *files)
{
- gth_thumb_loader_set_save_thumbnails (file_list->priv->thumb_loader,
- g_settings_get_boolean (file_list->priv->settings, PREF_BROWSER_SAVE_THUMBNAILS));
- gth_thumb_loader_set_max_file_size (file_list->priv->thumb_loader,
- g_settings_get_int (file_list->priv->settings, PREF_BROWSER_THUMBNAIL_LIMIT));
+ gth_thumb_loader_set_save_thumbnails (file_list->priv->thumb_loader, g_settings_get_boolean (file_list->priv->settings, PREF_BROWSER_SAVE_THUMBNAILS));
+ gth_thumb_loader_set_max_file_size (file_list->priv->thumb_loader, g_settings_get_int (file_list->priv->settings, PREF_BROWSER_THUMBNAIL_LIMIT));
gth_file_selection_unselect_all (GTH_FILE_SELECTION (file_list->priv->view));
- gth_cell_renderer_caption_clear_cache (GTH_CELL_RENDERER_CAPTION (file_list->priv->caption_renderer));
gth_file_store_clear ((GthFileStore*) gth_file_view_get_model (GTH_FILE_VIEW (file_list->priv->view)));
g_hash_table_remove_all (file_list->priv->thumb_data);
gfl_add_files (file_list, files, -1);
@@ -1323,19 +1213,13 @@ gth_file_list_set_thumb_size (GthFileList *file_list,
file_list->priv->thumb_size = size;
gth_thumb_loader_set_requested_size (file_list->priv->thumb_loader, size);
- gth_thumb_loader_set_save_thumbnails (file_list->priv->thumb_loader,
- g_settings_get_boolean (file_list->priv->settings, PREF_BROWSER_SAVE_THUMBNAILS));
- gth_thumb_loader_set_max_file_size (file_list->priv->thumb_loader,
- g_settings_get_int (file_list->priv->settings, PREF_BROWSER_THUMBNAIL_LIMIT));
+ gth_thumb_loader_set_save_thumbnails (file_list->priv->thumb_loader, g_settings_get_boolean (file_list->priv->settings, PREF_BROWSER_SAVE_THUMBNAILS));
+ gth_thumb_loader_set_max_file_size (file_list->priv->thumb_loader, g_settings_get_int (file_list->priv->settings, PREF_BROWSER_THUMBNAIL_LIMIT));
gth_icon_cache_free (file_list->priv->icon_cache);
file_list->priv->icon_cache = gth_icon_cache_new (gtk_icon_theme_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (file_list))), size / 2);
- gth_file_view_update_attributes (GTH_FILE_VIEW (file_list->priv->view),
- file_list->priv->checkbox_renderer,
- file_list->priv->thumbnail_renderer,
- file_list->priv->caption_renderer,
- file_list->priv->thumb_size);
+ gth_file_view_set_thumbnail_size (GTH_FILE_VIEW (file_list->priv->view), file_list->priv->thumb_size);
_gth_file_list_update_orientation (file_list);
}
@@ -1345,10 +1229,7 @@ void
gth_file_list_set_caption (GthFileList *file_list,
const char *attributes)
{
- g_object_set (file_list->priv->caption_renderer,
- "file-attributes", attributes,
- NULL);
- gtk_widget_queue_resize (file_list->priv->view);
+ gth_file_view_set_caption (GTH_FILE_VIEW (file_list->priv->view), attributes);
}
@@ -2035,4 +1916,3 @@ gth_file_list_prev_file (GthFileList *file_list,
return (scan != NULL) ? pos : -1;
}
-
diff --git a/gthumb/gth-file-list.h b/gthumb/gth-file-list.h
index 5ae1ada..cfa5971 100644
--- a/gthumb/gth-file-list.h
+++ b/gthumb/gth-file-list.h
@@ -53,12 +53,12 @@ typedef struct _GthFileListClass GthFileListClass;
typedef struct _GthFileListPrivateData GthFileListPrivateData;
struct _GthFileList {
- GtkVBox __parent;
+ GtkBox __parent;
GthFileListPrivateData *priv;
};
struct _GthFileListClass {
- GtkVBoxClass __parent;
+ GtkBoxClass __parent;
};
GType gth_file_list_get_type (void);
diff --git a/gthumb/gth-file-store.c b/gthumb/gth-file-store.c
index e24bb25..8728f7c 100644
--- a/gthumb/gth-file-store.c
+++ b/gthumb/gth-file-store.c
@@ -23,6 +23,7 @@
#include <glib/gi18n.h>
#include "glib-utils.h"
#include "gth-file-store.h"
+#include "gth-marshal.h"
#undef DEBUG_FILE_STORE
@@ -246,9 +247,11 @@ gth_file_store_class_init (GthFileStoreClass *klass)
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GthFileStoreClass, thumbnail_changed),
NULL, NULL,
- g_cclosure_marshal_VOID__VOID,
+ gth_marshal_VOID__BOXED_BOXED,
G_TYPE_NONE,
- 0);
+ 2,
+ GTK_TYPE_TREE_PATH,
+ GTK_TYPE_TREE_ITER);
}
@@ -1533,6 +1536,40 @@ _gth_file_store_list_changed (GthFileStore *file_store)
}
+static void
+_gth_file_store_thumbnail_changed (GthFileStore *file_store,
+ GthFileRow *row)
+{
+ GtkTreePath *path;
+ GtkTreeIter iter;
+
+ path = gtk_tree_path_new ();
+ gtk_tree_path_append_index (path, row->pos);
+
+ iter.stamp = file_store->priv->stamp;
+ iter.user_data = row;
+
+ g_signal_emit (file_store, gth_file_store_signals[THUMBNAIL_CHANGED], 0, path, &iter);
+
+ gtk_tree_path_free (path);
+}
+
+
+static void
+_gth_file_store_thumbnails_changed (GthFileStore *file_store)
+{
+ int i;
+
+ for (i = 0; i < file_store->priv->num_rows; i++) {
+ GthFileRow *row = file_store->priv->rows[i];
+
+ if (row->visible && row->changed)
+ _gth_file_store_thumbnail_changed (file_store, row);
+ row->changed = FALSE;
+ }
+}
+
+
void
gth_file_store_exec_set (GthFileStore *file_store)
{
@@ -1541,11 +1578,11 @@ gth_file_store_exec_set (GthFileStore *file_store)
* emit the 'row-changed' signal for each row, which causes the
* GtkIconView to invalidate the size of all the items, and instead
* emit a single 'thumbnail-changed' signal that can be used to just
- * redraw GtkIconView (as done in gth-file-list.c). */
+ * redraw the GthFileView. */
if (file_store->priv->update_filter || file_store->priv->check_changed)
_gth_file_store_list_changed (file_store);
else
- g_signal_emit (file_store, gth_file_store_signals[THUMBNAIL_CHANGED], 0, NULL);
+ _gth_file_store_thumbnails_changed (file_store);
_gth_file_store_clear_queue (file_store);
diff --git a/gthumb/gth-file-store.h b/gthumb/gth-file-store.h
index 544ac9c..8093e83 100644
--- a/gthumb/gth-file-store.h
+++ b/gthumb/gth-file-store.h
@@ -59,7 +59,9 @@ struct _GthFileStoreClass
void (*visibility_changed) (GthFileStore *self);
void (*check_changed) (GthFileStore *self);
- void (*thumbnail_changed) (GthFileStore *self);
+ void (*thumbnail_changed) (GtkTreeModel *tree_model,
+ GtkTreePath *path,
+ GtkTreeIter *iter);
};
GType gth_file_store_get_type (void) G_GNUC_CONST;
diff --git a/gthumb/gth-file-view.c b/gthumb/gth-file-view.c
index ea5910d..66a644a 100644
--- a/gthumb/gth-file-view.c
+++ b/gthumb/gth-file-view.c
@@ -19,10 +19,15 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+#include <config.h>
#include "gth-file-view.h"
+#define DEFAULT_THUMBNAIL_SIZE 128
+
+
enum {
+ CURSOR_CHANGED,
FILE_ACTIVATED,
LAST_SIGNAL
};
@@ -37,20 +42,50 @@ G_DEFINE_INTERFACE (GthFileView, gth_file_view, 0)
static void
gth_file_view_default_init (GthFileViewInterface *iface)
{
- static gboolean initialized = FALSE;
-
- if (! initialized) {
- gth_file_view_signals[FILE_ACTIVATED] =
- g_signal_new ("file-activated",
- GTH_TYPE_FILE_VIEW,
- G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (GthFileViewInterface, file_activated),
- NULL, NULL,
- g_cclosure_marshal_VOID__BOXED,
- G_TYPE_NONE, 1,
- GTK_TYPE_TREE_PATH);
- initialized = TRUE;
- }
+ /* signals */
+
+ gth_file_view_signals[CURSOR_CHANGED] =
+ g_signal_new ("cursor-changed",
+ GTH_TYPE_FILE_VIEW,
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GthFileViewInterface, cursor_changed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__INT,
+ G_TYPE_NONE, 1,
+ G_TYPE_INT);
+
+ gth_file_view_signals[FILE_ACTIVATED] =
+ g_signal_new ("file-activated",
+ GTH_TYPE_FILE_VIEW,
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GthFileViewInterface, file_activated),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__BOXED,
+ G_TYPE_NONE, 1,
+ GTK_TYPE_TREE_PATH);
+
+ /* properties */
+
+ g_object_interface_install_property (iface,
+ g_param_spec_string ("caption",
+ "Caption",
+ "The file attributes to view in the caption",
+ "none",
+ G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT));
+ g_object_interface_install_property (iface,
+ g_param_spec_object ("model",
+ "Data Store",
+ "The data to view",
+ GTK_TYPE_TREE_MODEL,
+ G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT));
+ g_object_interface_install_property (iface,
+ g_param_spec_int ("thumbnail-size",
+ "Thumbnail size",
+ "The max width and height of the thumbnails",
+ 0,
+ G_MAXINT32,
+ DEFAULT_THUMBNAIL_SIZE,
+ G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT));
}
@@ -58,14 +93,68 @@ void
gth_file_view_set_model (GthFileView *self,
GtkTreeModel *model)
{
- GTH_FILE_VIEW_GET_INTERFACE (self)->set_model (self, model);
+ g_return_if_fail (GTH_IS_FILE_VIEW (self));
+
+ g_object_set (self, "model", model, NULL);
}
GtkTreeModel *
gth_file_view_get_model (GthFileView *self)
{
- return GTH_FILE_VIEW_GET_INTERFACE (self)->get_model (self);
+ GtkTreeModel *model;
+
+ g_return_val_if_fail (GTH_IS_FILE_VIEW (self), NULL);
+
+ g_object_get (self, "model", &model, NULL);
+
+ return model;
+}
+
+
+void
+gth_file_view_set_caption (GthFileView *self,
+ const char *attributes)
+{
+ g_return_if_fail (GTH_IS_FILE_VIEW (self));
+
+ g_object_set (self, "caption", attributes, NULL);
+}
+
+
+char *
+gth_file_view_get_caption (GthFileView *self)
+{
+ char *attributes;
+
+ g_return_val_if_fail (GTH_IS_FILE_VIEW (self), NULL);
+
+ g_object_get (self, "caption", &attributes, NULL);
+
+ return attributes;
+}
+
+
+void
+gth_file_view_set_thumbnail_size (GthFileView *self,
+ int value)
+{
+ g_return_if_fail (GTH_IS_FILE_VIEW (self));
+
+ g_object_set (self, "thumbnail-size", value, NULL);
+}
+
+
+gboolean
+gth_file_view_get_thumbnail_size (GthFileView *self)
+{
+ int value;
+
+ g_return_val_if_fail (GTH_IS_FILE_VIEW (self), FALSE);
+
+ g_object_get (self, "thumbnail-size", &value, NULL);
+
+ return value;
}
@@ -113,7 +202,14 @@ void
gth_file_view_activated (GthFileView *self,
int pos)
{
- GTH_FILE_VIEW_GET_INTERFACE (self)->activated (self, pos);
+ GtkTreePath *path;
+
+ g_return_if_fail (GTH_IS_FILE_VIEW (self));
+
+ path = gtk_tree_path_new_from_indices (pos, -1);
+ g_signal_emit (self, gth_file_view_signals[FILE_ACTIVATED], 0, path);
+
+ gtk_tree_path_free (path);
}
@@ -121,7 +217,9 @@ void
gth_file_view_set_cursor (GthFileView *self,
int pos)
{
- GTH_FILE_VIEW_GET_INTERFACE (self)->set_cursor (self, pos);
+ g_return_if_fail (GTH_IS_FILE_VIEW (self));
+
+ g_signal_emit (self, gth_file_view_signals[CURSOR_CHANGED], 0, pos);
}
@@ -133,14 +231,6 @@ gth_file_view_get_cursor (GthFileView *self)
void
-gth_file_view_set_spacing (GthFileView *self,
- int spacing)
-{
- GTH_FILE_VIEW_GET_INTERFACE (self)->set_spacing (self, spacing);
-}
-
-
-void
gth_file_view_enable_drag_source (GthFileView *self,
GdkModifierType start_button_mask,
const GtkTargetEntry *targets,
@@ -187,36 +277,9 @@ gth_file_view_set_drag_dest_pos (GthFileView *self,
}
-GtkCellLayout *
-gth_file_view_add_renderer (GthFileView *self,
- GthFileViewRendererType renderer_type,
- GtkCellRenderer *renderer)
-{
- return GTH_FILE_VIEW_GET_INTERFACE (self)->add_renderer (self, renderer_type, renderer);
-}
-
-
-void
-gth_file_view_update_attributes (GthFileView *self,
- GtkCellRenderer *checkbox_renderer,
- GtkCellRenderer *thumbnail_renderer,
- GtkCellRenderer *text_renderer,
- int thumb_size)
-{
- GTH_FILE_VIEW_GET_INTERFACE (self)->update_attributes (self, checkbox_renderer, thumbnail_renderer, text_renderer, thumb_size);
-}
-
-
-gboolean
-gth_file_view_truncate_metadata (GthFileView *self)
-{
- return GTH_FILE_VIEW_GET_INTERFACE (self)->truncate_metadata (self);
-}
-
-
void
-gth_file_view_activate_file (GthFileView *self,
- GtkTreePath *path)
+gth_file_view_get_drag_dest_pos (GthFileView *self,
+ int *pos)
{
- g_signal_emit (self, gth_file_view_signals[FILE_ACTIVATED], 0, path);
+ GTH_FILE_VIEW_GET_INTERFACE (self)->get_drag_dest_pos (self, pos);
}
diff --git a/gthumb/gth-file-view.h b/gthumb/gth-file-view.h
index 100225f..f38828f 100644
--- a/gthumb/gth-file-view.h
+++ b/gthumb/gth-file-view.h
@@ -36,8 +36,6 @@ G_BEGIN_DECLS
typedef struct _GthFileView GthFileView;
typedef struct _GthFileViewInterface GthFileViewInterface;
-#define THUMBNAIL_BORDER (8 * 2)
-
typedef enum {
GTH_VISIBILITY_NONE,
GTH_VISIBILITY_FULL,
@@ -57,14 +55,13 @@ struct _GthFileViewInterface {
/*< signals >*/
+ void (*cursor_changed) (GthFileView *self,
+ int pos);
void (*file_activated) (GthFileView *self,
- GtkTreePath *path);
+ int pos);
/*< virtual functions >*/
- void (*set_model) (GthFileView *self,
- GtkTreeModel *model);
- GtkTreeModel * (*get_model) (GthFileView *self);
void (*scroll_to) (GthFileView *self,
int pos,
double yalign);
@@ -75,13 +72,7 @@ struct _GthFileViewInterface {
int y);
int (*get_first_visible) (GthFileView *self);
int (*get_last_visible) (GthFileView *self);
- void (*activated) (GthFileView *self,
- int pos);
- void (*set_cursor) (GthFileView *self,
- int pos);
int (*get_cursor) (GthFileView *self);
- void (*set_spacing) (GthFileView *self,
- int spacing);
void (*enable_drag_source) (GthFileView *self,
GdkModifierType start_button_mask,
const GtkTargetEntry *targets,
@@ -99,21 +90,21 @@ struct _GthFileViewInterface {
int y,
guint time,
int *pos);
- GtkCellLayout * (*add_renderer) (GthFileView *self,
- GthFileViewRendererType renderer_type,
- GtkCellRenderer *renderer);
- void (*update_attributes) (GthFileView *self,
- GtkCellRenderer *checkbox_renderer,
- GtkCellRenderer *thumbnail_renderer,
- GtkCellRenderer *text_renderer,
- int thumb_size);
- gboolean (*truncate_metadata) (GthFileView *self);
+ void (*get_drag_dest_pos) (GthFileView *self,
+ int *pos);
+
};
GType gth_file_view_get_type (void);
void gth_file_view_set_model (GthFileView *self,
GtkTreeModel *model);
GtkTreeModel * gth_file_view_get_model (GthFileView *self);
+void gth_file_view_set_caption (GthFileView *self,
+ const char *attributes);
+char * gth_file_view_get_caption (GthFileView *self);
+void gth_file_view_set_thumbnail_size (GthFileView *self,
+ int value);
+gboolean gth_file_view_get_thumbnail_size (GthFileView *self);
void gth_file_view_scroll_to (GthFileView *self,
int pos,
double yalign);
@@ -129,8 +120,6 @@ void gth_file_view_activated (GthFileView *self,
void gth_file_view_set_cursor (GthFileView *self,
int pos);
int gth_file_view_get_cursor (GthFileView *self);
-void gth_file_view_set_spacing (GthFileView *self,
- int spacing);
void gth_file_view_enable_drag_source (GthFileView *self,
GdkModifierType start_button_mask,
const GtkTargetEntry *targets,
@@ -148,17 +137,8 @@ void gth_file_view_set_drag_dest_pos (GthFileView *self,
int y,
guint time,
int *pos);
-GtkCellLayout * gth_file_view_add_renderer (GthFileView *self,
- GthFileViewRendererType renderer_type,
- GtkCellRenderer *renderer);
-void gth_file_view_update_attributes (GthFileView *self,
- GtkCellRenderer *checkbox_renderer,
- GtkCellRenderer *thumbnail_renderer,
- GtkCellRenderer *text_renderer,
- int thumb_size);
-gboolean gth_file_view_truncate_metadata (GthFileView *self);
-void gth_file_view_activate_file (GthFileView *self,
- GtkTreePath *path);
+void gth_file_view_get_drag_dest_pos (GthFileView *self,
+ int *pos);
G_END_DECLS
diff --git a/gthumb/gth-grid-view.c b/gthumb/gth-grid-view.c
new file mode 100644
index 0000000..899e669
--- /dev/null
+++ b/gthumb/gth-grid-view.c
@@ -0,0 +1,3698 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * GThumb
+ *
+ * Copyright (C) 2001-2011 The Free Software Foundation, Inc.
+ *
+ * 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/>.
+ */
+
+#include <config.h>
+#include <libintl.h>
+#include <string.h>
+#include <math.h>
+#include <glib.h>
+#include <gdk/gdkkeysyms.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include <gtk/gtk.h>
+#include "cairo-utils.h"
+#include "glib-utils.h"
+#include "gth-file-data.h"
+#include "gth-file-selection.h"
+#include "gth-file-store.h"
+#include "gth-file-view.h"
+#include "gth-grid-view.h"
+#include "gth-marshal.h"
+#include "gth-enum-types.h"
+#include "gtk-utils.h"
+
+
+#define GTH_GRID_VIEW_ITEM(x) ((GthGridViewItem *)(x))
+#define GTH_GRID_VIEW_LINE(x) ((GthGridViewLine *)(x))
+#define CAPTION_LINE_SPACING 4
+#define DEFAULT_CAPTION_SPACING 4
+#define DEFAULT_CAPTION_PADDING 2
+#define DEFAULT_CELL_SPACING 16
+#define DEFAULT_CELL_PADDING 5
+#define DEFAULT_THUMBNAIL_BORDER 6
+#define SCROLL_DELAY 30
+#define LAYOUT_DELAY 20
+#define MAX_DELTA_FOR_SCROLLING 1024.0
+
+
+static void gth_grid_view_gth_file_selection_interface_init (GthFileSelectionInterface *iface);
+static void gth_grid_view_gth_file_view_interface_init (GthFileViewInterface *iface);
+
+
+G_DEFINE_TYPE_WITH_CODE (GthGridView,
+ gth_grid_view,
+ GTK_TYPE_WIDGET,
+ G_IMPLEMENT_INTERFACE (GTH_TYPE_FILE_SELECTION,
+ gth_grid_view_gth_file_selection_interface_init)
+ G_IMPLEMENT_INTERFACE (GTH_TYPE_FILE_VIEW,
+ gth_grid_view_gth_file_view_interface_init)
+ G_IMPLEMENT_INTERFACE (GTK_TYPE_SCROLLABLE, NULL))
+
+
+enum {
+ MOVE_CURSOR,
+ SET_CURSOR_SELECTION,
+ TOGGLE_CURSOR_SELECTION,
+ LAST_SIGNAL
+};
+
+
+enum {
+ PROP_0,
+ PROP_CAPTION,
+ PROP_CELL_SPACING,
+ PROP_HADJUSTMENT,
+ PROP_HSCROLL_POLICY,
+ PROP_MODEL,
+ PROP_THUMBNAIL_SIZE,
+ PROP_VADJUSTMENT,
+ PROP_VSCROLL_POLICY
+};
+
+
+typedef enum {
+ SYNC_INSERT,
+ SYNC_REMOVE
+} SyncType;
+
+
+static guint grid_view_signals[LAST_SIGNAL] = { 0 };
+
+
+typedef struct {
+ /* data */
+
+ guint ref;
+ GthFileData *file_data;
+ GdkPixbuf *thumbnail;
+ gboolean is_icon;
+ gboolean checked;
+ char *caption;
+
+ /* item state */
+
+ GtkStateFlags state;
+ GtkStateFlags tmp_state;
+ gboolean update_caption_height;
+
+ /* geometry info */
+
+ cairo_rectangle_int_t area; /* union of thumbnail_area and caption_area */
+ cairo_rectangle_int_t thumbnail_area;
+ cairo_rectangle_int_t pixbuf_area;
+ cairo_rectangle_int_t caption_area;
+} GthGridViewItem;
+
+
+typedef struct {
+ int y;
+ int height;
+ GList *items;
+} GthGridViewLine;
+
+
+struct _GthGridViewPrivate {
+ GtkTreeModel *model;
+ GList *items;
+ int n_items;
+ GList *lines;
+ GList *selection;
+ int focused_item;
+ int first_focused_item; /* Used to do multiple selection with the keyboard. */
+
+ guint dirty_layout : 1; /* Whether the layout needs to be updated */
+ guint frozen_layout;
+ guint layout_timeout;
+ int relayout_from_line;
+ guint update_caption_height : 1;
+
+ int width; /* size of the view */
+ int height;
+ int thumbnail_size;
+ int thumbnail_border;
+ int cell_size; /* max size of any cell area */
+ int cell_spacing; /* space between adjacent cell areas both horizontally and vertically */
+ int cell_padding; /* space between the cell area border and its content */
+ int caption_spacing; /* space between the thumbnail area and the caption area */
+ int caption_padding; /* space between the caption area border and its content */
+
+ guint scroll_timeout; /* timeout ID for autoscrolling */
+ double autoscroll_y_delta; /* change the adjustment value by this amount when autoscrolling */
+
+ double event_last_x; /* mouse position for autoscrolling */
+ double event_last_y;
+
+ /* selection */
+
+ guint selecting : 1; /* whether the user is performing a rubberband selection. */
+ guint select_pending : 1; /* whether selection is pending after a button press. */
+ int select_pending_pos;
+ GthGridViewItem *select_pending_item;
+ GtkSelectionMode selection_mode;
+ cairo_rectangle_int_t selection_area;
+ int last_selected_pos;
+ GthGridViewItem *last_selected_item;
+ guint multi_selecting_with_keyboard : 1; /* Whether a multi selection with the keyboard has started. */
+ guint selection_changed : 1;
+ int sel_start_x; /* The point where the mouse selection started. */
+ int sel_start_y;
+ guint sel_state; /* Modifier state when the selection began. */
+
+ /* drag and drop */
+
+ guint dragging : 1; /* Whether the user is dragging items. */
+ guint drag_started : 1; /* Whether the drag has started. */
+ gboolean drag_source_enabled;
+ GdkModifierType drag_start_button_mask;
+ int drag_button;
+ GtkTargetList *drag_target_list;
+ GdkDragAction drag_actions;
+ int drag_start_x; /* The point where the drag started. */
+ int drag_start_y;
+ int drop_item;
+ GthDropPosition drop_pos;
+
+ /* */
+
+ GtkAdjustment *hadjustment;
+ GtkAdjustment *vadjustment;
+ GdkWindow *bin_window;
+
+ char *caption_attributes;
+ char **caption_attributes_v;
+ PangoLayout *caption_layout;
+};
+
+
+/* -- gth_grid_view_item -- */
+
+
+static void
+gth_grid_view_item_set_file_data (GthGridViewItem *item,
+ GthFileData *file_data)
+{
+ _g_object_unref (item->file_data);
+ item->file_data = _g_object_ref (file_data);
+}
+
+
+static void
+gth_grid_view_item_set_thumbnail (GthGridViewItem *item,
+ GdkPixbuf *thumbnail)
+{
+ _g_object_unref (item->thumbnail);
+ item->thumbnail = _g_object_ref (thumbnail);
+
+ if (item->thumbnail != NULL) {
+ item->pixbuf_area.width = gdk_pixbuf_get_width (item->thumbnail);
+ item->pixbuf_area.height = gdk_pixbuf_get_height (item->thumbnail);
+ }
+ else {
+ item->pixbuf_area.width = 0;
+ item->pixbuf_area.height = 0;
+ }
+
+ item->pixbuf_area.x = item->thumbnail_area.x + ((item->thumbnail_area.width - item->pixbuf_area.width) / 2);
+ item->pixbuf_area.y = item->thumbnail_area.y + ((item->thumbnail_area.height - item->pixbuf_area.height) / 2);
+}
+
+
+#define MAX_TEXT_LENGTH 70
+#define ODD_ROW_ATTR_STYLE ""
+#define EVEN_ROW_ATTR_STYLE " style='italic'"
+
+
+static void
+gth_grid_view_item_update_caption (GthGridViewItem *item,
+ char **attributes_v)
+{
+ GString *metadata;
+ gboolean odd;
+ int i;
+
+ item->update_caption_height = TRUE;
+
+ g_free (item->caption);
+ item->caption = NULL;
+
+ if ((item->file_data == NULL)
+ || (attributes_v == NULL)
+ || g_str_equal (attributes_v[0], "none"))
+ {
+ return;
+ }
+
+ metadata = g_string_new (NULL);
+
+ odd = TRUE;
+ for (i = 0; attributes_v[i] != NULL; i++) {
+ char *value;
+
+ value = gth_file_data_get_attribute_as_string (item->file_data, attributes_v[i]);
+ if ((value != NULL) && ! g_str_equal (value, "")) {
+ char *escaped;
+
+ if (metadata->len > 0)
+ g_string_append (metadata, "\n");
+ if (g_utf8_strlen (value, -1) > MAX_TEXT_LENGTH) {
+ char *tmp;
+
+ tmp = g_strdup (value);
+ g_utf8_strncpy (tmp, value, MAX_TEXT_LENGTH);
+ g_free (value);
+ value = g_strdup_printf ("%sâ", tmp);
+
+ g_free (tmp);
+ }
+
+ escaped = g_markup_escape_text (value, -1);
+ g_string_append_printf (metadata, "<span%s>%s</span>", (odd ? ODD_ROW_ATTR_STYLE : EVEN_ROW_ATTR_STYLE), escaped);
+
+ g_free (escaped);
+ }
+ odd = ! odd;
+
+ g_free (value);
+ }
+
+ item->caption = g_string_free (metadata, FALSE);
+}
+
+
+static GthGridViewItem *
+gth_grid_view_item_new (GthGridView *grid_view,
+ GthFileData *file_data,
+ GdkPixbuf *thumbnail,
+ gboolean is_icon,
+ char **attributes_v)
+{
+ GthGridViewItem *item;
+
+ item = g_new0 (GthGridViewItem, 1);
+ item->ref = 1;
+ gth_grid_view_item_set_file_data (item, file_data);
+ gth_grid_view_item_set_thumbnail (item, thumbnail);
+ item->is_icon = is_icon;
+ gth_grid_view_item_update_caption (item, attributes_v);
+
+ return item;
+}
+
+
+static GthGridViewItem *
+gth_grid_view_item_ref (GthGridViewItem *item)
+{
+ if (item != NULL)
+ item->ref++;
+ return item;
+}
+
+
+static void
+gth_grid_view_item_unref (GthGridViewItem *item)
+{
+ if ((item == NULL) || (--item->ref > 0))
+ return;
+
+ g_free (item->caption);
+ _g_object_unref (item->thumbnail);
+ _g_object_unref (item->file_data);
+ g_free (item);
+}
+
+
+/* -- gth_grid_view_line -- */
+
+
+static void
+gth_grid_view_line_free (GthGridViewLine *line)
+{
+ g_list_foreach (line->items, (GFunc) gth_grid_view_item_unref, NULL);
+ g_list_free (line->items);
+ g_free (line);
+}
+
+
+/**/
+
+
+static void
+_gth_grid_view_free_lines (GthGridView *self)
+{
+ g_list_foreach (self->priv->lines, (GFunc) gth_grid_view_line_free, NULL);
+ g_list_free (self->priv->lines);
+ self->priv->lines = NULL;
+ self->priv->height = 0;
+}
+
+
+static void
+_gth_grid_view_free_items (GthGridView *self)
+{
+ g_list_foreach (self->priv->items, (GFunc) gth_grid_view_item_unref, NULL);
+ g_list_free (self->priv->items);
+ self->priv->items = NULL;
+}
+
+
+static void
+gth_grid_view_finalize (GObject *object)
+{
+ GthGridView *self;
+
+ self = GTH_GRID_VIEW (object);
+
+ if (self->priv->layout_timeout != 0) {
+ g_source_remove (self->priv->layout_timeout);
+ self->priv->layout_timeout = 0;
+ }
+
+ if (self->priv->scroll_timeout != 0) {
+ g_source_remove (self->priv->scroll_timeout);
+ self->priv->scroll_timeout = 0;
+ }
+
+ _gth_grid_view_free_items (self);
+ _gth_grid_view_free_lines (self);
+ g_list_free (self->priv->selection);
+
+ if (self->priv->hadjustment != NULL) {
+ g_signal_handlers_disconnect_by_data (self->priv->hadjustment, self);
+ g_object_unref (self->priv->hadjustment);
+ self->priv->hadjustment = NULL;
+ }
+
+ if (self->priv->vadjustment != NULL) {
+ g_signal_handlers_disconnect_by_data (self->priv->vadjustment, self);
+ g_object_unref (self->priv->vadjustment);
+ self->priv->vadjustment = NULL;
+ }
+
+ if (self->priv->drag_target_list != NULL) {
+ gtk_target_list_unref (self->priv->drag_target_list);
+ self->priv->drag_target_list = NULL;
+ }
+ g_free (self->priv->caption_attributes);
+ g_strfreev (self->priv->caption_attributes_v);
+ _g_object_unref (self->priv->model);
+
+ G_OBJECT_CLASS (gth_grid_view_parent_class)->finalize (object);
+}
+
+
+static gboolean
+adjustment_value_changed (GtkAdjustment *adj,
+ GthGridView *self)
+{
+ if (gtk_widget_get_realized (GTK_WIDGET (self)))
+ gdk_window_move (self->priv->bin_window,
+ (int) - gtk_adjustment_get_value (self->priv->hadjustment),
+ (int) - gtk_adjustment_get_value (self->priv->vadjustment));
+
+ return FALSE;
+}
+
+
+static void
+gth_grid_view_map (GtkWidget *widget)
+{
+ GthGridView *self = GTH_GRID_VIEW (widget);
+
+ gtk_widget_set_mapped (widget, TRUE);
+ gdk_window_show (self->priv->bin_window);
+ gdk_window_show (gtk_widget_get_window (widget));
+}
+
+
+static void
+_gth_grid_view_stop_dragging (GthGridView *self)
+{
+ if (! self->priv->dragging)
+ return;
+
+ self->priv->dragging = FALSE;
+ self->priv->drag_started = FALSE;
+}
+
+
+static void
+gth_grid_view_unmap (GtkWidget *widget)
+{
+ GthGridView *self = GTH_GRID_VIEW (widget);
+
+ _gth_grid_view_stop_dragging (self);
+ GTK_WIDGET_CLASS (gth_grid_view_parent_class)->unmap (widget);
+}
+
+
+static void _gth_grid_view_queue_relayout (GthGridView *self);
+
+
+static void
+gth_grid_view_realize (GtkWidget *widget)
+{
+ GthGridView *self;
+ GtkAllocation allocation;
+ GdkWindowAttr attributes;
+ int attributes_mask;
+ GdkWindow *window;
+ GtkStyleContext *style_context;
+
+ self = GTH_GRID_VIEW (widget);
+
+ gtk_widget_set_realized (widget, TRUE);
+ gtk_widget_get_allocation (widget, &allocation);
+
+ /* view window */
+
+ attributes.window_type = GDK_WINDOW_CHILD;
+ attributes.x = allocation.x;
+ attributes.y = allocation.y;
+ attributes.width = allocation.width;
+ attributes.height = allocation.height;
+ attributes.wclass = GDK_INPUT_OUTPUT;
+ attributes.visual = gtk_widget_get_visual (widget);
+ attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK;
+ attributes_mask = (GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL);
+ window = gdk_window_new (gtk_widget_get_parent_window (widget),
+ &attributes,
+ attributes_mask);
+ gtk_widget_set_window (widget, window);
+ gdk_window_set_user_data (window, widget);
+
+ /* bin window */
+
+ attributes.x = 0;
+ attributes.y = 0;
+ attributes.width = MAX (self->priv->width, allocation.width);
+ attributes.height = MAX (self->priv->height, allocation.height);
+ attributes.event_mask = (GDK_EXPOSURE_MASK
+ | GDK_SCROLL_MASK
+ | GDK_POINTER_MOTION_MASK
+ | GDK_ENTER_NOTIFY_MASK
+ | GDK_LEAVE_NOTIFY_MASK
+ | GDK_BUTTON_PRESS_MASK
+ | GDK_BUTTON_RELEASE_MASK
+ | gtk_widget_get_events (widget));
+
+ self->priv->bin_window = gdk_window_new (gtk_widget_get_window (widget),
+ &attributes,
+ attributes_mask);
+ gdk_window_set_user_data (self->priv->bin_window, widget);
+
+ /* style */
+
+ style_context = gtk_widget_get_style_context (widget);
+ gtk_style_context_save (style_context);
+ gtk_style_context_add_class (style_context, GTK_STYLE_CLASS_VIEW);
+ gtk_style_context_set_background (style_context, self->priv->bin_window);
+ gtk_style_context_restore (style_context);
+
+ self->priv->caption_layout = gtk_widget_create_pango_layout (widget, NULL);
+ pango_layout_set_wrap (self->priv->caption_layout, PANGO_WRAP_WORD_CHAR);
+ pango_layout_set_alignment (self->priv->caption_layout, PANGO_ALIGN_CENTER);
+ pango_layout_set_spacing (self->priv->caption_layout, CAPTION_LINE_SPACING);
+
+ /**/
+
+ gdk_window_show (self->priv->bin_window);
+ _gth_grid_view_queue_relayout (self);
+}
+
+
+static void
+gth_grid_view_unrealize (GtkWidget *widget)
+{
+ GthGridView *self;
+
+ self = GTH_GRID_VIEW (widget);
+
+ gdk_window_set_user_data (self->priv->bin_window, NULL);
+ gdk_window_destroy (self->priv->bin_window);
+ self->priv->bin_window = NULL;
+
+ g_object_unref (self->priv->caption_layout);
+ self->priv->caption_layout = NULL;
+
+ GTK_WIDGET_CLASS (gth_grid_view_parent_class)->unrealize (widget);
+}
+
+
+static void
+_gth_grid_view_update_background (GthGridView *self)
+{
+ GtkWidget *widget = GTK_WIDGET (self);
+ GtkStyleContext *style_context;
+
+ if (! gtk_widget_get_realized (widget))
+ return;
+
+ style_context = gtk_widget_get_style_context (widget);
+ gtk_style_context_save (style_context);
+ gtk_style_context_add_class (style_context, GTK_STYLE_CLASS_VIEW);
+ gtk_style_context_set_background (style_context, gtk_widget_get_window (widget));
+ gtk_style_context_set_background (style_context, self->priv->bin_window);
+ gtk_style_context_restore (style_context);
+}
+
+
+static void
+gth_grid_view_state_flags_changed (GtkWidget *widget,
+ GtkStateFlags previous_state)
+{
+ _gth_grid_view_update_background (GTH_GRID_VIEW (widget));
+ gtk_widget_queue_draw (widget);
+}
+
+
+static void
+gth_grid_view_style_updated (GtkWidget *widget)
+{
+ GTK_WIDGET_CLASS (gth_grid_view_parent_class)->style_updated (widget);
+
+ _gth_grid_view_update_background (GTH_GRID_VIEW (widget));
+ gtk_widget_queue_resize (widget);
+}
+
+
+static void
+gth_grid_view_get_preferred_width (GtkWidget *widget,
+ int *minimum,
+ int *natural)
+{
+ GthGridView *self = GTH_GRID_VIEW (widget);
+
+ if (minimum != NULL)
+ *minimum = self->priv->cell_size;
+ if (natural != NULL)
+ *natural = *minimum;
+}
+
+
+static void
+gth_grid_view_get_preferred_height (GtkWidget *widget,
+ int *minimum,
+ int *natural)
+{
+ GthGridView *self = GTH_GRID_VIEW (widget);
+
+ if (minimum != NULL)
+ *minimum = self->priv->cell_size;
+ if (natural != NULL)
+ *natural = *minimum;
+}
+
+
+static void
+_gth_grid_view_queue_draw (GthGridView *self)
+{
+ if (self->priv->bin_window != NULL)
+ gdk_window_invalidate_rect (self->priv->bin_window, NULL, FALSE);
+}
+
+
+/* -- grid layout -- */
+
+
+static void
+_gth_grid_view_update_item_size (GthGridView *self,
+ GthGridViewItem *item)
+{
+ int width;
+
+ width = self->priv->cell_size - (self->priv->cell_padding * 2);
+ item->thumbnail_area.width = width;
+ item->thumbnail_area.height = width;
+
+ item->caption_area.width = width;
+
+ item->area.width = self->priv->cell_size;
+ item->area.height = self->priv->cell_padding + item->thumbnail_area.height;
+
+ if (self->priv->update_caption_height || item->update_caption_height) {
+ if ((item->caption != NULL) && (g_strcmp0 (item->caption, "") != 0)) {
+ pango_layout_set_markup (self->priv->caption_layout, item->caption, -1);
+ pango_layout_get_pixel_size (self->priv->caption_layout, NULL, &item->caption_area.height);
+ }
+ item->update_caption_height = FALSE;
+ }
+
+ if ((item->caption != NULL) && (g_strcmp0 (item->caption, "") != 0)) {
+ item->caption_area.height += self->priv->caption_padding * 2;
+ item->area.height += self->priv->caption_spacing + item->caption_area.height;
+ }
+ else
+ item->caption_area.height = 0;
+
+ item->area.height += self->priv->cell_padding;
+}
+
+
+static void
+_gth_grid_view_place_item_at (GthGridView *self,
+ GthGridViewItem *item,
+ int x,
+ int y)
+{
+ item->area.x = x;
+ item->area.y = y;
+
+ item->thumbnail_area.x = item->area.x + self->priv->cell_padding;
+ item->thumbnail_area.y = item->area.y + self->priv->cell_padding;
+
+ item->pixbuf_area.x = item->thumbnail_area.x + ((item->thumbnail_area.width - item->pixbuf_area.width) / 2);
+ item->pixbuf_area.y = item->thumbnail_area.y + ((item->thumbnail_area.height - item->pixbuf_area.height) / 2);
+
+ item->caption_area.x = item->area.x + self->priv->cell_padding;
+ item->caption_area.y = item->area.y + self->priv->cell_padding + item->thumbnail_area.height + self->priv->caption_spacing;
+}
+
+
+static void
+_gth_grid_view_layout_line (GthGridView *self,
+ GthGridViewLine *line)
+{
+ int x;
+ int direction;
+ GList *scan;
+
+ if (gtk_widget_get_direction (GTK_WIDGET (self)) == GTK_TEXT_DIR_RTL) {
+ x = self->priv->width - self->priv->cell_size;
+ direction = -1;
+ }
+ else {
+ x = 0;
+ direction = 1;
+ }
+
+ for (scan = line->items; scan; scan = scan->next) {
+ GthGridViewItem *item = scan->data;
+
+ x += direction * self->priv->cell_spacing;
+ _gth_grid_view_place_item_at (self, item, x, line->y);
+ x += direction * self->priv->cell_size;
+ }
+}
+
+
+static void
+_gth_grid_view_add_and_layout_line (GthGridView *self,
+ GList *items,
+ int y,
+ int height)
+{
+ GthGridViewLine *line;
+
+ line = g_new0 (GthGridViewLine, 1);
+ line->items = g_list_reverse (items);
+ line->y = y;
+ line->height = height;
+ _gth_grid_view_layout_line (self, line);
+
+ self->priv->lines = g_list_prepend (self->priv->lines, line);
+}
+
+
+static void
+_gth_grid_view_configure_hadjustment (GthGridView *self)
+{
+ double page_size;
+ double value;
+
+ if (self->priv->hadjustment == NULL)
+ return;
+
+ page_size = gtk_widget_get_allocated_width (GTK_WIDGET (self));
+ value = gtk_adjustment_get_value (self->priv->hadjustment);
+ if (value + page_size > self->priv->width)
+ value = MAX (self->priv->width - page_size, 0);
+
+ gtk_adjustment_configure (self->priv->hadjustment,
+ value,
+ 0.0,
+ MAX (page_size, self->priv->width),
+ 0.1 * page_size,
+ 0.5 * page_size,
+ page_size);
+}
+
+
+static void
+_gth_grid_view_configure_vadjustment (GthGridView *self)
+{
+ double page_size;
+ double value;
+
+ if (self->priv->vadjustment == NULL)
+ return;
+
+ page_size = gtk_widget_get_allocated_height (GTK_WIDGET (self));
+ value = gtk_adjustment_get_value (self->priv->vadjustment);
+ if (value + page_size > self->priv->height)
+ value = MAX (self->priv->height - page_size, 0);
+
+ gtk_adjustment_configure (self->priv->vadjustment,
+ value,
+ 0.0,
+ MAX (page_size, self->priv->height),
+ 0.1 * page_size,
+ 0.5 * page_size,
+ page_size);
+}
+
+
+static void
+_gth_grid_view_relayout_at (GthGridView *self,
+ int pos,
+ int y)
+{
+ int items_per_line;
+ GList *items;
+ int max_height;
+ GList *scan;
+ int n;
+
+ items_per_line = gth_grid_view_get_items_per_line (self);
+ items = NULL;
+ max_height = 0;
+ for (scan = g_list_nth (self->priv->items, pos), n = pos;
+ scan;
+ scan = scan->next, n++)
+ {
+ GthGridViewItem *item = scan->data;
+
+ if ((n % items_per_line) == 0) {
+ if (items != NULL) {
+ _gth_grid_view_add_and_layout_line (self, items, y, max_height);
+ items = NULL;
+ y += max_height + self->priv->cell_spacing;
+ }
+ max_height = 0;
+ }
+
+ _gth_grid_view_update_item_size (self, item);
+ max_height = MAX (item->area.height, max_height);
+
+ items = g_list_prepend (items, gth_grid_view_item_ref (item));
+ }
+
+ if (items != NULL) {
+ _gth_grid_view_add_and_layout_line (self, items, y, max_height);
+ y += max_height + self->priv->cell_spacing;
+ }
+
+ self->priv->lines = g_list_reverse (self->priv->lines);
+
+ if (y != self->priv->height) {
+ GtkAllocation allocation;
+
+ self->priv->height = y;
+
+ gtk_widget_get_allocation (GTK_WIDGET (self), &allocation);
+ gdk_window_resize (self->priv->bin_window,
+ MAX (self->priv->width, allocation.width),
+ MAX (self->priv->height, allocation.height));
+ }
+
+ _gth_grid_view_configure_hadjustment (self);
+ _gth_grid_view_configure_vadjustment (self);
+}
+
+
+static void
+_gth_grid_view_free_lines_from (GthGridView *self,
+ int first_line)
+{
+ GList *lines;
+ GList *scan;
+
+ lines = g_list_nth (self->priv->lines, first_line);
+ if (lines == NULL)
+ return;
+
+ /* truncate self->priv->lines before the first line to free */
+ if (lines->prev != NULL)
+ lines->prev->next = NULL;
+ else
+ self->priv->lines = NULL;
+
+ for (scan = lines; scan; scan = scan->next)
+ gth_grid_view_line_free (GTH_GRID_VIEW_LINE (scan->data));
+ g_list_free (lines);
+}
+
+
+static void
+_gth_grid_view_relayout_from_line (GthGridView *self,
+ int line)
+{
+ int y;
+ GList *scan;
+
+ if (! gtk_widget_get_realized (GTK_WIDGET (self)))
+ return;
+
+ if (self->priv->update_caption_height)
+ pango_layout_set_width (self->priv->caption_layout,
+ (self->priv->cell_size - (self->priv->cell_padding * 2)) * PANGO_SCALE);
+
+ _gth_grid_view_free_lines_from (self, line);
+ y = self->priv->cell_spacing;
+ for (scan = self->priv->lines; scan; scan = scan->next)
+ y += GTH_GRID_VIEW_LINE (scan->data)->height + self->priv->cell_spacing;
+
+ _gth_grid_view_relayout_at (self,
+ line * gth_grid_view_get_items_per_line (self),
+ y);
+
+ self->priv->dirty_layout = FALSE;
+ self->priv->update_caption_height = FALSE;
+
+ gtk_widget_queue_draw (GTK_WIDGET (self));
+}
+
+
+static gboolean
+_gth_grid_view_relayout_cb (gpointer data)
+{
+ GthGridView *self = data;
+
+ if (self->priv->layout_timeout != 0)
+ g_source_remove (self->priv->layout_timeout);
+
+ _gth_grid_view_relayout_from_line (self, self->priv->relayout_from_line);
+
+ self->priv->layout_timeout = 0;
+
+ return FALSE;
+}
+
+
+static void
+_gth_grid_view_queue_relayout_from_line (GthGridView *self,
+ int line)
+{
+ self->priv->relayout_from_line = MIN (line, self->priv->relayout_from_line);
+
+ if (self->priv->frozen_layout) {
+ self->priv->dirty_layout = TRUE;
+ return;
+ }
+
+ if (self->priv->layout_timeout != 0)
+ g_source_remove (self->priv->layout_timeout);
+ self->priv->layout_timeout = g_timeout_add (LAYOUT_DELAY,
+ _gth_grid_view_relayout_cb,
+ self);
+}
+
+
+static void
+_gth_grid_view_queue_relayout (GthGridView *self)
+{
+ _gth_grid_view_queue_relayout_from_line (self, 0);
+}
+
+
+static void
+_gth_grid_view_queue_relayout_from_position (GthGridView *self,
+ int pos)
+{
+ _gth_grid_view_queue_relayout_from_line (self, pos / gth_grid_view_get_items_per_line (self));
+}
+
+
+static void
+gth_grid_view_size_allocate (GtkWidget *widget,
+ GtkAllocation *allocation)
+{
+ GthGridView *self;
+ int old_cells_per_line;
+
+ self = GTH_GRID_VIEW (widget);
+
+ old_cells_per_line = gth_grid_view_get_items_per_line (self);
+ self->priv->width = allocation->width;
+
+ gtk_widget_set_allocation (widget, allocation);
+
+ if (gtk_widget_get_realized (widget)) {
+ gdk_window_move_resize (gtk_widget_get_window (widget),
+ allocation->x,
+ allocation->y,
+ allocation->width,
+ allocation->height);
+ gdk_window_resize (self->priv->bin_window,
+ MAX (self->priv->width, allocation->width),
+ MAX (self->priv->height, allocation->height));
+
+ if (old_cells_per_line != gth_grid_view_get_items_per_line (self))
+ _gth_grid_view_queue_relayout (self);
+ }
+ else
+ self->priv->dirty_layout = TRUE;
+
+ _gth_grid_view_configure_hadjustment (self);
+ _gth_grid_view_configure_vadjustment (self);
+}
+
+
+static int
+get_first_visible_at_offset (GthGridView *self,
+ double ofs)
+{
+ int n_line;
+ GList *scan;
+ int pos;
+
+ if (self->priv->n_items == 0)
+ return -1;
+
+ n_line = 0;
+ for (scan = self->priv->lines; scan && (ofs > 0.0); scan = scan->next) {
+ ofs -= GTH_GRID_VIEW_LINE (scan->data)->height + self->priv->cell_spacing;
+ n_line++;
+ }
+ pos = gth_grid_view_get_items_per_line (self) * (n_line - 1);
+
+ return CLAMP (pos, 0, self->priv->n_items - 1);
+}
+
+
+static int
+gth_grid_view_get_first_visible (GthFileView *file_view)
+{
+ GthGridView *self = GTH_GRID_VIEW (file_view);
+
+ return get_first_visible_at_offset (self, gtk_adjustment_get_value (self->priv->vadjustment));
+}
+
+
+static int
+get_last_visible_at_offset (GthGridView *self,
+ double ofs)
+{
+ int n_line;
+ GList *scan;
+ int pos;
+
+ if (self->priv->n_items == 0)
+ return -1;
+
+ n_line = 0;
+ for (scan = self->priv->lines; scan && (ofs > 0.0); scan = scan->next) {
+ ofs -= GTH_GRID_VIEW_LINE (scan->data)->height + self->priv->cell_spacing;
+ n_line++;
+ }
+ pos = gth_grid_view_get_items_per_line (self) * n_line - 1;
+
+ return CLAMP (pos, 0, self->priv->n_items - 1);
+}
+
+
+static int
+gth_grid_view_get_last_visible (GthFileView *file_view)
+{
+ GthGridView *self = GTH_GRID_VIEW (file_view);
+
+ return get_last_visible_at_offset (self,
+ (gtk_adjustment_get_value (self->priv->vadjustment)
+ + gtk_adjustment_get_page_size (self->priv->vadjustment)));
+}
+
+
+/* -- gth_grid_view_draw -- */
+
+
+static cairo_pattern_t *
+_cairo_film_pattern_create (void)
+{
+ static cairo_pattern_t *film_pattern = NULL;
+ cairo_pattern_t *pattern;
+ static GStaticMutex mutex = G_STATIC_MUTEX_INIT;
+
+ g_static_mutex_lock (&mutex);
+ if (film_pattern == NULL) {
+ char *filename;
+ cairo_surface_t *surface;
+
+ filename = g_build_filename (GTHUMB_DATADIR, "filmholes.png", NULL);
+ surface = cairo_image_surface_create_from_png (filename);
+ film_pattern = cairo_pattern_create_for_surface (surface);
+ cairo_pattern_set_filter (film_pattern, CAIRO_FILTER_FAST);
+ cairo_pattern_set_extend (film_pattern, CAIRO_EXTEND_REPEAT);
+
+ cairo_surface_destroy (surface);
+ g_free (filename);
+
+ }
+ pattern = cairo_pattern_reference (film_pattern);
+ g_static_mutex_unlock (&mutex);
+
+ return pattern;
+}
+
+
+static void
+_gth_grid_view_item_draw_thumbnail (GthGridViewItem *item,
+ cairo_t *cr,
+ GtkWidget *widget,
+ GtkStateFlags item_state,
+ GthGridView *grid_view)
+{
+ GdkPixbuf *pixbuf;
+ GtkStyleContext *style_context;
+ cairo_rectangle_int_t frame_rect;
+
+ pixbuf = item->thumbnail;
+ if (pixbuf == NULL)
+ return;
+
+ g_object_ref (pixbuf);
+
+ cairo_save (cr);
+ style_context = gtk_widget_get_style_context (widget);
+ gtk_style_context_save (style_context);
+ gtk_style_context_remove_class (style_context, GTK_STYLE_CLASS_VIEW);
+ gtk_style_context_add_class (style_context, GTK_STYLE_CLASS_CELL);
+
+ frame_rect = item->pixbuf_area;
+
+ if (item->is_icon
+ || ((item->pixbuf_area.width < grid_view->priv->thumbnail_size) && (item->pixbuf_area.height < grid_view->priv->thumbnail_size))
+ || (item->file_data == NULL)
+ || ! (_g_mime_type_is_image (gth_file_data_get_mime_type (item->file_data)) || (item_state & GTK_STATE_FLAG_SELECTED) || (item_state == GTK_STATE_FLAG_NORMAL)))
+ {
+ GdkRGBA background_color;
+
+ /* use a gray rounded box for icons or when the original size
+ * is smaller than the thumbnail size... */
+
+ gtk_style_context_get_background_color (style_context, item_state, &background_color);
+ gdk_cairo_set_source_rgba (cr, &background_color);
+
+ _cairo_draw_rounded_box (cr,
+ item->thumbnail_area.x,
+ item->thumbnail_area.y,
+ item->thumbnail_area.width,
+ item->thumbnail_area.height,
+ 4);
+ cairo_fill (cr);
+ }
+
+ if (! item->is_icon && _g_mime_type_is_image (gth_file_data_get_mime_type (item->file_data))) {
+
+ /* ...draw a frame with a drop-shadow effect */
+
+ GdkRGBA background_color;
+ GdkRGBA lighter_color;
+ GdkRGBA darker_color;
+
+ gdk_rgba_parse (&background_color, "#edeceb");
+ gtk_style_context_get_background_color (style_context, item_state, &background_color);
+ _gdk_rgba_darker (&background_color, &lighter_color);
+ _gdk_rgba_darker (&lighter_color, &darker_color);
+
+ cairo_translate (cr, 0.5, 0.5);
+ cairo_set_line_width (cr, 0.5);
+ cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
+
+ frame_rect = item->thumbnail_area;
+
+ /* the drop shadow */
+
+ gdk_cairo_set_source_rgba (cr, &darker_color);
+ _cairo_draw_rounded_box (cr,
+ frame_rect.x + 2,
+ frame_rect.y + 2,
+ frame_rect.width - 2,
+ frame_rect.height - 2,
+ 1);
+ cairo_fill (cr);
+
+ /* the outer frame */
+
+ gdk_cairo_set_source_rgba (cr, &background_color);
+ _cairo_draw_rounded_box (cr,
+ frame_rect.x,
+ frame_rect.y,
+ frame_rect.width - 2,
+ frame_rect.height - 2,
+ 1);
+ cairo_fill_preserve (cr);
+
+ if (item_state == GTK_STATE_FLAG_SELECTED)
+ gdk_cairo_set_source_rgba (cr, &darker_color);
+ else
+ gdk_cairo_set_source_rgba (cr, &lighter_color);
+ cairo_stroke (cr);
+
+ /* the inner frame */
+
+ cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
+ cairo_rectangle (cr,
+ item->pixbuf_area.x,
+ item->pixbuf_area.y,
+ item->pixbuf_area.width,
+ item->pixbuf_area.height);
+ cairo_fill (cr);
+
+ gdk_cairo_set_source_rgba (cr, &lighter_color);
+ cairo_move_to (cr,
+ item->pixbuf_area.x - 1,
+ item->pixbuf_area.y + item->pixbuf_area.height + 1);
+ cairo_rel_line_to (cr, 0, - item->pixbuf_area.height - 2);
+ cairo_rel_line_to (cr, item->pixbuf_area.width + 2, 0);
+ cairo_stroke (cr);
+
+ /*
+ cairo_set_source_rgb (cr, 0.9, 0.9, 0.9);
+ cairo_move_to (cr,
+ item->pixbuf_area.x - 1,
+ item->pixbuf_area.y + item->pixbuf_area.height);
+ cairo_rel_line_to (cr, item->pixbuf_area.width + 1, 0);
+ cairo_rel_line_to (cr, 0, - item->pixbuf_area.height - 1);
+ cairo_stroke (cr);
+ */
+
+ cairo_identity_matrix (cr);
+ }
+
+ if (! item->is_icon && _g_mime_type_is_video (gth_file_data_get_mime_type (item->file_data))) {
+ cairo_pattern_t *pattern;
+ int x;
+ cairo_matrix_t matrix;
+ int film_strip = 9;
+
+ frame_rect.x = item->pixbuf_area.x;
+ frame_rect.y = item->thumbnail_area.y + grid_view->priv->thumbnail_border;
+ frame_rect.width = item->pixbuf_area.width;
+ frame_rect.height = item->thumbnail_area.height - (grid_view->priv->thumbnail_border * 2);
+
+ /* dark background */
+
+ cairo_set_source_rgb (cr, 0.1, 0.1, 0.1);
+ cairo_rectangle (cr,
+ frame_rect.x,
+ frame_rect.y ,
+ frame_rect.width,
+ frame_rect.height);
+ cairo_fill (cr);
+
+ /* left film strip */
+
+ pattern = _cairo_film_pattern_create ();
+ x = frame_rect.x;
+ cairo_matrix_init_translate (&matrix, -frame_rect.x, -frame_rect.y);
+ cairo_pattern_set_matrix (pattern, &matrix);
+ cairo_set_source (cr, pattern);
+ cairo_rectangle (cr,
+ x,
+ frame_rect.y,
+ film_strip,
+ frame_rect.height);
+ cairo_fill (cr);
+
+ /* right film strip */
+
+ x = frame_rect.x + item->pixbuf_area.width - film_strip;
+ cairo_matrix_init_translate (&matrix, -x, -frame_rect.y);
+ cairo_pattern_set_matrix (pattern, &matrix);
+ cairo_set_source (cr, pattern);
+ cairo_rectangle (cr,
+ x,
+ frame_rect.y,
+ film_strip,
+ frame_rect.height);
+ cairo_fill (cr);
+
+ cairo_pattern_destroy (pattern);
+ }
+
+ /* thumbnail */
+
+ gdk_cairo_set_source_pixbuf (cr, pixbuf, item->pixbuf_area.x, item->pixbuf_area.y);
+ cairo_rectangle (cr, item->pixbuf_area.x, item->pixbuf_area.y, item->pixbuf_area.width, item->pixbuf_area.height);
+ cairo_fill (cr);
+
+ if (item_state & GTK_STATE_FLAG_SELECTED) {
+ GdkRGBA color;
+
+ gtk_style_context_get_background_color (style_context, item_state, &color);
+ cairo_set_source_rgba (cr, color.red, color.green, color.blue, 0.33);
+ cairo_rectangle (cr,
+ frame_rect.x,
+ frame_rect.y,
+ frame_rect.width,
+ frame_rect.height);
+ cairo_fill (cr);
+ }
+
+ gtk_style_context_restore (style_context);
+ cairo_restore (cr);
+
+ g_object_unref (pixbuf);
+}
+
+
+static void
+_gth_grid_view_item_draw_caption (GthGridViewItem *item,
+ cairo_t *cr,
+ GtkWidget *widget,
+ GtkStateFlags item_state,
+ PangoLayout *pango_layout,
+ GthGridView *grid_view)
+{
+ GtkStyleContext *style_context;
+ GdkRGBA color;
+
+ if (item->caption_area.height == 0)
+ return;
+
+ cairo_save (cr);
+
+ style_context = gtk_widget_get_style_context (widget);
+ gtk_style_context_get_color (style_context, item_state, &color);
+ gdk_cairo_set_source_rgba (cr, &color);
+ cairo_move_to (cr, item->caption_area.x, item->caption_area.y + grid_view->priv->caption_padding);
+ pango_layout_set_markup (pango_layout, item->caption, -1);
+ pango_cairo_show_layout (cr, pango_layout);
+
+ if (item_state & GTK_STATE_FLAG_FOCUSED)
+ gtk_render_focus (style_context,
+ cr,
+ item->caption_area.x,
+ item->caption_area.y,
+ item->caption_area.width,
+ item->caption_area.height);
+
+ cairo_restore (cr);
+}
+
+
+static void
+_gth_grid_view_draw_item (GthGridView *self,
+ GthGridViewItem *item,
+ cairo_t *cr)
+{
+ GtkStateType item_state;
+
+ item_state = item->state;
+ if (! gtk_widget_has_focus (GTK_WIDGET (self)) && (item_state & GTK_STATE_FLAG_FOCUSED))
+ item_state ^= GTK_STATE_FLAG_FOCUSED;
+ if (! gtk_widget_has_focus (GTK_WIDGET (self)) && (item_state & GTK_STATE_FLAG_ACTIVE))
+ item_state ^= GTK_STATE_FLAG_ACTIVE;
+
+ if (item_state ^ GTK_STATE_FLAG_NORMAL) {
+ GtkStyleContext *style_context;
+ GdkRGBA color;
+
+ cairo_save (cr);
+ style_context = gtk_widget_get_style_context (GTK_WIDGET (self));
+ gtk_style_context_get_background_color (style_context, item_state, &color);
+ _gdk_rgba_lighter (&color, &color);
+ cairo_set_source_rgba (cr, color.red, color.green, color.blue, color.alpha);
+ _cairo_draw_rounded_box (cr,
+ item->area.x,
+ item->area.y,
+ item->area.width,
+ item->area.height,
+ 4);
+ cairo_fill (cr);
+
+ cairo_restore (cr);
+ }
+
+ _gth_grid_view_item_draw_thumbnail (item, cr, GTK_WIDGET (self), item_state, self);
+ _gth_grid_view_item_draw_caption (item, cr, GTK_WIDGET (self), item_state, self->priv->caption_layout, self);
+}
+
+
+static void
+_gth_grid_view_draw_rubberband (GthGridView *self,
+ cairo_t *cr)
+{
+ GtkStyleContext *style_context;
+
+ if ((self->priv->selection_area.width <= 1.0) || (self->priv->selection_area.height <= 1.0))
+ return;
+
+ cairo_save (cr);
+
+ style_context = gtk_widget_get_style_context (GTK_WIDGET (self));
+ gtk_style_context_save (style_context);
+ gtk_style_context_add_class (style_context, GTK_STYLE_CLASS_RUBBERBAND);
+
+ gdk_cairo_rectangle (cr, &self->priv->selection_area);
+ cairo_clip (cr);
+ gtk_render_background (style_context,
+ cr,
+ self->priv->selection_area.x,
+ self->priv->selection_area.y,
+ self->priv->selection_area.width,
+ self->priv->selection_area.height);
+ gtk_render_frame (style_context,
+ cr,
+ self->priv->selection_area.x,
+ self->priv->selection_area.y,
+ self->priv->selection_area.width,
+ self->priv->selection_area.height);
+
+ gtk_style_context_restore (style_context);
+ cairo_restore (cr);
+}
+
+
+static void
+_gth_grid_view_draw_drop_target (GthGridView *self,
+ cairo_t *cr)
+{
+ GtkStyleContext *style_context;
+ GthGridViewItem *item;
+ int x;
+
+ if ((self->priv->drop_item < 0) || (self->priv->drop_item >= self->priv->n_items))
+ return;
+
+ style_context = gtk_widget_get_style_context (GTK_WIDGET (self));
+
+ item = g_list_nth (self->priv->items, self->priv->drop_item)->data;
+
+ x = 0;
+ if (self->priv->drop_pos == GTH_DROP_POSITION_LEFT)
+ x = item->area.x - (self->priv->cell_spacing / 2);
+ else if (self->priv->drop_pos == GTH_DROP_POSITION_RIGHT)
+ x = item->area.x + self->priv->cell_size + (self->priv->cell_spacing / 2);
+
+ gtk_render_focus (style_context,
+ cr,
+ x - 1,
+ item->area.y + self->priv->cell_padding,
+ 2,
+ item->area.height - (self->priv->cell_padding * 2));
+}
+
+
+static gboolean
+gth_grid_view_draw (GtkWidget *widget,
+ cairo_t *cr)
+{
+ GthGridView *self = (GthGridView*) widget;
+ int first_visible;
+ int last_visible;
+ int i;
+ GList *scan;
+
+ if (! gtk_cairo_should_draw_window (cr, self->priv->bin_window))
+ return FALSE;
+
+ first_visible = gth_grid_view_get_first_visible (GTH_FILE_VIEW (self));
+ if (first_visible == -1)
+ return TRUE;
+
+ last_visible = gth_grid_view_get_last_visible (GTH_FILE_VIEW (self));
+
+ cairo_save (cr);
+ gtk_cairo_transform_to_window (cr, widget, self->priv->bin_window);
+ cairo_set_line_width (cr, 1.0);
+
+ for (i = first_visible, scan = g_list_nth (self->priv->items, first_visible);
+ (i <= last_visible) && scan;
+ i++, scan = scan->next)
+ {
+ _gth_grid_view_draw_item (self, GTH_GRID_VIEW_ITEM (scan->data), cr);
+ }
+
+ if (self->priv->selecting || self->priv->multi_selecting_with_keyboard)
+ _gth_grid_view_draw_rubberband (self, cr);
+
+ if (self->priv->drop_pos != GTH_DROP_POSITION_NONE)
+ _gth_grid_view_draw_drop_target (self, cr);
+
+ cairo_restore (cr);
+
+ return TRUE;
+}
+
+
+static void
+_gth_grid_view_keep_focus_consistent (GthGridView *self)
+{
+ if (self->priv->focused_item > self->priv->n_items - 1)
+ self->priv->focused_item = - 1;
+}
+
+
+static gboolean
+gth_grid_view_focus_in (GtkWidget *widget,
+ GdkEventFocus *event)
+{
+ GthGridView *self = GTH_GRID_VIEW (widget);
+
+ _gth_grid_view_keep_focus_consistent (self);
+ if ((self->priv->focused_item == -1) && (self->priv->n_items > 0))
+ gth_file_view_set_cursor (GTH_FILE_VIEW (self), 0);
+ _gth_grid_view_queue_draw (self);
+
+ return TRUE;
+}
+
+
+static gboolean
+gth_grid_view_focus_out (GtkWidget *widget,
+ GdkEventFocus *event)
+{
+ gtk_widget_queue_draw (widget);
+
+ return TRUE;
+}
+
+
+static gboolean
+gth_grid_view_key_press (GtkWidget *widget,
+ GdkEventKey *event)
+{
+ GthGridView *self = GTH_GRID_VIEW (widget);
+ gboolean handled;
+
+ if (! self->priv->multi_selecting_with_keyboard
+ && (event->state & GDK_SHIFT_MASK)
+ && ((event->keyval == GDK_KEY_Left)
+ || (event->keyval == GDK_KEY_Right)
+ || (event->keyval == GDK_KEY_Up)
+ || (event->keyval == GDK_KEY_Down)
+ || (event->keyval == GDK_KEY_Page_Up)
+ || (event->keyval == GDK_KEY_Page_Down)
+ || (event->keyval == GDK_KEY_Home)
+ || (event->keyval == GDK_KEY_End)))
+ {
+ self->priv->multi_selecting_with_keyboard = TRUE;
+ self->priv->first_focused_item = self->priv->focused_item;
+
+ self->priv->selection_area.x = 0;
+ self->priv->selection_area.y = 0;
+ self->priv->selection_area.width = 0;
+ self->priv->selection_area.height = 0;
+ }
+
+ handled = gtk_bindings_activate (G_OBJECT (widget),
+ event->keyval,
+ event->state);
+
+ if (handled)
+ return TRUE;
+
+ if ((GTK_WIDGET_CLASS (gth_grid_view_parent_class)->key_press_event != NULL)
+ && GTK_WIDGET_CLASS (gth_grid_view_parent_class)->key_press_event (widget, event))
+ {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+static gboolean
+gth_grid_view_key_release (GtkWidget *widget,
+ GdkEventKey *event)
+{
+ GthGridView *self = GTH_GRID_VIEW (widget);
+
+ if (self->priv->multi_selecting_with_keyboard
+ && (event->state & GDK_SHIFT_MASK)
+ && ((event->keyval == GDK_KEY_Shift_L)
+ || (event->keyval == GDK_KEY_Shift_R)))
+ {
+ self->priv->multi_selecting_with_keyboard = FALSE;
+ }
+
+ _gth_grid_view_queue_draw (self);
+
+ if ((GTK_WIDGET_CLASS (gth_grid_view_parent_class)->key_press_event != NULL)
+ && GTK_WIDGET_CLASS (gth_grid_view_parent_class)->key_press_event (widget, event))
+ {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+static gboolean
+gth_grid_view_scroll_event (GtkWidget *widget,
+ GdkEventScroll *event)
+{
+ GtkAdjustment *adj;
+ gdouble new_value;
+ double step;
+
+ if (event->direction != GDK_SCROLL_UP &&
+ event->direction != GDK_SCROLL_DOWN)
+ {
+ return FALSE;
+ }
+
+ adj = GTH_GRID_VIEW (widget)->priv->vadjustment;
+
+ new_value = gtk_adjustment_get_value (adj);
+ step = gtk_adjustment_get_page_increment (adj);
+ if (event->direction == GDK_SCROLL_UP)
+ new_value -= step;
+ else
+ new_value += step;
+ new_value = CLAMP (new_value,
+ gtk_adjustment_get_lower (adj),
+ gtk_adjustment_get_upper (adj) - gtk_adjustment_get_page_size (adj));
+
+ gtk_adjustment_set_value (adj, new_value);
+
+ return TRUE;
+}
+
+
+/* -- GthFileSelection interface -- */
+
+
+static void
+gth_grid_view_set_selection_mode (GthFileSelection *selection,
+ GtkSelectionMode mode)
+{
+ GthGridView *self = GTH_GRID_VIEW (selection);
+
+ self->priv->selection_mode = mode;
+
+ /* the cell padding is used to show the selection, set it to 0 if
+ * selection is not allowed. */
+ self->priv->cell_padding = (mode == GTK_SELECTION_NONE) ? 0 : DEFAULT_CELL_PADDING;
+}
+
+
+static GList *
+gth_grid_view_get_selected (GthFileSelection *selection)
+{
+ GthGridView *self = GTH_GRID_VIEW (selection);
+ GList *selected;
+ GList *scan;
+
+ selected = NULL;
+ for (scan = self->priv->selection; scan; scan = scan->next) {
+ int pos;
+
+ pos = GPOINTER_TO_INT (scan->data);
+ selected = g_list_prepend (selected, gtk_tree_path_new_from_indices (pos, -1));
+ }
+
+ return g_list_reverse (selected);
+}
+
+
+static void
+_gth_grid_view_queue_draw_item (GthGridView *self,
+ GthGridViewItem *item)
+{
+ if (gtk_widget_get_realized (GTK_WIDGET (self)))
+ gdk_window_invalidate_rect (self->priv->bin_window, &item->area, FALSE);
+}
+
+
+static void
+_gth_grid_view_select_item (GthGridView *self,
+ int pos)
+{
+ GList *link;
+ GthGridViewItem *item;
+
+ g_return_if_fail ((pos >= 0) && (pos < self->priv->n_items));
+
+ if (self->priv->selection_mode == GTK_SELECTION_NONE)
+ return;
+
+ link = g_list_nth (self->priv->items, pos);
+ g_return_if_fail (link != NULL);
+
+ item = link->data;
+ if (item->state & GTK_STATE_FLAG_SELECTED)
+ return;
+
+ item->state |= GTK_STATE_FLAG_SELECTED;
+ self->priv->selection = g_list_prepend (self->priv->selection, GINT_TO_POINTER (pos));
+ self->priv->selection_changed = TRUE;
+
+ _gth_grid_view_queue_draw_item (self, item);
+}
+
+
+static void
+_gth_grid_view_unselect_item (GthGridView *self,
+ int pos)
+{
+ GList *link;
+ GthGridViewItem *item;
+
+ g_return_if_fail ((pos >= 0) && (pos < self->priv->n_items));
+
+ if (self->priv->selection_mode == GTK_SELECTION_NONE)
+ return;
+
+ link = g_list_nth (self->priv->items, pos);
+ g_return_if_fail (link != NULL);
+
+ item = link->data;
+
+ if (! (item->state & GTK_STATE_FLAG_SELECTED))
+ return;
+
+ item->state ^= GTK_STATE_FLAG_SELECTED;
+ self->priv->selection = g_list_remove (self->priv->selection, GINT_TO_POINTER (pos));
+ self->priv->selection_changed = TRUE;
+
+ _gth_grid_view_queue_draw_item (self, item);
+}
+
+
+static void
+_gth_grid_view_set_item_selected (GthGridView *self,
+ gboolean selected,
+ int pos)
+{
+ if (selected)
+ _gth_grid_view_select_item (self, pos);
+ else
+ _gth_grid_view_unselect_item (self, pos);
+}
+
+
+static void
+_gth_grid_view_emit_selection_changed (GthGridView *self)
+{
+ if (self->priv->selection_changed && (self->priv->selection_mode != GTK_SELECTION_NONE)) {
+ gth_file_selection_changed (GTH_FILE_SELECTION (self));
+ self->priv->selection_changed = FALSE;
+ }
+}
+
+
+static void
+_gth_grid_view_set_item_selected_and_emit_signal (GthGridView *self,
+ gboolean select,
+ int pos)
+{
+ _gth_grid_view_set_item_selected (self, select, pos);
+ _gth_grid_view_emit_selection_changed (self);
+}
+
+
+static int
+_gth_grid_view_unselect_all (GthGridView *self,
+ gpointer keep_selected)
+{
+ GthGridViewPrivate *priv = self->priv;
+ int idx;
+ GList *scan;
+ int i;
+
+ idx = 0;
+ for (scan = priv->items, i = 0;
+ scan != NULL;
+ scan = scan->next, i++)
+ {
+ GthGridViewItem *item = scan->data;
+
+ if (item == keep_selected)
+ idx = i;
+ else if (item->state & GTK_STATE_FLAG_SELECTED)
+ _gth_grid_view_set_item_selected (self, FALSE, i);
+ }
+
+ return idx;
+}
+
+
+static void
+gth_grid_view_select (GthFileSelection *selection,
+ int pos)
+{
+ GthGridView *self = GTH_GRID_VIEW (selection);
+ GList *list;
+ int i;
+
+ switch (self->priv->selection_mode) {
+ case GTK_SELECTION_SINGLE:
+ for (list = self->priv->items, i = 0;
+ list != NULL;
+ list = list->next, i++)
+ {
+ GthGridViewItem *item = list->data;
+
+ if ((i != pos) && (item->state & GTK_STATE_FLAG_SELECTED))
+ _gth_grid_view_set_item_selected (self, FALSE, i);
+ }
+ _gth_grid_view_set_item_selected (self, TRUE, pos);
+ _gth_grid_view_emit_selection_changed (self);
+ break;
+
+ case GTK_SELECTION_MULTIPLE:
+ self->priv->select_pending = FALSE;
+
+ _gth_grid_view_unselect_all (self, NULL);
+ _gth_grid_view_set_item_selected_and_emit_signal (self, TRUE, pos);
+ self->priv->last_selected_pos = pos;
+ self->priv->last_selected_item = g_list_nth (self->priv->items, pos)->data;
+ break;
+
+ default:
+ break;
+ }
+}
+
+
+static void
+gth_grid_view_unselect (GthFileSelection *selection,
+ int pos)
+{
+ _gth_grid_view_set_item_selected_and_emit_signal (GTH_GRID_VIEW (selection), FALSE, pos);
+}
+
+
+static void
+_gth_grid_view_select_all (GthGridView *self)
+{
+ GList *scan;
+ int i;
+
+ for (scan = self->priv->items, i = 0;
+ scan != NULL;
+ scan = scan->next, i++)
+ {
+ GthGridViewItem *item = scan->data;
+
+ if (! (item->state & GTK_STATE_FLAG_SELECTED))
+ _gth_grid_view_set_item_selected (self, TRUE, i);
+ }
+}
+
+
+static void
+gth_grid_view_select_all (GthFileSelection *selection)
+{
+ GthGridView *self = GTH_GRID_VIEW (selection);
+
+ _gth_grid_view_select_all (self);
+ _gth_grid_view_emit_selection_changed (self);
+}
+
+
+static void
+gth_grid_view_unselect_all (GthFileSelection *selection)
+{
+ GthGridView *self = GTH_GRID_VIEW (selection);
+
+ _gth_grid_view_unselect_all (self, NULL);
+ _gth_grid_view_emit_selection_changed (self);
+}
+
+
+static gboolean
+gth_grid_view_is_selected (GthFileSelection *selection,
+ int pos)
+{
+ GthGridView *self = GTH_GRID_VIEW (selection);
+ GList *scan;
+
+ for (scan = self->priv->selection; scan; scan = scan->next)
+ if (GPOINTER_TO_INT (scan->data) == pos)
+ return TRUE;
+
+ return FALSE;
+}
+
+
+static GtkTreePath *
+gth_grid_view_get_first_selected (GthFileSelection *selection)
+{
+ GthGridView *self = GTH_GRID_VIEW (selection);
+ GList *scan;
+ int pos;
+
+ scan = self->priv->selection;
+ if (scan == NULL)
+ return NULL;
+
+ pos = GPOINTER_TO_INT (scan->data);
+ for (scan = scan->next; scan; scan = scan->next)
+ pos = MIN (pos, GPOINTER_TO_INT (scan->data));
+
+ return gtk_tree_path_new_from_indices (pos, -1);
+}
+
+
+static GtkTreePath *
+gth_grid_view_get_last_selected (GthFileSelection *selection)
+{
+ GthGridView *self = GTH_GRID_VIEW (selection);
+ GList *scan;
+ int pos;
+
+ scan = self->priv->selection;
+ if (scan == NULL)
+ return NULL;
+
+ pos = GPOINTER_TO_INT (scan->data);
+ for (scan = scan->next; scan; scan = scan->next)
+ pos = MAX (pos, GPOINTER_TO_INT (scan->data));
+
+ return gtk_tree_path_new_from_indices (pos, -1);
+}
+
+
+static guint
+gth_grid_view_get_n_selected (GthFileSelection *selection)
+{
+ return g_list_length (GTH_GRID_VIEW (selection)->priv->selection);
+}
+
+
+/* -- GthFileView interface -- */
+
+
+static void
+model_row_changed_cb (GtkTreeModel *tree_model,
+ GtkTreePath *path,
+ GtkTreeIter *iter,
+ gpointer user_data)
+{
+ GthGridView *self = user_data;
+ int pos;
+ GList *link;
+ GthFileData *file_data;
+ GdkPixbuf *thumbnail;
+ gboolean is_icon;
+ GthGridViewItem *item;
+
+ gtk_tree_model_get (tree_model,
+ iter,
+ GTH_FILE_STORE_FILE_DATA_COLUMN, &file_data,
+ GTH_FILE_STORE_THUMBNAIL_COLUMN, &thumbnail,
+ GTH_FILE_STORE_IS_ICON_COLUMN, &is_icon,
+ -1);
+
+ pos = gtk_tree_path_get_indices (path)[0];
+ link = g_list_nth (self->priv->items, pos);
+ g_return_if_fail (link != NULL);
+
+ item = GTH_GRID_VIEW_ITEM (link->data);
+ gth_grid_view_item_set_file_data (item, file_data);
+ gth_grid_view_item_set_thumbnail (item, thumbnail);
+ item->is_icon = is_icon;
+ gth_grid_view_item_update_caption (item, self->priv->caption_attributes_v);
+
+ _gth_grid_view_queue_relayout_from_position (self, pos);
+
+ g_object_unref (file_data);
+ g_object_unref (thumbnail);
+}
+
+
+static void
+model_row_deleted_cb (GtkTreeModel *tree_model,
+ GtkTreePath *path,
+ gpointer user_data)
+{
+ GthGridView *self = user_data;
+ int pos;
+ GList *link;
+
+ pos = gtk_tree_path_get_indices (path)[0];
+ link = g_list_nth (self->priv->items, pos);
+ self->priv->items = g_list_remove_link (self->priv->items, link);
+ self->priv->n_items--;
+
+ _gth_grid_view_keep_focus_consistent (self);
+ _gth_grid_view_queue_relayout_from_position (self, pos);
+
+ gth_grid_view_item_unref (GTH_GRID_VIEW_ITEM (link->data));
+ g_list_free (link);
+}
+
+
+static void
+model_row_inserted_cb (GtkTreeModel *tree_model,
+ GtkTreePath *path,
+ GtkTreeIter *iter,
+ gpointer user_data)
+{
+ GthGridView *self = user_data;
+ GthFileData *file_data;
+ GdkPixbuf *thumbnail;
+ gboolean is_icon;
+ GthGridViewItem *item;
+ int pos;
+
+ gtk_tree_model_get (tree_model,
+ iter,
+ GTH_FILE_STORE_FILE_DATA_COLUMN, &file_data,
+ GTH_FILE_STORE_THUMBNAIL_COLUMN, &thumbnail,
+ GTH_FILE_STORE_IS_ICON_COLUMN, &is_icon,
+ -1);
+ item = gth_grid_view_item_new (self,
+ file_data,
+ thumbnail,
+ is_icon,
+ self->priv->caption_attributes_v);
+ pos = gtk_tree_path_get_indices (path)[0];
+ self->priv->items = g_list_insert (self->priv->items, item, pos);
+ self->priv->n_items++;
+
+ _gth_grid_view_queue_relayout_from_position (self, pos);
+
+ g_object_unref (file_data);
+ g_object_unref (thumbnail);
+}
+
+
+static void
+model_rows_reordered_cb (GtkTreeModel *tree_model,
+ GtkTreePath *path,
+ GtkTreeIter *iter,
+ gpointer new_order,
+ gpointer user_data)
+{
+ GthGridView *self = user_data;
+ GList *items;
+ int i;
+ int min_changed_pos;
+ GList *scan;
+
+ /* change the order of the items list */
+
+ min_changed_pos = -1;
+ items = NULL;
+ for (i = 0; i < self->priv->n_items; i++) {
+ GList *link;
+ int old_pos;
+
+ old_pos = ((int *) new_order)[i];
+ if ((min_changed_pos == -1) && (old_pos != i))
+ min_changed_pos = i;
+
+ link = g_list_nth (self->priv->items, old_pos);
+ g_return_if_fail (link != NULL);
+ items = g_list_prepend (items, link->data);
+ }
+ items = g_list_reverse (items);
+
+ g_list_free (self->priv->items);
+ self->priv->items = items;
+
+ /* update the selection */
+
+ for (scan = self->priv->selection; scan; scan = scan->next) {
+ int selected_pos = GPOINTER_TO_INT (scan->data);
+
+ for (i = 0; i < self->priv->n_items; i++) {
+ int old_pos = ((int *) new_order)[i];
+
+ if (selected_pos == old_pos) {
+ scan->data = GINT_TO_POINTER (i);
+ break;
+ }
+ }
+ }
+
+ /* relayout from the minimum changed position */
+
+ if (min_changed_pos >= 0)
+ _gth_grid_view_queue_relayout_from_position (self, min_changed_pos);
+}
+
+
+static void
+model_thumbnail_changed_cb (GtkTreeModel *tree_model,
+ GtkTreePath *path,
+ GtkTreeIter *iter,
+ gpointer user_data)
+{
+ GthGridView *self = user_data;
+ int pos;
+ GList *link;
+ GdkPixbuf *thumbnail;
+ gboolean is_icon;
+ GthGridViewItem *item;
+
+ gtk_tree_model_get (tree_model,
+ iter,
+ GTH_FILE_STORE_THUMBNAIL_COLUMN, &thumbnail,
+ GTH_FILE_STORE_IS_ICON_COLUMN, &is_icon,
+ -1);
+
+ pos = gtk_tree_path_get_indices (path)[0];
+ link = g_list_nth (self->priv->items, pos);
+ g_return_if_fail (link != NULL);
+
+ item = GTH_GRID_VIEW_ITEM (link->data);
+ gth_grid_view_item_set_thumbnail (item, thumbnail);
+ item->is_icon = is_icon;
+
+ _gth_grid_view_queue_draw_item (self, item);
+
+ g_object_unref (thumbnail);
+}
+
+
+static void
+gth_grid_view_set_model (GthFileView *file_view,
+ GtkTreeModel *model)
+{
+ GthGridView *self = GTH_GRID_VIEW (file_view);
+
+ if (model != NULL)
+ g_object_ref (model);
+ if (self->priv->model != NULL) {
+ g_signal_handlers_disconnect_by_data (self->priv->model, self);
+ g_object_unref (self->priv->model);
+ }
+ self->priv->model = model;
+ g_object_notify (G_OBJECT (self), "model");
+
+ if (self->priv->model == NULL)
+ return;
+
+ g_signal_connect (self->priv->model,
+ "row-changed",
+ G_CALLBACK (model_row_changed_cb),
+ self);
+ g_signal_connect (self->priv->model,
+ "row-deleted",
+ G_CALLBACK (model_row_deleted_cb),
+ self);
+ g_signal_connect (self->priv->model,
+ "row-inserted",
+ G_CALLBACK (model_row_inserted_cb),
+ self);
+ g_signal_connect (self->priv->model,
+ "rows-reordered",
+ G_CALLBACK (model_rows_reordered_cb),
+ self);
+ g_signal_connect (self->priv->model,
+ "thumbnail-changed",
+ G_CALLBACK (model_thumbnail_changed_cb),
+ self);
+}
+
+
+static void
+gth_grid_view_scroll_to (GthFileView *file_view,
+ int pos,
+ double yalign)
+{
+ GthGridView *self = GTH_GRID_VIEW (file_view);
+ int n_line;
+ int y;
+ int i;
+ GList *line;
+
+ g_return_if_fail ((pos >= 0) && (pos < self->priv->n_items));
+ g_return_if_fail ((yalign >= 0.0) && (yalign <= 1.0));
+
+ if (self->priv->lines == NULL)
+ return;
+
+ n_line = pos / gth_grid_view_get_items_per_line (self);
+ y = self->priv->cell_spacing;
+ for (i = 0, line = self->priv->lines;
+ (i < n_line) && (line != NULL);
+ i++, line = line->next)
+ {
+ y += GTH_GRID_VIEW_LINE (line->data)->height + self->priv->cell_spacing;
+ }
+
+ if (line != NULL) {
+ int h;
+ double value;
+
+ h = gtk_widget_get_allocated_height (GTK_WIDGET (self)) - GTH_GRID_VIEW_LINE (line->data)->height - self->priv->cell_spacing;
+ value = CLAMP ((y - (h * yalign) - (1.0 - yalign) * self->priv->cell_spacing),
+ 0.0,
+ gtk_adjustment_get_upper (self->priv->vadjustment) - gtk_adjustment_get_page_size (self->priv->vadjustment));
+ gtk_adjustment_set_value (self->priv->vadjustment, value);
+ }
+}
+
+
+static GthVisibility
+gth_grid_view_get_visibility (GthFileView *file_view,
+ int pos)
+{
+ GthGridView *self = GTH_GRID_VIEW (file_view);
+ int cell_top;
+ int line_n;
+ int i;
+ GList *line;
+ int cell_bottom;
+ int window_top;
+ int window_bottom;
+
+ g_return_val_if_fail ((pos >= 0) && (pos < self->priv->n_items), GTH_VISIBILITY_NONE);
+
+ if (self->priv->lines == NULL)
+ return GTH_VISIBILITY_NONE;
+
+ cell_top = self->priv->cell_spacing;
+ line_n = pos / gth_grid_view_get_items_per_line (self);
+ for (i = 0, line = self->priv->lines;
+ (i < line_n) && (line != NULL);
+ i++, line = line->next)
+ {
+ cell_top += GTH_GRID_VIEW_LINE (line->data)->height + self->priv->cell_spacing;
+ }
+
+ if (line == NULL)
+ return GTH_VISIBILITY_NONE;
+
+ cell_bottom = cell_top + GTH_GRID_VIEW_LINE (line->data)->height + self->priv->cell_spacing;
+ window_top = gtk_adjustment_get_value (self->priv->vadjustment);
+ window_bottom = window_top + gtk_widget_get_allocated_height (GTK_WIDGET (self));
+
+ if (cell_bottom < window_top)
+ return GTH_VISIBILITY_NONE;
+
+ if (cell_top > window_bottom)
+ return GTH_VISIBILITY_NONE;
+
+ if ((cell_top >= window_top) && (cell_bottom <= window_bottom))
+ return GTH_VISIBILITY_FULL;
+
+ if ((cell_top < window_top) && (cell_bottom >= window_top))
+ return GTH_VISIBILITY_PARTIAL_TOP;
+
+ if ((cell_top <= window_bottom) && (cell_bottom > window_bottom))
+ return GTH_VISIBILITY_PARTIAL_BOTTOM;
+
+ return GTH_VISIBILITY_PARTIAL;
+}
+
+
+static int
+gth_grid_view_get_at_position (GthFileView *file_view,
+ int x,
+ int y)
+{
+ GthGridView *self = GTH_GRID_VIEW (file_view);
+ GList *scan;
+ int n;
+
+ for (scan = self->priv->items, n = 0;
+ scan != NULL;
+ scan = scan->next, n++)
+ {
+ GthGridViewItem *item = scan->data;
+
+ if (_cairo_rectangle_contains_point (&item->thumbnail_area, x, y)
+ || _cairo_rectangle_contains_point (&item->caption_area, x, y))
+ {
+ return n;
+ }
+ }
+
+ return -1;
+}
+
+
+static void
+gth_grid_view_cursor_changed (GthFileView *file_view,
+ int pos)
+{
+ GthGridView *self = GTH_GRID_VIEW (file_view);
+ GthGridViewItem *old_item;
+ GList *link;
+ GthGridViewItem *new_item;
+
+ old_item = NULL;
+ if (self->priv->focused_item >= 0) {
+ link = g_list_nth (self->priv->items, self->priv->focused_item);
+ if (link != NULL)
+ old_item = link->data;
+ }
+
+ link = g_list_nth (self->priv->items, pos);
+ g_return_if_fail (link != NULL);
+
+ self->priv->focused_item = pos;
+ if (old_item != NULL) {
+ old_item->state ^= GTK_STATE_FLAG_FOCUSED | GTK_STATE_FLAG_ACTIVE;
+ _gth_grid_view_queue_draw_item (self, old_item);
+ }
+
+ new_item = link->data;
+ new_item->state |= GTK_STATE_FLAG_FOCUSED | GTK_STATE_FLAG_ACTIVE;
+ _gth_grid_view_queue_draw_item (self, new_item);
+}
+
+
+static int
+gth_grid_view_get_cursor (GthFileView *file_view)
+{
+ GthGridView *self = GTH_GRID_VIEW (file_view);
+
+ if (! gtk_widget_has_focus (GTK_WIDGET (self)))
+ return -1;
+ else
+ return self->priv->focused_item;
+}
+
+
+static void
+gth_grid_view_enable_drag_source (GthFileView *file_view,
+ GdkModifierType start_button_mask,
+ const GtkTargetEntry *targets,
+ int n_targets,
+ GdkDragAction actions)
+{
+ GthGridView *self = GTH_GRID_VIEW (file_view);
+
+ if (self->priv->drag_target_list != NULL)
+ gtk_target_list_unref (self->priv->drag_target_list);
+
+ self->priv->drag_source_enabled = TRUE;
+ self->priv->drag_start_button_mask = start_button_mask;
+ self->priv->drag_target_list = gtk_target_list_new (targets, n_targets);
+ self->priv->drag_actions = actions;
+}
+
+
+static void
+gth_grid_view_unset_drag_source (GthFileView *file_view)
+{
+ GTH_GRID_VIEW (file_view)->priv->drag_source_enabled = FALSE;
+}
+
+
+static void
+gth_grid_view_enable_drag_dest (GthFileView *file_view,
+ const GtkTargetEntry *targets,
+ int n_targets,
+ GdkDragAction actions)
+{
+ GthGridView *self = GTH_GRID_VIEW (file_view);
+
+ gtk_drag_dest_set (GTK_WIDGET (self),
+ 0,
+ targets,
+ n_targets,
+ actions);
+}
+
+
+static void
+gth_grid_view_unset_drag_dest (GthFileView *file_view)
+{
+ GthGridView *self = GTH_GRID_VIEW (file_view);
+
+ gtk_drag_dest_unset (GTK_WIDGET (self));
+}
+
+
+static int
+_gth_grid_view_get_drop_target_at (GthGridView *self,
+ int x,
+ int y)
+{
+ int row;
+ int height;
+ GList *scan;
+ int items_per_line;
+ int col;
+
+ x += gtk_adjustment_get_value (self->priv->hadjustment);
+ y += gtk_adjustment_get_value (self->priv->vadjustment);
+
+ row = -1;
+ height = self->priv->cell_spacing;
+ for (scan = self->priv->lines; scan && (height < y); scan = scan->next) {
+ height += GTH_GRID_VIEW_LINE (scan->data)->height + self->priv->cell_spacing;
+ row++;
+ }
+ if (height < y)
+ row++;
+ row = MAX (row, 0);
+
+ items_per_line = gth_grid_view_get_items_per_line (self);
+ col = (x - (self->priv->cell_spacing / 2)) / (self->priv->cell_size + self->priv->cell_spacing) + 1;
+ col = MIN (col, items_per_line);
+
+ return (items_per_line * row) + col - 1;
+}
+
+
+static void
+gth_grid_view_set_drag_dest_pos (GthFileView *file_view,
+ GdkDragContext *context,
+ int x,
+ int y,
+ guint time,
+ int *pos)
+{
+ GthGridView *self = GTH_GRID_VIEW (file_view);
+ GthDropPosition drop_pos;
+ int drop_image;
+
+ g_return_if_fail (GTH_IS_GRID_VIEW (self));
+
+ drop_pos = self->priv->drop_pos;
+ drop_image = self->priv->drop_item;
+
+ if ((x < 0) && (y < 0) && (drop_pos != GTH_DROP_POSITION_NONE)) {
+ if (drop_pos == GTH_DROP_POSITION_RIGHT)
+ drop_image++;
+ drop_pos = GTH_DROP_POSITION_NONE;
+ }
+ else {
+ drop_image = _gth_grid_view_get_drop_target_at (self, x, y);
+
+ if (drop_image < 0) {
+ drop_image = 0;
+ drop_pos = GTH_DROP_POSITION_LEFT;
+ }
+ else if (drop_image >= self->priv->n_items) {
+ drop_image = self->priv->n_items - 1;
+ drop_pos = GTH_DROP_POSITION_RIGHT;
+ }
+ else {
+ GthGridViewItem *item = g_list_nth (self->priv->items, drop_image)->data;
+ if (x - item->area.x > self->priv->cell_size / 2)
+ drop_pos = GTH_DROP_POSITION_RIGHT;
+ else
+ drop_pos = GTH_DROP_POSITION_LEFT;
+ }
+ }
+
+ if (pos != NULL) {
+ *pos = drop_image;
+ if (gtk_widget_get_direction(GTK_WIDGET (self)) == GTK_TEXT_DIR_RTL) {
+ if (drop_pos == GTH_DROP_POSITION_LEFT)
+ *pos = *pos + 1;
+ }
+ else {
+ if (drop_pos == GTH_DROP_POSITION_RIGHT)
+ *pos = *pos + 1;
+ }
+ }
+
+ if ((drop_pos != self->priv->drop_pos) || (drop_image != self->priv->drop_item)) {
+ self->priv->drop_pos = drop_pos;
+ self->priv->drop_item = drop_image;
+ gtk_widget_queue_draw (GTK_WIDGET (self));
+ }
+}
+
+
+void
+gth_grid_view_get_drag_dest_pos (GthFileView *file_view,
+ int *pos)
+{
+ GthGridView *self = GTH_GRID_VIEW (file_view);
+
+ g_return_if_fail (pos != NULL);
+ *pos = self->priv->drop_item;
+}
+
+
+/* GtkWidget methods */
+
+
+static void
+_gth_grid_view_select_range (GthGridView *self,
+ GthGridViewItem *item,
+ int pos)
+{
+ int a, b;
+ GList *scan;
+
+ if (self->priv->last_selected_pos == -1) {
+ self->priv->last_selected_pos = pos;
+ self->priv->last_selected_item = item;
+ }
+
+ if (pos < self->priv->last_selected_pos) {
+ a = pos;
+ b = self->priv->last_selected_pos;
+ }
+ else {
+ a = self->priv->last_selected_pos;
+ b = pos;
+ }
+
+ for (scan = g_list_nth (self->priv->items, a);
+ a <= b;
+ a++, scan = scan->next)
+ {
+ GthGridViewItem *item = scan->data;
+
+ if (! (item->state & GTK_STATE_FLAG_SELECTED))
+ _gth_grid_view_set_item_selected (self, TRUE, a);
+ }
+
+ _gth_grid_view_set_item_selected (self, TRUE, pos);
+ _gth_grid_view_emit_selection_changed (self);
+
+ gth_file_view_set_cursor (GTH_FILE_VIEW (self), pos);
+}
+
+
+static void
+_gth_grid_view_select_single (GthGridView *self,
+ GthGridViewItem *item,
+ int pos,
+ GdkEventButton *event)
+{
+ if (item->state & GTK_STATE_FLAG_SELECTED) {
+ /* postpone selection to handle dragging. */
+ self->priv->select_pending = TRUE;
+ self->priv->select_pending_pos = pos;
+ self->priv->select_pending_item = item;
+ }
+ else {
+ _gth_grid_view_unselect_all (self, NULL);
+ _gth_grid_view_set_item_selected_and_emit_signal (self, TRUE, pos);
+ self->priv->last_selected_pos = pos;
+ self->priv->last_selected_item = item;
+ }
+
+ gth_file_view_set_cursor (GTH_FILE_VIEW (self), pos);
+}
+
+
+static void
+_gth_grid_view_select_multiple (GthGridView *self,
+ GthGridViewItem *item,
+ int pos,
+ GdkEventButton *event)
+{
+ gboolean range;
+ gboolean additive;
+
+ range = (event->state & GDK_SHIFT_MASK) != 0;
+ additive = (event->state & GDK_CONTROL_MASK) != 0;
+
+ if (! additive && ! range) {
+ _gth_grid_view_select_single (self, item, pos, event);
+ return;
+ }
+
+ if (range) {
+ _gth_grid_view_unselect_all (self, item);
+ _gth_grid_view_select_range (self, item, pos);
+ }
+ else if (additive) {
+ _gth_grid_view_set_item_selected_and_emit_signal (self, ! (item->state & GTK_STATE_FLAG_SELECTED), pos);
+ self->priv->last_selected_pos = pos;
+ self->priv->last_selected_item = item;
+ }
+
+ gth_file_view_set_cursor (GTH_FILE_VIEW (self), pos);
+}
+
+
+static void
+gth_grid_view_store_items_state (GthGridView *self)
+{
+ GList *scan;
+
+ for (scan = self->priv->items; scan; scan = scan->next) {
+ GthGridViewItem *item = scan->data;
+ item->tmp_state = item->state;
+ }
+}
+
+
+static int
+gth_grid_view_button_press (GtkWidget *widget,
+ GdkEventButton *event)
+{
+ GthGridView *self = GTH_GRID_VIEW (widget);
+ int retval = FALSE;
+ int pos;
+
+ if (event->window != self->priv->bin_window)
+ return FALSE;
+
+ if (! gtk_widget_has_focus (widget))
+ gtk_widget_grab_focus (widget);
+
+ pos = gth_grid_view_get_at_position (GTH_FILE_VIEW (self), event->x, event->y);
+
+ if ((pos != -1) && (event->button == 1) && (event->type == GDK_2BUTTON_PRESS)) {
+ /* Double click activates the item */
+
+ if (((event->state & GDK_CONTROL_MASK) != GDK_CONTROL_MASK)
+ && ((event->state & GDK_SHIFT_MASK) != GDK_SHIFT_MASK))
+ {
+ gth_file_view_activated (GTH_FILE_VIEW (self), pos);
+ }
+ retval = TRUE;
+ }
+ else if ((pos != -1) && (event->button == 2) && (event->type == GDK_BUTTON_PRESS)) {
+ /* This can be the start of a dragging action. */
+
+ if (! (event->state & GDK_CONTROL_MASK)
+ && ! (event->state & GDK_SHIFT_MASK)
+ && self->priv->drag_source_enabled)
+ {
+ self->priv->dragging = TRUE;
+ self->priv->drag_button = 2;
+ self->priv->drag_start_x = event->x;
+ self->priv->drag_start_y = event->y;
+ }
+ retval = TRUE;
+ }
+ else if ((pos != -1) && (event->button == 1) && (event->type == GDK_BUTTON_PRESS)) {
+ /* This can be the start of a dragging action. */
+
+ self->priv->dragging = TRUE;
+ self->priv->drag_button = 1;
+ self->priv->drag_start_x = event->x;
+ self->priv->drag_start_y = event->y;
+
+ if (self->priv->selection_mode != GTK_SELECTION_NONE) {
+ GthGridViewItem *item;
+
+ item = g_list_nth (self->priv->items, pos)->data;
+ if (self->priv->selection_mode == GTK_SELECTION_MULTIPLE)
+ _gth_grid_view_select_multiple (self, item, pos, event);
+ else
+ _gth_grid_view_select_single (self, item, pos, event);
+ }
+ else
+ gth_file_view_set_cursor (GTH_FILE_VIEW (self), pos);
+
+ retval = TRUE;
+ }
+ else if ((pos == -1) && (event->button == 1) && (event->type == GDK_BUTTON_PRESS) && (self->priv->selection_mode == GTK_SELECTION_MULTIPLE)) {
+ /* This can be the start of a selection */
+
+ if ((event->state & GDK_CONTROL_MASK) == 0)
+ _gth_grid_view_unselect_all (self, NULL);
+
+ if (! self->priv->selecting) {
+ gth_grid_view_store_items_state (self);
+
+ self->priv->sel_start_x = event->x;
+ self->priv->sel_start_y = event->y;
+ self->priv->selection_area.x = event->x;
+ self->priv->selection_area.y = event->y;
+ self->priv->selection_area.width = 0;
+ self->priv->selection_area.height = 0;
+ self->priv->sel_state = event->state;
+ self->priv->selecting = TRUE;
+ }
+ retval = TRUE;
+ }
+
+ return retval;
+}
+
+
+static gboolean
+gth_grid_view_item_is_inside_area (GthGridViewItem *item,
+ int x1,
+ int y1,
+ int x2,
+ int y2)
+{
+ GdkRectangle area;
+ GdkRectangle item_area;
+ double x_ofs;
+ double y_ofs;
+
+ if ((x1 == x2) && (y1 == y2))
+ return FALSE;
+
+ area.x = x1;
+ area.y = y1;
+ area.width = x2 - x1;
+ area.height = y2 - y1;
+
+ item_area = item->area;
+ x_ofs = item_area.width / 6;
+ y_ofs = item_area.height / 6;
+ item_area.x += x_ofs;
+ item_area.y += y_ofs;
+ item_area.width -= x_ofs * 2;
+ item_area.height -= y_ofs * 2;
+
+ return gdk_rectangle_intersect (&item_area, &area, NULL);
+}
+
+
+static void
+_gth_grid_view_update_mouse_selection (GthGridView *self,
+ int x,
+ int y)
+{
+ cairo_rectangle_int_t old_selection_area;
+ cairo_region_t *invalid_region;
+ int x1, y1, x2, y2;
+ GtkAllocation allocation;
+ cairo_region_t *common_region;
+ gboolean additive;
+ gboolean invert;
+ int min_y, max_y;
+ GList *l, *begin, *end;
+ int i, begin_idx, end_idx;
+
+ old_selection_area = self->priv->selection_area;
+
+ /* calculate the new selection area */
+
+ if (self->priv->sel_start_x < x) {
+ x1 = self->priv->sel_start_x;
+ x2 = x;
+ }
+ else {
+ x1 = x;
+ x2 = self->priv->sel_start_x;
+ }
+
+ if (self->priv->sel_start_y < y) {
+ y1 = self->priv->sel_start_y;
+ y2 = y;
+ }
+ else {
+ y1 = y;
+ y2 = self->priv->sel_start_y;
+ }
+
+ gtk_widget_get_allocation (GTK_WIDGET (self), &allocation);
+ allocation.width = MAX (allocation.width, self->priv->width);
+ allocation.height = MAX (allocation.height, self->priv->height);
+ x1 = CLAMP (x1, 0, allocation.width);
+ y1 = CLAMP (y1, 0, allocation.height);
+ x2 = CLAMP (x2, 0, allocation.width);
+ y2 = CLAMP (y2, 0, allocation.height);
+
+ self->priv->selection_area.x = x1;
+ self->priv->selection_area.y = y1;
+ self->priv->selection_area.width = x2 - x1;
+ self->priv->selection_area.height = y2 - y1;
+
+ /* repaint the changed area */
+
+ invalid_region = cairo_region_create_rectangle (&old_selection_area);
+ cairo_region_union_rectangle (invalid_region, &self->priv->selection_area);
+
+ common_region = cairo_region_create_rectangle (&old_selection_area);
+ cairo_region_intersect_rectangle (common_region, &self->priv->selection_area);
+
+ if (! cairo_region_is_empty (common_region)) {
+ cairo_rectangle_int_t common_region_extents;
+
+ /* invalidate the border as well */
+ cairo_region_get_extents (common_region, &common_region_extents);
+ common_region_extents.x += 2;
+ common_region_extents.y += 2;
+ common_region_extents.width -= 4;
+ common_region_extents.height -= 4;
+
+ cairo_region_subtract_rectangle (invalid_region, &common_region_extents);
+ }
+ gdk_window_invalidate_region (self->priv->bin_window, invalid_region, FALSE);
+
+ cairo_region_destroy (common_region);
+ cairo_region_destroy (invalid_region);
+
+ /* select or unselect images as appropriate */
+
+ additive = self->priv->sel_state & GDK_SHIFT_MASK;
+ invert = self->priv->sel_state & GDK_CONTROL_MASK;
+
+ /* Consider only images in the min_y --> max_y offset. */
+
+ min_y = self->priv->selection_area.y;
+ max_y = self->priv->selection_area.y + self->priv->selection_area.height;
+
+ begin_idx = get_first_visible_at_offset (self, min_y);
+ begin = g_list_nth (self->priv->items, begin_idx);
+
+ end_idx = get_last_visible_at_offset (self, max_y);
+ end = g_list_nth (self->priv->items, end_idx);
+
+ if (end != NULL)
+ end = end->next;
+
+ gdk_window_freeze_updates (self->priv->bin_window);
+
+ x1 = self->priv->selection_area.x;
+ y1 = self->priv->selection_area.y;
+ x2 = x1 + self->priv->selection_area.width;
+ y2 = y1 + self->priv->selection_area.height;
+
+ for (l = begin, i = begin_idx; l != end; l = l->next, i++) {
+ GthGridViewItem *item = l->data;
+ gboolean selection_changed;
+
+ selection_changed = (item->state & GTK_STATE_FLAG_SELECTED) != (item->tmp_state & GTK_STATE_FLAG_SELECTED);
+ if (gth_grid_view_item_is_inside_area (item, x1, y1, x2, y2)) {
+ if (invert) {
+ if (! selection_changed)
+ _gth_grid_view_set_item_selected (self, ! (item->state & GTK_STATE_FLAG_SELECTED), i);
+ }
+ else if (additive) {
+ if (! (item->state & GTK_STATE_FLAG_SELECTED))
+ _gth_grid_view_set_item_selected (self, TRUE, i);
+ }
+ else {
+ if (! (item->state & GTK_STATE_FLAG_SELECTED))
+ _gth_grid_view_set_item_selected (self, TRUE, i);
+ }
+ }
+ else if (selection_changed)
+ _gth_grid_view_set_item_selected (self, (item->tmp_state & GTK_STATE_FLAG_SELECTED), i);
+ }
+
+ gdk_window_thaw_updates (self->priv->bin_window);
+
+ _gth_grid_view_emit_selection_changed (self);
+}
+
+
+static void
+_gth_grid_view_stop_selecting (GthGridView *self)
+{
+ if (! self->priv->selecting)
+ return;
+
+ self->priv->selecting = FALSE;
+ self->priv->sel_start_x = 0;
+ self->priv->sel_start_y = 0;
+
+ if (self->priv->scroll_timeout != 0) {
+ g_source_remove (self->priv->scroll_timeout);
+ self->priv->scroll_timeout = 0;
+ }
+
+ gdk_window_invalidate_rect (self->priv->bin_window,
+ &self->priv->selection_area,
+ FALSE);
+}
+
+
+static gboolean
+gth_grid_view_button_release (GtkWidget *widget,
+ GdkEventButton *event)
+{
+ GthGridView *self = GTH_GRID_VIEW (widget);
+
+ if (self->priv->dragging) {
+ self->priv->select_pending = self->priv->select_pending && ! self->priv->drag_started;
+ _gth_grid_view_stop_dragging (self);
+ }
+
+ if (self->priv->selecting) {
+ _gth_grid_view_update_mouse_selection (self, event->x, event->y);
+ _gth_grid_view_stop_selecting (self);
+ }
+
+ if (self->priv->select_pending) {
+ self->priv->select_pending = FALSE;
+ _gth_grid_view_unselect_all (self, NULL);
+ _gth_grid_view_set_item_selected_and_emit_signal (self, TRUE, self->priv->select_pending_pos);
+ self->priv->last_selected_pos = self->priv->select_pending_pos;
+ self->priv->last_selected_item = self->priv->select_pending_item;
+ }
+
+ return FALSE;
+}
+
+
+static gboolean
+autoscroll_cb (gpointer user_data)
+{
+ GthGridView *self = user_data;
+ double max_value;
+ double value;
+
+ GDK_THREADS_ENTER ();
+
+ max_value = gtk_adjustment_get_upper (self->priv->vadjustment) - gtk_adjustment_get_page_size (self->priv->vadjustment);
+ value = gtk_adjustment_get_value (self->priv->vadjustment) + self->priv->autoscroll_y_delta;
+ if (value > max_value)
+ value = max_value;
+
+ gtk_adjustment_set_value (self->priv->vadjustment, value);
+ self->priv->event_last_y = self->priv->event_last_y + self->priv->autoscroll_y_delta;
+ _gth_grid_view_update_mouse_selection (self, self->priv->event_last_x, self->priv->event_last_y);
+
+ GDK_THREADS_LEAVE ();
+
+ return TRUE;
+}
+
+
+static gboolean
+gth_grid_view_motion_notify (GtkWidget *widget,
+ GdkEventMotion *event)
+{
+ GthGridView *self = GTH_GRID_VIEW (widget);
+
+ if (self->priv->dragging) {
+ if (! self->priv->drag_started
+ && (self->priv->selection != NULL)
+ && gtk_drag_check_threshold (widget,
+ self->priv->drag_start_x,
+ self->priv->drag_start_y,
+ event->x,
+ event->y))
+ {
+ int pos;
+ GdkDragContext *context;
+ gboolean multi_dnd;
+
+ /**/
+
+ pos = gth_grid_view_get_at_position (GTH_FILE_VIEW (self),
+ self->priv->drag_start_x,
+ self->priv->drag_start_y);
+ if (pos != -1)
+ gth_file_view_set_cursor (GTH_FILE_VIEW (self), pos);
+
+ /**/
+
+ self->priv->drag_started = TRUE;
+ context = gtk_drag_begin (widget,
+ self->priv->drag_target_list,
+ self->priv->drag_actions,
+ self->priv->drag_button,
+ (GdkEvent *) event);
+ if (self->priv->drag_button == 2)
+ gdk_drag_status (context, GDK_ACTION_ASK, event->time);
+
+ /* FIXME: create a cool drag icon here */
+ multi_dnd = self->priv->selection->next != NULL;
+ gtk_drag_set_icon_stock (context,
+ multi_dnd ? GTK_STOCK_DND_MULTIPLE : GTK_STOCK_DND,
+ -4, -4);
+ }
+
+ return TRUE;
+ }
+ else if (self->priv->selecting) {
+ double y_delta;
+
+ y_delta = event->y - gtk_adjustment_get_value (self->priv->vadjustment);
+ if (fabs (y_delta) > MAX_DELTA_FOR_SCROLLING)
+ event->y = gtk_adjustment_get_upper (self->priv->vadjustment);
+
+ _gth_grid_view_update_mouse_selection (self, event->x, event->y);
+
+ /* If we are out of bounds, schedule a timeout that will do
+ * the scrolling */
+
+ y_delta = event->y - gtk_adjustment_get_value (self->priv->vadjustment);
+ if ((y_delta < 0) || (y_delta > gtk_widget_get_allocated_height (widget))) {
+ self->priv->event_last_x = event->x;
+ self->priv->event_last_y = event->y;
+
+ /* Make the stepping relative to the mouse
+ * distance from the canvas.
+ * Also notice the timeout below is small to give a
+ * more smooth movement.
+ */
+ if (y_delta < 0)
+ self->priv->autoscroll_y_delta = y_delta;
+ else
+ self->priv->autoscroll_y_delta = y_delta - gtk_widget_get_allocated_height (widget);
+ self->priv->autoscroll_y_delta /= 2;
+
+ if (self->priv->scroll_timeout == 0)
+ self->priv->scroll_timeout = g_timeout_add (SCROLL_DELAY, autoscroll_cb, self);
+ }
+ else if (self->priv->scroll_timeout != 0) {
+ g_source_remove (self->priv->scroll_timeout);
+ self->priv->scroll_timeout = 0;
+ }
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+static void
+select_range_with_keyboard (GthGridView *self,
+ int next_focused_item)
+{
+ int begin_idx;
+ int end_idx;
+ GList *begin;
+ GList *end;
+ int i;
+ GList *link;
+
+ begin_idx = MIN (MIN (self->priv->first_focused_item, self->priv->focused_item), next_focused_item);
+ end_idx = MAX (MAX (self->priv->first_focused_item, self->priv->focused_item), next_focused_item);
+ begin = g_list_nth (self->priv->items, begin_idx);
+ end = g_list_nth (self->priv->items, end_idx);
+ if (end != NULL)
+ end = end->next;
+
+ gdk_window_freeze_updates (self->priv->bin_window);
+
+ for (link = begin, i = begin_idx; link != end; link = link->next, i++) {
+ if (next_focused_item > self->priv->first_focused_item) {
+ if ((i >= self->priv->first_focused_item) && (i <= next_focused_item))
+ _gth_grid_view_select_item (self, i);
+ else
+ _gth_grid_view_unselect_item (self, i);
+ }
+ else {
+ if ((i >= next_focused_item) && (i <= self->priv->first_focused_item))
+ _gth_grid_view_select_item (self, i);
+ else
+ _gth_grid_view_unselect_item (self, i);
+ }
+ }
+
+ gdk_window_thaw_updates (self->priv->bin_window);
+
+ _gth_grid_view_emit_selection_changed (self);
+}
+
+
+static GList *
+_gth_grid_view_get_line_at_position (GthGridView *self,
+ int pos)
+{
+ GList *link;
+ GthGridViewItem *item;
+ GList *scan;
+
+ link = g_list_nth (self->priv->items, pos);
+ g_return_val_if_fail (link != NULL, NULL);
+
+ item = link->data;
+ for (scan = self->priv->lines; scan; scan = scan->next) {
+ GthGridViewLine *line = scan->data;
+
+ if (g_list_find (line->items, item) != NULL)
+ return scan;
+ }
+
+ return NULL;
+}
+
+
+static int
+_gth_grid_view_get_item_at_page_distance (GthGridView *self,
+ int focused_item,
+ gboolean downward)
+{
+ int old_focused_item;
+ int direction;
+ int h;
+ GList *line;
+ int items_per_line;
+
+ old_focused_item = focused_item;
+ direction = downward ? 1 : -1;
+ h = gtk_widget_get_allocated_height (GTK_WIDGET (self));
+ line = _gth_grid_view_get_line_at_position (self, focused_item);
+ items_per_line = gth_grid_view_get_items_per_line (self);
+
+ while ((h > 0) && (line != NULL)) {
+ h -= GTH_GRID_VIEW_LINE (line->data)->height + self->priv->cell_spacing;
+ if (h > 0) {
+ focused_item = focused_item + direction * items_per_line;
+ if ((focused_item >= self->priv->n_items - 1) || (focused_item <= 0))
+ return focused_item;
+ }
+
+ if (downward)
+ line = line->next;
+ else
+ line = line->prev;
+ }
+
+ if (old_focused_item == focused_item)
+ focused_item = focused_item + (direction * items_per_line);
+
+ return focused_item;
+}
+
+
+static gboolean
+gth_grid_view_move_cursor (GthGridView *self,
+ GthCursorMovement dir,
+ GthSelectionChange sel_change)
+{
+ int items_per_line;
+ int next_focused_item;
+
+ if (self->priv->n_items == 0)
+ return FALSE;
+
+ if (! gtk_widget_has_focus (GTK_WIDGET (self)))
+ return FALSE;
+
+ items_per_line = gth_grid_view_get_items_per_line (self);
+ next_focused_item = self->priv->focused_item;
+
+ if (self->priv->focused_item == -1) {
+ self->priv->first_focused_item = 0;
+ next_focused_item = 0;
+ }
+ else {
+ switch (dir) {
+ case GTH_CURSOR_MOVE_RIGHT:
+ next_focused_item++;
+ break;
+
+ case GTH_CURSOR_MOVE_LEFT:
+ next_focused_item--;
+ break;
+
+ case GTH_CURSOR_MOVE_DOWN:
+ next_focused_item += items_per_line;
+ break;
+
+ case GTH_CURSOR_MOVE_UP:
+ next_focused_item -= items_per_line;
+ break;
+
+ case GTH_CURSOR_MOVE_PAGE_UP:
+ next_focused_item = _gth_grid_view_get_item_at_page_distance (self,
+ next_focused_item,
+ FALSE);
+ break;
+
+ case GTH_CURSOR_MOVE_PAGE_DOWN:
+ next_focused_item = _gth_grid_view_get_item_at_page_distance (self,
+ next_focused_item,
+ TRUE);
+ break;
+
+ case GTH_CURSOR_MOVE_BEGIN:
+ next_focused_item = 0;
+ break;
+
+ case GTH_CURSOR_MOVE_END:
+ next_focused_item = self->priv->n_items - 1;
+ break;
+
+ default:
+ break;
+ }
+
+ if (((next_focused_item < 0) && (self->priv->focused_item == 0))
+ || ((next_focused_item > self->priv->n_items - 1) && (self->priv->focused_item == self->priv->n_items - 1)))
+ {
+ gdk_beep ();
+ }
+ next_focused_item = CLAMP (next_focused_item, 0, self->priv->n_items - 1);
+ }
+
+ if ((dir == GTH_CURSOR_MOVE_UP)
+ || (dir == GTH_CURSOR_MOVE_DOWN)
+ || (dir == GTH_CURSOR_MOVE_PAGE_UP)
+ || (dir == GTH_CURSOR_MOVE_PAGE_DOWN)
+ || (dir == GTH_CURSOR_MOVE_BEGIN)
+ || (dir == GTH_CURSOR_MOVE_END))
+ { /* Vertical movement. */
+ GthVisibility visibility;
+
+ visibility = gth_grid_view_get_visibility (GTH_FILE_VIEW (self), next_focused_item);
+ if (visibility != GTH_VISIBILITY_FULL) {
+ gboolean upward;
+
+ upward = ((dir == GTH_CURSOR_MOVE_UP)
+ || (dir == GTH_CURSOR_MOVE_PAGE_UP)
+ || (dir == GTH_CURSOR_MOVE_BEGIN));
+ gth_grid_view_scroll_to (GTH_FILE_VIEW (self),
+ next_focused_item,
+ upward ? 0.0 : 1.0);
+ }
+ }
+ else {
+ GthVisibility visibility;
+
+ visibility = gth_grid_view_get_visibility (GTH_FILE_VIEW (self), next_focused_item);
+ if (visibility != GTH_VISIBILITY_FULL) {
+ double offset = -1.0;
+
+ switch (visibility) {
+ case GTH_VISIBILITY_NONE:
+ offset = 0.5;
+ break;
+
+ case GTH_VISIBILITY_PARTIAL_TOP:
+ offset = 0.0;
+ break;
+
+ case GTH_VISIBILITY_PARTIAL_BOTTOM:
+ offset = 1.0;
+ break;
+
+ case GTH_VISIBILITY_PARTIAL:
+ case GTH_VISIBILITY_FULL:
+ offset = -1.0;
+ break;
+ }
+
+ if (offset >= 0.0)
+ gth_grid_view_scroll_to (GTH_FILE_VIEW (self),
+ next_focused_item,
+ offset);
+ }
+ }
+
+ if (sel_change == GTH_SELECTION_SET_CURSOR) {
+ _gth_grid_view_unselect_all (self, NULL);
+ _gth_grid_view_set_item_selected (self, TRUE, next_focused_item);
+ _gth_grid_view_emit_selection_changed (self);
+ }
+ else if (sel_change == GTH_SELECTION_SET_RANGE)
+ select_range_with_keyboard (self, next_focused_item);
+
+ gth_file_view_set_cursor (GTH_FILE_VIEW (self), next_focused_item);
+
+ return TRUE;
+}
+
+
+static gboolean
+gth_grid_view_set_cursor_selection (GthGridView *self)
+{
+ GthGridViewItem *item;
+
+ if (self->priv->focused_item == -1)
+ return FALSE;
+
+ item = g_list_nth (self->priv->items, self->priv->focused_item)->data;
+ g_return_val_if_fail (item != NULL, FALSE);
+
+ _gth_grid_view_unselect_all (self, item);
+ _gth_grid_view_select_item (self, self->priv->focused_item);
+ self->priv->last_selected_pos = self->priv->select_pending_pos;
+ self->priv->last_selected_item = self->priv->select_pending_item;
+ _gth_grid_view_emit_selection_changed (self);
+
+ return TRUE;
+}
+
+
+static gboolean
+gth_grid_view_toggle_cursor_selection (GthGridView *self)
+{
+ GList *link;
+ GthGridViewItem *item;
+
+ if (self->priv->focused_item == -1)
+ return FALSE;
+
+ link = g_list_nth (self->priv->items, self->priv->focused_item);
+ g_return_val_if_fail (link != NULL, FALSE);
+
+ item = link->data;
+ if (item->state & GTK_STATE_FLAG_SELECTED)
+ _gth_grid_view_unselect_item (self, self->priv->focused_item);
+ else
+ _gth_grid_view_select_item (self, self->priv->focused_item);
+
+ return TRUE;
+}
+
+
+static void
+_gtk_binding_entry_add_move_cursor_signals (GtkBindingSet *binding_set,
+ guint keyval,
+ GthCursorMovement dir)
+{
+ gtk_binding_entry_add_signal (binding_set, keyval, 0,
+ "move-cursor", 2,
+ G_TYPE_ENUM, dir,
+ G_TYPE_ENUM, GTH_SELECTION_SET_CURSOR);
+
+ gtk_binding_entry_add_signal (binding_set, keyval, GDK_CONTROL_MASK,
+ "move-cursor", 2,
+ G_TYPE_ENUM, dir,
+ G_TYPE_ENUM, GTH_SELECTION_KEEP);
+
+ gtk_binding_entry_add_signal (binding_set, keyval, GDK_SHIFT_MASK,
+ "move-cursor", 2,
+ G_TYPE_ENUM, dir,
+ G_TYPE_ENUM, GTH_SELECTION_SET_RANGE);
+}
+
+
+static void
+_gth_grid_view_set_hadjustment (GthGridView *self,
+ GtkAdjustment *adjustment)
+{
+ if (adjustment != NULL)
+ g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment));
+ else
+ adjustment = gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
+
+ if ((self->priv->hadjustment != NULL) && (self->priv->hadjustment != adjustment)) {
+ g_signal_handlers_disconnect_by_data (self->priv->hadjustment, self);
+ g_object_unref (self->priv->hadjustment);
+ self->priv->hadjustment = NULL;
+ }
+
+ if (self->priv->hadjustment != adjustment) {
+ self->priv->hadjustment = adjustment;
+ g_object_ref (self->priv->hadjustment);
+ g_object_ref_sink (self->priv->hadjustment);
+
+ _gth_grid_view_configure_hadjustment (self);
+
+ g_signal_connect (self->priv->hadjustment,
+ "value-changed",
+ G_CALLBACK (adjustment_value_changed),
+ self);
+ }
+}
+
+
+static void
+_gth_grid_view_set_vadjustment (GthGridView *self,
+ GtkAdjustment *adjustment)
+{
+ if (adjustment != NULL)
+ g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment));
+ else
+ adjustment = gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
+
+ if ((self->priv->vadjustment != NULL) && (self->priv->vadjustment != adjustment)) {
+ g_signal_handlers_disconnect_by_data (self->priv->vadjustment, self);
+ g_object_unref (self->priv->vadjustment);
+ self->priv->vadjustment = NULL;
+ }
+
+ if (self->priv->vadjustment != adjustment) {
+ self->priv->vadjustment = adjustment;
+ g_object_ref (self->priv->vadjustment);
+ g_object_ref_sink (self->priv->vadjustment);
+
+ _gth_grid_view_configure_vadjustment (self);
+
+ g_signal_connect (self->priv->vadjustment,
+ "value-changed",
+ G_CALLBACK (adjustment_value_changed),
+ self);
+ }
+}
+
+
+static void
+_gth_grid_view_set_thumbnail_size (GthGridView *self,
+ int size)
+{
+ self->priv->thumbnail_size = size;
+ self->priv->cell_size = self->priv->thumbnail_size + (self->priv->thumbnail_border * 2) + (self->priv->cell_padding * 2);
+ self->priv->update_caption_height = TRUE;
+ g_object_notify (G_OBJECT (self), "thumbnail-size");
+
+ _gth_grid_view_queue_relayout (self);
+}
+
+
+static void
+_gth_grid_view_set_caption (GthGridView *self,
+ const char *attributes)
+{
+ GList *scan;
+
+ g_free (self->priv->caption_attributes);
+ self->priv->caption_attributes = g_strdup (attributes);
+
+ if (self->priv->caption_attributes_v != NULL) {
+ g_strfreev (self->priv->caption_attributes_v);
+ self->priv->caption_attributes_v = NULL;
+ }
+ if (self->priv->caption_attributes != NULL)
+ self->priv->caption_attributes_v = g_strsplit (self->priv->caption_attributes, ",", -1);
+
+ for (scan = self->priv->items; scan; scan = scan->next)
+ gth_grid_view_item_update_caption (GTH_GRID_VIEW_ITEM (scan->data), self->priv->caption_attributes_v);
+ self->priv->update_caption_height = TRUE;
+
+ g_object_notify (G_OBJECT (self), "caption");
+
+ _gth_grid_view_queue_relayout (self);
+}
+
+
+static void
+gth_grid_view_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GthGridView *self;
+
+ self = GTH_GRID_VIEW (object);
+
+ switch (prop_id) {
+ case PROP_CAPTION:
+ _gth_grid_view_set_caption (self, g_value_get_string (value));
+ break;
+ case PROP_HADJUSTMENT:
+ _gth_grid_view_set_hadjustment (self, g_value_get_object (value));
+ break;
+ case PROP_HSCROLL_POLICY:
+ /* FIXME */
+ break;
+ case PROP_MODEL:
+ gth_grid_view_set_model (GTH_FILE_VIEW (self), g_value_get_object (value));
+ break;
+ case PROP_THUMBNAIL_SIZE:
+ _gth_grid_view_set_thumbnail_size (self, g_value_get_int (value));
+ break;
+ case PROP_VADJUSTMENT:
+ _gth_grid_view_set_vadjustment (self, g_value_get_object (value));
+ break;
+ case PROP_VSCROLL_POLICY:
+ /* FIXME */
+ break;
+ default:
+ break;
+ }
+}
+
+
+static void
+gth_grid_view_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GthGridView *self;
+
+ self = GTH_GRID_VIEW (object);
+
+ switch (prop_id) {
+ case PROP_CAPTION:
+ g_value_set_string (value, self->priv->caption_attributes);
+ break;
+ case PROP_CELL_SPACING:
+ g_value_set_int (value, self->priv->cell_spacing);
+ break;
+ case PROP_HADJUSTMENT:
+ g_value_set_object (value, self->priv->hadjustment);
+ break;
+ case PROP_HSCROLL_POLICY:
+ /* FIXME */
+ break;
+ case PROP_MODEL:
+ g_value_set_object (value, self->priv->model);
+ break;
+ case PROP_THUMBNAIL_SIZE:
+ g_value_set_int (value, self->priv->thumbnail_size);
+ break;
+ case PROP_VADJUSTMENT:
+ g_value_set_object (value, self->priv->vadjustment);
+ break;
+ case PROP_VSCROLL_POLICY:
+ /* FIXME */
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+
+static void
+gth_grid_view_class_init (GthGridViewClass *grid_view_class)
+{
+ GObjectClass *gobject_class;
+ GtkWidgetClass *widget_class;
+ GtkBindingSet *binding_set;
+
+ g_type_class_add_private (grid_view_class, sizeof (GthGridViewPrivate));
+
+ /* Methods */
+
+ gobject_class = (GObjectClass*) grid_view_class;
+ gobject_class->finalize = gth_grid_view_finalize;
+ gobject_class->set_property = gth_grid_view_set_property;
+ gobject_class->get_property = gth_grid_view_get_property;
+
+ widget_class = (GtkWidgetClass*) grid_view_class;
+ widget_class->map = gth_grid_view_map;
+ widget_class->unmap = gth_grid_view_unmap;
+ widget_class->realize = gth_grid_view_realize;
+ widget_class->unrealize = gth_grid_view_unrealize;
+ widget_class->state_flags_changed = gth_grid_view_state_flags_changed;
+ widget_class->style_updated = gth_grid_view_style_updated;
+ widget_class->get_preferred_width = gth_grid_view_get_preferred_width;
+ widget_class->get_preferred_height = gth_grid_view_get_preferred_height;
+ widget_class->size_allocate = gth_grid_view_size_allocate;
+ widget_class->draw = gth_grid_view_draw;
+ widget_class->focus_in_event = gth_grid_view_focus_in;
+ widget_class->focus_out_event = gth_grid_view_focus_out;
+ widget_class->key_press_event = gth_grid_view_key_press;
+ widget_class->key_release_event = gth_grid_view_key_release;
+ widget_class->scroll_event = gth_grid_view_scroll_event;
+ widget_class->button_press_event = gth_grid_view_button_press;
+ widget_class->button_release_event = gth_grid_view_button_release;
+ widget_class->motion_notify_event = gth_grid_view_motion_notify;
+
+ grid_view_class->move_cursor = gth_grid_view_move_cursor;
+ grid_view_class->set_cursor_selection = gth_grid_view_set_cursor_selection;
+ grid_view_class->toggle_cursor_selection = gth_grid_view_toggle_cursor_selection;
+
+ /* Signals */
+
+ grid_view_signals[MOVE_CURSOR] =
+ g_signal_new ("move-cursor",
+ G_TYPE_FROM_CLASS (gobject_class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (GthGridViewClass, move_cursor),
+ NULL, NULL,
+ gth_marshal_BOOLEAN__ENUM_ENUM,
+ G_TYPE_BOOLEAN, 2,
+ GTH_TYPE_CURSOR_MOVEMENT,
+ GTH_TYPE_SELECTION_CHANGE);
+ grid_view_signals[SET_CURSOR_SELECTION] =
+ g_signal_new ("set-cursor-selection",
+ G_TYPE_FROM_CLASS (gobject_class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (GthGridViewClass, set_cursor_selection),
+ NULL, NULL,
+ gth_marshal_BOOLEAN__VOID,
+ G_TYPE_BOOLEAN, 0);
+ grid_view_signals[TOGGLE_CURSOR_SELECTION] =
+ g_signal_new ("toggle-cursor-selection",
+ G_TYPE_FROM_CLASS (gobject_class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (GthGridViewClass, toggle_cursor_selection),
+ NULL, NULL,
+ gth_marshal_BOOLEAN__VOID,
+ G_TYPE_BOOLEAN, 0);
+
+ /* Properties */
+
+ g_object_class_install_property (gobject_class,
+ PROP_CELL_SPACING,
+ g_param_spec_int ("cell-spacing",
+ "Cell Spacing",
+ "Spacing between cells both horizontally and vertically",
+ 0,
+ G_MAXINT32,
+ DEFAULT_CELL_SPACING,
+ G_PARAM_READWRITE));
+
+ /* GtkScrollable properties */
+
+ g_object_class_override_property (gobject_class, PROP_HADJUSTMENT, "hadjustment");
+ g_object_class_override_property (gobject_class, PROP_VADJUSTMENT, "vadjustment");
+ g_object_class_override_property (gobject_class, PROP_HSCROLL_POLICY, "hscroll-policy");
+ g_object_class_override_property (gobject_class, PROP_VSCROLL_POLICY, "vscroll-policy");
+
+ /* GthFileView properties */
+
+ g_object_class_override_property (gobject_class, PROP_CAPTION, "caption");
+ g_object_class_override_property (gobject_class, PROP_MODEL, "model");
+ g_object_class_override_property (gobject_class, PROP_THUMBNAIL_SIZE, "thumbnail-size");
+
+ /* Key bindings */
+
+ binding_set = gtk_binding_set_by_class (grid_view_class);
+
+ _gtk_binding_entry_add_move_cursor_signals (binding_set, GDK_KEY_Right, GTH_CURSOR_MOVE_RIGHT);
+ _gtk_binding_entry_add_move_cursor_signals (binding_set, GDK_KEY_Left, GTH_CURSOR_MOVE_LEFT);
+ _gtk_binding_entry_add_move_cursor_signals (binding_set, GDK_KEY_Down, GTH_CURSOR_MOVE_DOWN);
+ _gtk_binding_entry_add_move_cursor_signals (binding_set, GDK_KEY_Up, GTH_CURSOR_MOVE_UP);
+ _gtk_binding_entry_add_move_cursor_signals (binding_set, GDK_KEY_Page_Up, GTH_CURSOR_MOVE_PAGE_UP);
+ _gtk_binding_entry_add_move_cursor_signals (binding_set, GDK_KEY_Page_Down, GTH_CURSOR_MOVE_PAGE_DOWN);
+ _gtk_binding_entry_add_move_cursor_signals (binding_set, GDK_KEY_Home, GTH_CURSOR_MOVE_BEGIN);
+ _gtk_binding_entry_add_move_cursor_signals (binding_set, GDK_KEY_End, GTH_CURSOR_MOVE_END);
+
+ gtk_binding_entry_add_signal (binding_set, GDK_KEY_space, 0,
+ "set-cursor-selection", 0);
+ gtk_binding_entry_add_signal (binding_set, GDK_KEY_space, GDK_CONTROL_MASK,
+ "toggle-cursor-selection", 0);
+ gtk_binding_entry_add_signal (binding_set, GDK_KEY_a, GDK_CONTROL_MASK,
+ "select-all", 0);
+ gtk_binding_entry_add_signal (binding_set, GDK_KEY_slash, GDK_CONTROL_MASK,
+ "select-all", 0);
+ gtk_binding_entry_add_signal (binding_set, GDK_KEY_A, GDK_SHIFT_MASK | GDK_CONTROL_MASK,
+ "unselect-all", 0);
+ gtk_binding_entry_add_signal (binding_set, GDK_KEY_backslash, GDK_CONTROL_MASK,
+ "unselect-all", 0);
+}
+
+
+static void
+gth_grid_view_gth_file_selection_interface_init (GthFileSelectionInterface *iface)
+{
+ iface->set_selection_mode = gth_grid_view_set_selection_mode;
+ iface->get_selected = gth_grid_view_get_selected;
+ iface->select = gth_grid_view_select;
+ iface->unselect = gth_grid_view_unselect;
+ iface->select_all = gth_grid_view_select_all;
+ iface->unselect_all = gth_grid_view_unselect_all;
+ iface->is_selected = gth_grid_view_is_selected;
+ iface->get_first_selected = gth_grid_view_get_first_selected;
+ iface->get_last_selected = gth_grid_view_get_last_selected;
+ iface->get_n_selected = gth_grid_view_get_n_selected;
+}
+
+
+static void
+gth_grid_view_gth_file_view_interface_init (GthFileViewInterface *iface)
+{
+ iface->scroll_to = gth_grid_view_scroll_to;
+ iface->get_visibility = gth_grid_view_get_visibility;
+ iface->get_at_position = gth_grid_view_get_at_position;
+ iface->get_first_visible = gth_grid_view_get_first_visible;
+ iface->get_last_visible = gth_grid_view_get_last_visible;
+ iface->cursor_changed = gth_grid_view_cursor_changed;
+ iface->get_cursor = gth_grid_view_get_cursor;
+ iface->enable_drag_source = gth_grid_view_enable_drag_source;
+ iface->unset_drag_source = gth_grid_view_unset_drag_source;
+ iface->enable_drag_dest = gth_grid_view_enable_drag_dest;
+ iface->unset_drag_dest = gth_grid_view_unset_drag_dest;
+ iface->set_drag_dest_pos = gth_grid_view_set_drag_dest_pos;
+ iface->get_drag_dest_pos = gth_grid_view_get_drag_dest_pos;
+}
+
+
+static void
+gth_grid_view_init (GthGridView *self)
+{
+ gtk_widget_set_can_focus (GTK_WIDGET (self), TRUE);
+
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GTH_TYPE_GRID_VIEW, GthGridViewPrivate);
+
+ /* self->priv->model = NULL; */
+ self->priv->items = NULL;
+ self->priv->n_items = 0;
+ self->priv->lines = NULL;
+ self->priv->selection = NULL;
+ self->priv->focused_item = -1;
+ self->priv->first_focused_item = -1;
+ self->priv->dirty_layout = FALSE;
+ self->priv->frozen_layout = 0;
+ self->priv->layout_timeout = 0;
+ self->priv->relayout_from_line = 0;
+ self->priv->update_caption_height = TRUE;
+ self->priv->width = 0;
+ self->priv->height = 0;
+ /* self->priv->thumbnail_size = 0; */
+ self->priv->thumbnail_border = DEFAULT_THUMBNAIL_BORDER;
+
+ /* self->priv->cell_size = 0; */
+ self->priv->cell_spacing = DEFAULT_CELL_SPACING;
+ /* self->priv->cell_padding = DEFAULT_CELL_PADDING; */
+ self->priv->caption_spacing = DEFAULT_CAPTION_SPACING;
+ self->priv->caption_padding = DEFAULT_CAPTION_PADDING;
+
+ self->priv->scroll_timeout = 0;
+ self->priv->autoscroll_y_delta = 0;
+ self->priv->event_last_x = 0;
+ self->priv->event_last_y = 0;
+
+ self->priv->selecting = FALSE;
+ self->priv->select_pending = FALSE;
+ self->priv->select_pending_pos = -1;
+ self->priv->select_pending_item = NULL;
+ gth_grid_view_set_selection_mode (GTH_FILE_SELECTION (self), GTK_SELECTION_MULTIPLE);
+ /* self->priv->selection_area = 0; */
+ self->priv->last_selected_pos = -1;
+ self->priv->last_selected_item = NULL;
+ self->priv->multi_selecting_with_keyboard = FALSE;
+ self->priv->selection_changed = FALSE;
+ self->priv->sel_start_x = 0;
+ self->priv->sel_start_y = 0;
+ self->priv->sel_state = 0;
+
+ self->priv->dragging = FALSE;
+ self->priv->drag_started = FALSE;
+ self->priv->drag_source_enabled = FALSE;
+ self->priv->drag_start_button_mask = 0;
+ self->priv->drag_button = 0;
+ self->priv->drag_target_list = NULL;
+ self->priv->drag_actions = 0;
+ self->priv->drag_start_x = 0;
+ self->priv->drag_start_y = 0;
+ self->priv->drop_item = -1;
+ self->priv->drop_pos = GTH_DROP_POSITION_NONE;
+
+ self->priv->bin_window = NULL;
+
+ self->priv->caption_attributes = NULL;
+ self->priv->caption_attributes_v = NULL;
+ self->priv->caption_layout = NULL;
+
+ _gth_grid_view_set_hadjustment (self, gtk_adjustment_new (0.0, 1.0, 0.0, 0.1, 1.0, 1.0));
+ _gth_grid_view_set_vadjustment (self, gtk_adjustment_new (0.0, 1.0, 0.0, 0.1, 1.0, 1.0));
+}
+
+
+GtkWidget *
+gth_grid_view_new (void)
+{
+ return g_object_new (GTH_TYPE_GRID_VIEW, NULL);
+}
+
+
+void
+gth_grid_view_set_cell_spacing (GthGridView *self,
+ int cell_spacing)
+{
+ g_return_if_fail (GTH_IS_GRID_VIEW (self));
+
+ self->priv->cell_spacing = cell_spacing;
+ g_object_notify (G_OBJECT (self), "cell-spacing");
+
+ _gth_grid_view_queue_relayout (self);
+}
+
+
+int
+gth_grid_view_get_items_per_line (GthGridView *self)
+{
+ g_return_val_if_fail (GTH_IS_GRID_VIEW (self), 0);
+
+ return MAX (/* gtk_widget_get_allocated_width (GTK_WIDGET (self))*/ self->priv->width / (self->priv->cell_size + self->priv->cell_spacing), 1);
+}
diff --git a/gthumb/gth-grid-view.h b/gthumb/gth-grid-view.h
new file mode 100644
index 0000000..4cdd7db
--- /dev/null
+++ b/gthumb/gth-grid-view.h
@@ -0,0 +1,94 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * GThumb
+ *
+ * Copyright (C) 2003-2011 The Free Software Foundation, Inc.
+ *
+ * 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 GTH_GRID_VIEW_H
+#define GTH_GRID_VIEW_H
+
+#include <gtk/gtk.h>
+#include "typedefs.h"
+
+G_BEGIN_DECLS
+
+typedef enum {
+ GTH_CURSOR_MOVE_UP,
+ GTH_CURSOR_MOVE_DOWN,
+ GTH_CURSOR_MOVE_RIGHT,
+ GTH_CURSOR_MOVE_LEFT,
+ GTH_CURSOR_MOVE_PAGE_UP,
+ GTH_CURSOR_MOVE_PAGE_DOWN,
+ GTH_CURSOR_MOVE_BEGIN,
+ GTH_CURSOR_MOVE_END,
+} GthCursorMovement;
+
+typedef enum {
+ GTH_DROP_POSITION_NONE,
+ GTH_DROP_POSITION_INTO,
+ GTH_DROP_POSITION_LEFT,
+ GTH_DROP_POSITION_RIGHT
+} GthDropPosition;
+
+typedef enum {
+ GTH_SELECTION_KEEP, /* Do not change the selection. */
+ GTH_SELECTION_SET_CURSOR, /* Select the cursor image. */
+ GTH_SELECTION_SET_RANGE /* Select the images contained
+ * in the rectangle that has as
+ * opposite corners the last
+ * focused image and the
+ * currently focused image. */
+} GthSelectionChange;
+
+#define GTH_TYPE_GRID_VIEW (gth_grid_view_get_type ())
+#define GTH_GRID_VIEW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTH_TYPE_GRID_VIEW, GthGridView))
+#define GTH_GRID_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTH_TYPE_GRID_VIEW, GthGridViewClass))
+#define GTH_IS_GRID_VIEW(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTH_TYPE_GRID_VIEW))
+#define GTH_IS_GRID_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTH_TYPE_GRID_VIEW))
+#define GTH_GRID_VIEW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GTH_TYPE_GRID_VIEW, GthGridViewClass))
+
+typedef struct _GthGridView GthGridView;
+typedef struct _GthGridViewClass GthGridViewClass;
+typedef struct _GthGridViewPrivate GthGridViewPrivate;
+
+struct _GthGridView {
+ GtkWidget __parent;
+ GthGridViewPrivate *priv;
+};
+
+struct _GthGridViewClass {
+ GtkWidgetClass __parent_class;
+
+ /*< key binding signals >*/
+
+ gboolean (* move_cursor) (GthGridView *grid_view,
+ GthCursorMovement dir,
+ GthSelectionChange sel_change);
+ gboolean (* set_cursor_selection) (GthGridView *grid_view);
+ gboolean (* toggle_cursor_selection) (GthGridView *grid_view);
+};
+
+GType gth_grid_view_get_type (void);
+GtkWidget * gth_grid_view_new (void);
+void gth_grid_view_set_cell_spacing (GthGridView *grid_view,
+ int cell_spacing);
+int gth_grid_view_get_items_per_line (GthGridView *self);
+
+G_END_DECLS
+
+#endif /* GTH_GRID_VIEW_H */
diff --git a/gthumb/gth-marshal.list b/gthumb/gth-marshal.list
index e419c1a..3f16292 100644
--- a/gthumb/gth-marshal.list
+++ b/gthumb/gth-marshal.list
@@ -1,4 +1,7 @@
+BOOLEAN:ENUM, ENUM
+BOOLEAN:VOID
VOID:BOOLEAN, POINTER
+VOID:BOXED, BOXED
VOID:ENUM, ENUM
VOID:INT, INT
VOID:OBJECT, BOOLEAN
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]