/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
* Pix
*
* Copyright (C) 2009 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 .
*/
#include
#include
#include
#include
#include "actions.h"
#include "gth-image-viewer-page.h"
#include "preferences.h"
#include "shortcuts.h"
#define UPDATE_QUALITY_DELAY 200
#define UPDATE_VISIBILITY_DELAY 100
#define N_HEADER_BAR_BUTTONS 7
#define HIDE_OVERVIEW_TIMEOUT 2 /* in seconds */
#define OVERLAY_MARGIN 10
#define ZOOM_BUTTON 2
#define APPLY_ICC_PROFILE_BUTTON 3
#define TOGGLE_ANIMATION_BUTTON 4
#define STEP_ANIMATION_BUTTON 5
#define TRANSPARENCY_STYLE_BUTTON 6
#undef ALWAYS_LOAD_ORIGINAL_SIZE
#define N_FORWARD_PRELOADERS 2
#define N_BACKWARD_PRELOADERS 2
static void gth_viewer_page_interface_init (GthViewerPageInterface *iface);
static const GActionEntry actions[] = {
{ "image-zoom-in", gth_browser_activate_image_zoom_in },
{ "image-zoom-out", gth_browser_activate_image_zoom_out },
{ "image-zoom-100", gth_browser_activate_image_zoom_100 },
{ "image-zoom-200", gth_browser_activate_image_zoom_200 },
{ "image-zoom-300", gth_browser_activate_image_zoom_300 },
{ "image-zoom-fit", gth_browser_activate_image_zoom_fit },
{ "image-zoom-fit-if-larger", gth_browser_activate_image_zoom_fit_if_larger },
{ "image-zoom-fit-width", gth_browser_activate_image_zoom_fit_width },
{ "image-zoom-fit-width-if-larger", gth_browser_activate_image_zoom_fit_width_if_larger },
{ "image-zoom-fit-height", gth_browser_activate_image_zoom_fit_height },
{ "image-zoom-fit-height-if-larger", gth_browser_activate_image_zoom_fit_height_if_larger },
{ "image-undo", gth_browser_activate_image_undo },
{ "image-redo", gth_browser_activate_image_redo },
{ "copy-image", gth_browser_activate_copy_image },
{ "paste-image", gth_browser_activate_paste_image },
{ "apply-icc-profile", toggle_action_activated, NULL, "true", gth_browser_activate_apply_icc_profile },
{ "toggle-animation", toggle_action_activated, NULL, "true", gth_browser_activate_toggle_animation },
{ "step-animation", gth_browser_activate_step_animation },
{ "image-zoom", gth_browser_activate_image_zoom, "s", "''", NULL },
{ "transparency-style", gth_browser_activate_transparency_style, "s", "''", NULL },
{ "scroll-step-left", gth_browser_activate_scroll_step_left },
{ "scroll-step-right", gth_browser_activate_scroll_step_right },
{ "scroll-step-up", gth_browser_activate_scroll_step_up },
{ "scroll-step-down", gth_browser_activate_scroll_step_down },
{ "scroll-page-left", gth_browser_activate_scroll_page_left },
{ "scroll-page-right", gth_browser_activate_scroll_page_right },
{ "scroll-page-up", gth_browser_activate_scroll_page_up },
{ "scroll-page-down", gth_browser_activate_scroll_page_down },
{ "scroll-to-center", gth_browser_activate_scroll_to_center },
};
static const GthMenuEntry file_popup_entries[] = {
{ N_("Copy Image"), "win.copy-image" },
{ N_("Paste Image"), "win.paste-image" },
};
struct _GthImageViewerPagePrivate {
GthBrowser *browser;
GSettings *settings;
GtkWidget *image_navigator;
GtkWidget *overview_revealer;
GtkWidget *overview;
GtkWidget *viewer;
GthImagePreloader *preloader;
guint file_popup_merge_id;
GthImageHistory *history;
GthFileData *file_data;
GFileInfo *updated_info;
gboolean active;
gboolean image_changed;
gboolean loading_image;
GFile *last_loaded;
gboolean can_paste;
guint update_quality_id;
guint update_visibility_id;
GtkWidget *buttons[N_HEADER_BAR_BUTTONS];
GtkBuilder *builder;
gboolean pointer_on_viewer;
gboolean pointer_on_overview;
guint hide_overview_id;
gboolean apply_icc_profile;
GthFileData *next_file_data[N_FORWARD_PRELOADERS];
GthFileData *prev_file_data[N_BACKWARD_PRELOADERS];
gulong drag_data_get_event;
};
G_DEFINE_TYPE_WITH_CODE (GthImageViewerPage,
gth_image_viewer_page,
G_TYPE_OBJECT,
G_ADD_PRIVATE (GthImageViewerPage)
G_IMPLEMENT_INTERFACE (GTH_TYPE_VIEWER_PAGE,
gth_viewer_page_interface_init))
static void
gth_image_viewer_page_file_loaded (GthImageViewerPage *self,
gboolean success)
{
if (_g_file_equal (self->priv->last_loaded, self->priv->file_data->file))
return;
_g_object_unref (self->priv->last_loaded);
self->priv->last_loaded = g_object_ref (self->priv->file_data->file);
gth_viewer_page_file_loaded (GTH_VIEWER_PAGE (self),
self->priv->file_data,
self->priv->updated_info,
success);
}
static int
get_viewer_size (GthImageViewerPage *self)
{
GtkAllocation allocation;
int size;
gtk_widget_get_allocation (GTK_WIDGET (self->priv->viewer), &allocation);
size = MAX (allocation.width, allocation.height);
if (size <= 1) {
int window_width;
int window_height;
gtk_window_get_size (GTK_WINDOW (self->priv->browser),
&window_width,
&window_height);
size = MAX (window_width, window_height);;
}
return size;
}
static int
_gth_image_preloader_get_requested_size_for_next_images (GthImageViewerPage *self)
{
int requested_size;
requested_size = -1;
switch (gth_image_viewer_get_zoom_change (GTH_IMAGE_VIEWER (self->priv->viewer))) {
case GTH_ZOOM_CHANGE_ACTUAL_SIZE:
requested_size = -1;
break;
default:
requested_size = (int) floor ((double) get_viewer_size (self) * 0.5);
break;
}
return requested_size * gtk_widget_get_scale_factor (GTK_WIDGET (self->priv->viewer));
}
static int
_gth_image_preloader_get_requested_size_for_current_image (GthImageViewerPage *self)
{
int requested_size;
double zoom;
requested_size = -1;
switch (gth_image_viewer_get_fit_mode (GTH_IMAGE_VIEWER (self->priv->viewer))) {
case GTH_FIT_NONE:
zoom = gth_image_viewer_get_zoom (GTH_IMAGE_VIEWER (self->priv->viewer));
if (zoom < 1.0) {
int original_width;
int original_height;
gth_image_viewer_get_original_size (GTH_IMAGE_VIEWER (self->priv->viewer), &original_width, &original_height);
requested_size = MAX (original_width * zoom, original_height * zoom);
}
else
requested_size = -1;
break;
default:
requested_size = get_viewer_size (self);
break;
}
return requested_size * gtk_widget_get_scale_factor (GTK_WIDGET (self->priv->viewer));
}
/* -- _gth_image_viewer_page_load_with_preloader -- */
typedef struct {
GthImageViewerPage *self;
GthFileData *file_data;
int requested_size;
GCancellable *cancellable;
GAsyncReadyCallback callback;
gpointer user_data;
} ProfileData;
static void
profile_data_free (ProfileData *profile_data)
{
_g_object_unref (profile_data->cancellable);
_g_object_unref (profile_data->file_data);
_g_object_unref (profile_data->self);
g_free (profile_data);
}
static void
_gth_image_viewer_page_load_with_preloader_step2 (GthImageViewerPage *self,
GthFileData *file_data,
int requested_size,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_object_ref (self);
gth_image_preloader_load (self->priv->preloader,
file_data,
requested_size,
cancellable,
callback,
user_data,
N_FORWARD_PRELOADERS + N_BACKWARD_PRELOADERS,
self->priv->next_file_data[0],
self->priv->next_file_data[1],
self->priv->prev_file_data[0],
self->priv->prev_file_data[1]);
}
static void
profile_ready_cb (GObject *source_object,
GAsyncResult *res,
gpointer user_data)
{
ProfileData *profile_data = user_data;
GthImageViewerPage *self = profile_data->self;
if (self->priv->active && ! self->priv->image_changed && _g_file_equal (self->priv->file_data->file, profile_data->file_data->file)) {
GthICCProfile *profile;
profile = gth_color_manager_get_profile_finish (GTH_COLOR_MANAGER (source_object), res, NULL);
if (profile == NULL)
profile = _g_object_ref (gth_browser_get_monitor_profile (self->priv->browser));
gth_image_preloader_set_out_profile (self->priv->preloader, profile);
_gth_image_viewer_page_load_with_preloader_step2 (profile_data->self,
profile_data->file_data,
profile_data->requested_size,
profile_data->cancellable,
profile_data->callback,
profile_data->user_data);
_g_object_unref (profile);
}
profile_data_free (profile_data);
}
static void
_gth_image_viewer_page_load_with_preloader (GthImageViewerPage *self,
GthFileData *file_data,
int requested_size,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
if ((file_data != NULL) && self->priv->apply_icc_profile) {
char *monitor_name = NULL;
if (_gtk_window_get_monitor_info (GTK_WINDOW (self->priv->browser), NULL, NULL, &monitor_name)) {
ProfileData *profile_data;
profile_data = g_new (ProfileData, 1);
profile_data->self = g_object_ref (self);
profile_data->file_data = g_object_ref (file_data);
profile_data->requested_size = requested_size;
profile_data->cancellable = _g_object_ref (cancellable);
profile_data->callback = callback;
profile_data->user_data = user_data;
gth_color_manager_get_profile_async (gth_main_get_default_color_manager(),
monitor_name,
cancellable,
profile_ready_cb,
profile_data);
return;
}
}
gth_image_preloader_set_out_profile (self->priv->preloader, NULL);
_gth_image_viewer_page_load_with_preloader_step2 (self, file_data, requested_size, cancellable, callback, user_data);
}
static gboolean
_gth_image_viewer_page_load_with_preloader_finish (GthImageViewerPage *self)
{
gboolean active = self->priv->active;
g_object_unref (self);
return active;
}
static void
different_quality_ready_cb (GObject *source_object,
GAsyncResult *result,
gpointer user_data)
{
GthImageViewerPage *self = user_data;
GthFileData *requested;
GthImage *image;
int requested_size;
int original_width;
int original_height;
GError *error = NULL;
cairo_surface_t *s1 = NULL;
cairo_surface_t *s2;
int w1, h1, w2, h2;
gboolean got_better_quality;
if (! _gth_image_viewer_page_load_with_preloader_finish (self))
return;
if (! gth_image_preloader_load_finish (GTH_IMAGE_PRELOADER (source_object),
result,
&requested,
&image,
&requested_size,
&original_width,
&original_height,
&error))
{
g_clear_error (&error);
return;
}
if (! (self->priv->image_changed && requested == NULL) && ! _g_file_equal (requested->file, self->priv->file_data->file))
goto clear_data;
if (image == NULL)
goto clear_data;
/* check whether the image is of different quality */
s1 = gth_image_get_cairo_surface (image);
if (s1 == NULL)
goto clear_data;
s2 = gth_image_viewer_get_current_image (GTH_IMAGE_VIEWER (self->priv->viewer));
if (s2 == NULL) {
got_better_quality = TRUE;
}
else {
w1 = cairo_image_surface_get_width (s1);
h1 = cairo_image_surface_get_height (s1);
w2 = cairo_image_surface_get_width (s2);
h2 = cairo_image_surface_get_height (s2);
got_better_quality = ((w1 > w2) || (h1 > h2));
}
if (got_better_quality) {
gth_viewer_page_focus (GTH_VIEWER_PAGE (self));
gth_image_viewer_set_better_quality (GTH_IMAGE_VIEWER (self->priv->viewer),
image,
original_width,
original_height);
gth_image_viewer_set_requested_size (GTH_IMAGE_VIEWER (self->priv->viewer), requested_size);
gtk_widget_queue_draw (self->priv->viewer);
}
clear_data:
if (s1 != NULL)
cairo_surface_destroy (s1);
_g_object_unref (requested);
_g_object_unref (image);
g_clear_error (&error);
return;
}
static gboolean
_g_mime_type_can_load_different_quality (const char *mime_type)
{
static const char *supported[] = {
"image/jpeg",
"image/x-portable-pixmap"
};
int i;
for (i = 0; i < G_N_ELEMENTS (supported); i++)
if (g_strcmp0 (mime_type, supported[i]) == 0)
return TRUE;
if (_g_mime_type_is_raw (mime_type))
return TRUE;
return FALSE;
}
typedef struct {
GthImageViewerPage *self;
GthFileData *file_data;
} UpdateQualityData;
static void
update_quality_data_free (UpdateQualityData *data)
{
_g_object_unref (data->file_data);
g_free (data);
}
static gboolean
update_quality_cb (gpointer user_data)
{
UpdateQualityData *data = user_data;
GthImageViewerPage *self = data->self;
gboolean file_changed;
if (! _gth_image_viewer_page_load_with_preloader_finish (self)) {
update_quality_data_free (data);
return FALSE;
}
if (self->priv->update_quality_id != 0) {
g_source_remove (self->priv->update_quality_id);
self->priv->update_quality_id = 0;
}
file_changed = ! _g_file_equal (data->file_data->file, self->priv->file_data->file);
update_quality_data_free (data);
if (file_changed)
return FALSE;
if (! self->priv->active)
return FALSE;
if (self->priv->viewer == NULL)
return FALSE;
if (self->priv->loading_image)
return FALSE;
if (! self->priv->image_changed && ! _g_mime_type_can_load_different_quality (gth_file_data_get_mime_type (self->priv->file_data)))
return FALSE;
_gth_image_viewer_page_load_with_preloader (self,
self->priv->image_changed ? GTH_MODIFIED_IMAGE : self->priv->file_data,
_gth_image_preloader_get_requested_size_for_current_image (self),
NULL,
different_quality_ready_cb,
self);
return FALSE;
}
static void
update_image_quality_if_required (GthImageViewerPage *self)
{
#ifndef ALWAYS_LOAD_ORIGINAL_SIZE
GthImage *image;
if (self->priv->loading_image || gth_sidebar_tool_is_active (GTH_SIDEBAR (gth_browser_get_viewer_sidebar (self->priv->browser))))
return;
image = gth_image_viewer_get_image (GTH_IMAGE_VIEWER (self->priv->viewer));
if ((image != NULL) && (gth_image_get_is_zoomable (image) || gth_image_get_is_animation (image)))
return;
if (self->priv->update_quality_id != 0) {
g_source_remove (self->priv->update_quality_id);
self->priv->update_quality_id = 0;
}
UpdateQualityData *data;
data = g_new0 (UpdateQualityData, 1);
data->self = self;
data->file_data = _g_object_ref (self->priv->file_data);
_g_object_ref (self);
self->priv->update_quality_id = g_timeout_add (UPDATE_QUALITY_DELAY,
update_quality_cb,
data);
#endif
}
static gboolean
hide_overview_after_timeout (gpointer data)
{
GthImageViewerPage *self = data;
if (self->priv->hide_overview_id != 0)
g_source_remove (self->priv->hide_overview_id);
self->priv->hide_overview_id = 0;
if (! self->priv->pointer_on_overview)
gtk_revealer_set_reveal_child (GTK_REVEALER (self->priv->overview_revealer), FALSE);
return FALSE;
}
static gboolean
update_overview_visibility_now (gpointer user_data)
{
GthImageViewerPage *self;
gboolean overview_visible;
self = GTH_IMAGE_VIEWER_PAGE (user_data);
if (self->priv->update_visibility_id != 0) {
g_source_remove (self->priv->update_visibility_id);
self->priv->update_visibility_id = 0;
}
if (! self->priv->active)
return FALSE;
overview_visible = self->priv->pointer_on_overview || (self->priv->pointer_on_viewer && gth_image_viewer_has_scrollbars (GTH_IMAGE_VIEWER (self->priv->viewer)));
gtk_revealer_set_reveal_child (GTK_REVEALER (self->priv->overview_revealer), overview_visible);
if (overview_visible) {
if (self->priv->hide_overview_id != 0)
g_source_remove (self->priv->hide_overview_id);
self->priv->hide_overview_id = g_timeout_add_seconds (HIDE_OVERVIEW_TIMEOUT, hide_overview_after_timeout, self);
}
return FALSE;
}
static void
update_overview_visibility (GthImageViewerPage *self)
{
if (self->priv->update_visibility_id != 0) {
g_source_remove (self->priv->update_visibility_id);
self->priv->update_visibility_id = 0;
}
self->priv->update_visibility_id = g_timeout_add (UPDATE_VISIBILITY_DELAY,
update_overview_visibility_now,
self);
}
#define MIN_ZOOM_LEVEL 0.3
#define MAX_ZOOM_LEVEL 3.0
static void
zoom_scale_value_changed_cb (GtkScale *scale,
gpointer user_data)
{
GthImageViewerPage *self = user_data;
double x, zoom;
x = gtk_range_get_value (GTK_RANGE (scale));
zoom = MIN_ZOOM_LEVEL + (x / 100.0 * (MAX_ZOOM_LEVEL - MIN_ZOOM_LEVEL));
gth_image_viewer_set_zoom (GTH_IMAGE_VIEWER (self->priv->viewer), zoom);
}
static void
zoom_button_toggled_cb (GtkToggleButton *togglebutton,
gpointer user_data)
{
GthImageViewerPage *self = user_data;
if (! gtk_toggle_button_get_active (togglebutton))
return;
gth_browser_keep_mouse_visible (self->priv->browser, TRUE);
}
static void
zoom_popover_closed_cb (GtkPopover *popover,
gpointer user_data)
{
GthImageViewerPage *self = user_data;
gth_browser_keep_mouse_visible (self->priv->browser, FALSE);
call_when_idle ((DataFunc) gth_viewer_page_focus, self);
}
#define ZOOM_EQUAL(a,b) (fabs (a - b) < 1e-3)
static void
update_zoom_info (GthImageViewerPage *self)
{
double zoom;
char *text;
double x;
/* status bar */
zoom = gth_image_viewer_get_zoom (GTH_IMAGE_VIEWER (self->priv->viewer));
text = g_strdup_printf (" %d%% ", (int) (zoom * 100));
gth_statusbar_set_secondary_text (GTH_STATUSBAR (gth_browser_get_statusbar (self->priv->browser)), text);
g_free (text);
/* zoom menu */
gboolean zoom_enabled;
GthFit fit_mode;
GAction *action;
const char *state;
zoom_enabled = gth_image_viewer_get_zoom_enabled (GTH_IMAGE_VIEWER (self->priv->viewer));
fit_mode = gth_image_viewer_get_fit_mode (GTH_IMAGE_VIEWER (self->priv->viewer));
gth_window_enable_action (GTH_WINDOW (self->priv->browser), "image-zoom", zoom_enabled);
state = "";
if (fit_mode == GTH_FIT_SIZE)
state = "fit";
else if (fit_mode == GTH_FIT_WIDTH)
state = "fit-width";
else if (fit_mode == GTH_FIT_HEIGHT)
state = "fit-height";
else if (fit_mode == GTH_FIT_SIZE_IF_LARGER)
state = "automatic";
else if (ZOOM_EQUAL (zoom, 0.5))
state = "50";
else if (ZOOM_EQUAL (zoom, 1.0))
state = "100";
else if (ZOOM_EQUAL (zoom, 2.0))
state = "200";
else if (ZOOM_EQUAL (zoom, 3.0))
state = "300";
action = g_action_map_lookup_action (G_ACTION_MAP (self->priv->browser), "image-zoom");
g_simple_action_set_state (G_SIMPLE_ACTION (action), g_variant_new_string (state));
gth_window_enable_action (GTH_WINDOW (self->priv->browser), "image-zoom-100", ! ZOOM_EQUAL (zoom, 1.0));
gth_window_enable_action (GTH_WINDOW (self->priv->browser), "image-zoom-fit-if-larger", fit_mode != GTH_FIT_SIZE_IF_LARGER);
/* zoom menu scale */
GtkWidget *scale = _gtk_builder_get_widget (self->priv->builder, "zoom_level_scale");
_g_signal_handlers_block_by_data (scale, self);
x = (zoom - MIN_ZOOM_LEVEL) / (MAX_ZOOM_LEVEL - MIN_ZOOM_LEVEL) * 100.0;
gtk_range_set_value (GTK_RANGE (scale), CLAMP (x, 0, 100));
_g_signal_handlers_unblock_by_data (scale, self);
}
static void
viewer_zoom_changed_cb (GtkWidget *widget,
GthImageViewerPage *self)
{
update_image_quality_if_required (self);
self->priv->pointer_on_viewer = TRUE;
update_overview_visibility (self);
update_zoom_info (self);
}
static void
viewer_image_changed_cb (GtkWidget *widget,
GthImageViewerPage *self)
{
gth_viewer_page_update_sensitivity (GTH_VIEWER_PAGE (self));
update_image_quality_if_required (self);
update_overview_visibility (self);
update_zoom_info (self);
}
static gboolean
viewer_button_press_event_cb (GtkWidget *widget,
GdkEventButton *event,
GthImageViewerPage *self)
{
return gth_browser_viewer_button_press_cb (self->priv->browser, event);
}
static gboolean
viewer_popup_menu_cb (GtkWidget *widget,
GthImageViewerPage *self)
{
gth_browser_file_menu_popup (self->priv->browser, NULL);
return TRUE;
}
static gboolean
viewer_scroll_event_cb (GtkWidget *widget,
GdkEventScroll *event,
GthImageViewerPage *self)
{
return gth_browser_viewer_scroll_event_cb (self->priv->browser, event);
}
static gboolean
viewer_image_map_event_cb (GtkWidget *widget,
GdkEvent *event,
GthImageViewerPage *self)
{
gth_viewer_page_focus (GTH_VIEWER_PAGE (self));
return FALSE;
}
static void
clipboard_targets_received_cb (GtkClipboard *clipboard,
GdkAtom *atoms,
int n_atoms,
gpointer user_data)
{
GthImageViewerPage *self = user_data;
int i;
self->priv->can_paste = FALSE;
for (i = 0; ! self->priv->can_paste && (i < n_atoms); i++)
if (atoms[i] == gdk_atom_intern_static_string ("image/png"))
self->priv->can_paste = TRUE;
gth_window_enable_action (GTH_WINDOW (self->priv->browser), "paste-image", self->priv->can_paste);
g_object_unref (self);
}
static void
_gth_image_viewer_page_update_paste_command_sensitivity (GthImageViewerPage *self,
GtkClipboard *clipboard)
{
self->priv->can_paste = FALSE;
gth_window_enable_action (GTH_WINDOW (self->priv->browser), "paste-image", self->priv->can_paste);
if (clipboard == NULL)
clipboard = gtk_widget_get_clipboard (GTK_WIDGET (self->priv->viewer), GDK_SELECTION_CLIPBOARD);
gtk_clipboard_request_targets (clipboard,
clipboard_targets_received_cb,
g_object_ref (self));
}
static void
clipboard_owner_change_cb (GtkClipboard *clipboard,
GdkEvent *event,
gpointer user_data)
{
_gth_image_viewer_page_update_paste_command_sensitivity ((GthImageViewerPage *) user_data, clipboard);
}
static void
viewer_realize_cb (GtkWidget *widget,
gpointer user_data)
{
GthImageViewerPage *self = user_data;
GtkClipboard *clipboard;
clipboard = gtk_widget_get_clipboard (widget, GDK_SELECTION_CLIPBOARD);
g_signal_connect (clipboard,
"owner_change",
G_CALLBACK (clipboard_owner_change_cb),
self);
}
static void
viewer_unrealize_cb (GtkWidget *widget,
gpointer user_data)
{
GthImageViewerPage *self = user_data;
GtkClipboard *clipboard;
clipboard = gtk_widget_get_clipboard (widget, GDK_SELECTION_CLIPBOARD);
g_signal_handlers_disconnect_by_func (clipboard,
G_CALLBACK (clipboard_owner_change_cb),
self);
}
static gboolean
image_navigator_get_child_position_cb (GtkOverlay *overlay,
GtkWidget *widget,
GdkRectangle *allocation,
gpointer user_data)
{
GthImageViewerPage *self = GTH_IMAGE_VIEWER_PAGE (user_data);
GtkAllocation main_alloc;
gboolean allocation_filled = FALSE;
gtk_widget_get_allocation (gtk_bin_get_child (GTK_BIN (overlay)), &main_alloc);
gtk_widget_get_preferred_width (widget, NULL, &allocation->width);
gtk_widget_get_preferred_height (widget, NULL, &allocation->height);
if (widget == self->priv->overview_revealer) {
allocation->x = main_alloc.width - allocation->width - OVERLAY_MARGIN;
allocation->y = OVERLAY_MARGIN;
if (gth_browser_get_is_fullscreen (self->priv->browser))
allocation->y += gtk_widget_get_allocated_height (gth_browser_get_fullscreen_headerbar (self->priv->browser));
allocation_filled = TRUE;
}
return allocation_filled;
}
static gboolean
overview_motion_notify_event_cb (GtkWidget *widget,
GdkEventMotion *event,
gpointer data)
{
GthImageViewerPage *self = data;
if (self->priv->hide_overview_id != 0) {
g_source_remove (self->priv->hide_overview_id);
self->priv->hide_overview_id = 0;
}
self->priv->pointer_on_viewer = TRUE;
if (widget == self->priv->overview)
self->priv->pointer_on_overview = TRUE;
update_overview_visibility (data);
return FALSE;
}
static gboolean
overview_leave_notify_event_cb (GtkWidget *widget,
GdkEvent *event,
gpointer data)
{
GthImageViewerPage *self = data;
if (widget == self->priv->overview)
self->priv->pointer_on_overview = gth_image_overview_get_scrolling_is_active (GTH_IMAGE_OVERVIEW (self->priv->overview));
return FALSE;
}
static void
pref_zoom_quality_changed (GSettings *settings,
char *key,
gpointer user_data)
{
GthImageViewerPage *self = user_data;
if (! self->priv->active || (self->priv->viewer == NULL))
return;
gth_image_viewer_set_zoom_quality (GTH_IMAGE_VIEWER (self->priv->viewer),
g_settings_get_enum (self->priv->settings, PREF_IMAGE_VIEWER_ZOOM_QUALITY));
gtk_widget_queue_draw (self->priv->viewer);
}
static void
pref_zoom_change_changed (GSettings *settings,
char *key,
gpointer user_data)
{
GthImageViewerPage *self = user_data;
if (! self->priv->active || (self->priv->viewer == NULL))
return;
gth_image_viewer_set_zoom_change (GTH_IMAGE_VIEWER (self->priv->viewer),
g_settings_get_enum (self->priv->settings, PREF_IMAGE_VIEWER_ZOOM_CHANGE));
gtk_widget_queue_draw (self->priv->viewer);
}
static void
pref_reset_scrollbars_changed (GSettings *settings,
char *key,
gpointer user_data)
{
GthImageViewerPage *self = user_data;
if (! self->priv->active || (self->priv->viewer == NULL))
return;
gth_image_viewer_set_reset_scrollbars (GTH_IMAGE_VIEWER (self->priv->viewer),
g_settings_get_boolean (self->priv->settings, PREF_IMAGE_VIEWER_RESET_SCROLLBARS));
}
static void
pref_transparency_style_changed (GSettings *settings,
char *key,
gpointer user_data)
{
GthImageViewerPage *self = user_data;
GthTransparencyStyle style;
GAction *action;
const char *state;
if (! self->priv->active || (self->priv->viewer == NULL))
return;
style = g_settings_get_enum (self->priv->settings, PREF_IMAGE_VIEWER_TRANSPARENCY_STYLE);
state = "";
switch (style) {
case GTH_TRANSPARENCY_STYLE_CHECKERED:
state = "checkered";
break;
case GTH_TRANSPARENCY_STYLE_WHITE:
state = "white";
break;
case GTH_TRANSPARENCY_STYLE_GRAY:
state = "gray";
break;
case GTH_TRANSPARENCY_STYLE_BLACK:
state = "black";
break;
}
action = g_action_map_lookup_action (G_ACTION_MAP (self->priv->browser), "transparency-style");
if (action != NULL)
g_simple_action_set_state (G_SIMPLE_ACTION (action), g_variant_new_string (state));
gth_image_viewer_set_transparency_style (GTH_IMAGE_VIEWER (self->priv->viewer), style);
}
static void
paint_comment_over_image_func (GthImageViewer *image_viewer,
cairo_t *cr,
gpointer user_data)
{
GthImageViewerPage *self = user_data;
GthFileData *file_data = self->priv->file_data;
GString *file_info;
char *comment;
const char *file_date;
const char *file_size;
int current_position;
int n_visibles;
int width;
int height;
GthMetadata *metadata;
PangoLayout *layout;
PangoAttrList *attr_list = NULL;
GError *error = NULL;
char *text;
static GdkPixbuf *icon = NULL;
int icon_width;
int icon_height;
int image_width;
int image_height;
const int x_padding = 20;
const int y_padding = 20;
int max_text_width;
PangoRectangle bounds;
int text_x;
int text_y;
int icon_x;
int icon_y;
file_info = g_string_new ("");
comment = gth_file_data_get_attribute_as_string (file_data, "general::description");
if (comment != NULL) {
g_string_append_printf (file_info, "%s\n\n", comment);
g_free (comment);
}
metadata = (GthMetadata *) g_file_info_get_attribute_object (file_data->info, "general::datetime");
if (metadata != NULL)
file_date = gth_metadata_get_formatted (metadata);
else
file_date = g_file_info_get_attribute_string (file_data->info, "gth::file::display-mtime");
file_size = g_file_info_get_attribute_string (file_data->info, "gth::file::display-size");
gth_browser_get_file_list_info (self->priv->browser, ¤t_position, &n_visibles);
gth_image_viewer_get_original_size (GTH_IMAGE_VIEWER (self->priv->viewer), &width, &height);
g_string_append_printf (file_info,
"%s - %dx%d (%d%%) - %s\n%d/%d - %s",
file_date,
width,
height,
(int) (gth_image_viewer_get_zoom (GTH_IMAGE_VIEWER (self->priv->viewer)) * 100),
file_size,
current_position + 1,
n_visibles,
g_file_info_get_attribute_string (file_data->info, "standard::display-name"));
layout = gtk_widget_create_pango_layout (GTK_WIDGET (self->priv->viewer), NULL);
pango_layout_set_wrap (layout, PANGO_WRAP_WORD);
pango_layout_set_alignment (layout, PANGO_ALIGN_LEFT);
if (! pango_parse_markup (file_info->str,
-1,
0,
&attr_list,
&text,
NULL,
&error))
{
g_warning ("Failed to set text from markup due to error parsing markup: %s\nThis is the text that caused the error: %s", error->message, file_info->str);
g_error_free (error);
g_object_unref (layout);
g_string_free (file_info, TRUE);
return;
}
pango_layout_set_attributes (layout, attr_list);
pango_layout_set_text (layout, text, strlen (text));
if (icon == NULL) {
GIcon *gicon;
gicon = g_themed_icon_new ("dialog-information-symbolic");
icon = _g_icon_get_pixbuf (gicon, 24, _gtk_widget_get_icon_theme (GTK_WIDGET (image_viewer)));
g_object_unref (gicon);
}
icon_width = gdk_pixbuf_get_width (icon);
icon_height = gdk_pixbuf_get_height (icon);
image_width = gdk_window_get_width (gtk_widget_get_window (self->priv->viewer));
image_height = gdk_window_get_height (gtk_widget_get_window (self->priv->viewer));
max_text_width = ((image_width * 3 / 4) - icon_width - (x_padding * 3) - (x_padding * 2));
pango_layout_set_width (layout, max_text_width * PANGO_SCALE);
pango_layout_get_pixel_extents (layout, NULL, &bounds);
bounds.width += (2 * x_padding) + (icon_width + x_padding);
bounds.height = MIN (image_height - icon_height - (y_padding * 2), bounds.height + (2 * y_padding));
bounds.x = MAX ((image_width - bounds.width) / 2, 0);
bounds.y = MAX (image_height - bounds.height - (y_padding * 3), 0);
text_x = bounds.x + x_padding + icon_width + x_padding;
text_y = bounds.y + y_padding;
icon_x = bounds.x + x_padding;
icon_y = bounds.y + (bounds.height - icon_height) / 2;
cairo_save (cr);
/* background */
_cairo_draw_rounded_box (cr, bounds.x, bounds.y, bounds.width, bounds.height, 8.0);
cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, 0.80);
cairo_fill (cr);
cairo_set_line_width (cr, 1.0);
cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
cairo_stroke (cr);
/* icon */
gdk_cairo_set_source_pixbuf (cr, icon, icon_x, icon_y);
cairo_rectangle (cr, icon_x, icon_y, icon_width, icon_height);
cairo_fill (cr);
/* text */
cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
pango_cairo_update_layout (cr, layout);
cairo_move_to (cr, text_x, text_y);
pango_cairo_show_layout (cr, layout);
cairo_restore (cr);
g_free (text);
pango_attr_list_unref (attr_list);
g_object_unref (layout);
g_string_free (file_info, TRUE);
}
static void
gth_image_viewer_page_real_activate (GthViewerPage *base,
GthBrowser *browser)
{
GthImageViewerPage *self;
self = (GthImageViewerPage*) base;
self->priv->browser = browser;
self->priv->active = TRUE;
g_action_map_add_action_entries (G_ACTION_MAP (browser),
actions,
G_N_ELEMENTS (actions),
browser);
self->priv->buttons[0] =
gth_browser_add_header_bar_button (browser,
GTH_BROWSER_HEADER_SECTION_VIEWER_ZOOM,
"view-zoom-original-symbolic",
_("Set to actual size"),
"win.image-zoom-100",
NULL);
self->priv->buttons[1] =
gth_browser_add_header_bar_button (browser,
GTH_BROWSER_HEADER_SECTION_VIEWER_ZOOM,
"view-zoom-fit-symbolic",
_("Fit to window if larger"),
"win.image-zoom-fit-if-larger",
NULL);
self->priv->builder = gtk_builder_new_from_resource ("/org/x/Pix/image_viewer/data/ui/toolbar-zoom-menu.ui");
self->priv->buttons[ZOOM_BUTTON] =
gth_browser_add_header_bar_menu_button (browser,
GTH_BROWSER_HEADER_SECTION_VIEWER_ZOOM,
"view-zoom-in-symbolic",
NULL,
_gtk_builder_get_widget (self->priv->builder, "zoom_popover"));
g_signal_connect (_gtk_builder_get_widget (self->priv->builder, "zoom_level_scale"),
"value-changed",
G_CALLBACK (zoom_scale_value_changed_cb),
self);
g_signal_connect (self->priv->buttons[ZOOM_BUTTON],
"toggled",
G_CALLBACK (zoom_button_toggled_cb),
self);
g_signal_connect (_gtk_builder_get_widget (self->priv->builder, "zoom_popover"),
"closed",
G_CALLBACK (zoom_popover_closed_cb),
self);
self->priv->buttons[APPLY_ICC_PROFILE_BUTTON] =
gth_browser_add_header_bar_toggle_button (browser,
GTH_BROWSER_HEADER_SECTION_VIEWER_OTHER_COMMANDS,
"preferences-color-symbolic",
_("Apply the embedded color profile"),
"win.apply-icc-profile",
NULL);
self->priv->buttons[TOGGLE_ANIMATION_BUTTON] =
gth_browser_add_header_bar_toggle_button (browser,
GTH_BROWSER_HEADER_SECTION_VIEWER_COMMANDS,
"media-playback-start-symbolic",
_("Play"),
"win.toggle-animation",
NULL);
self->priv->buttons[STEP_ANIMATION_BUTTON] =
gth_browser_add_header_bar_button (browser,
GTH_BROWSER_HEADER_SECTION_VIEWER_COMMANDS,
"media-skip-forward-symbolic",
_("Next frame"),
"win.step-animation",
NULL);
self->priv->buttons[TRANSPARENCY_STYLE_BUTTON] =
gth_browser_add_header_bar_menu_button (browser,
GTH_BROWSER_HEADER_SECTION_VIEWER_OTHER_COMMANDS,
"transparency-symbolic",
_("Transparency"),
_gtk_builder_get_widget (self->priv->builder, "transparency_popover"));
self->priv->preloader = gth_browser_get_image_preloader (browser);
self->priv->viewer = gth_image_viewer_new ();
g_object_add_weak_pointer (G_OBJECT (self->priv->viewer), (gpointer *) &self->priv->viewer);
gtk_widget_add_events (self->priv->viewer, GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK);
gth_image_viewer_page_reset_viewer_tool (self);
gtk_widget_show (self->priv->viewer);
g_signal_connect (G_OBJECT (self->priv->viewer),
"zoom-changed",
G_CALLBACK (viewer_zoom_changed_cb),
self);
g_signal_connect (G_OBJECT (self->priv->viewer),
"image-changed",
G_CALLBACK (viewer_image_changed_cb),
self);
g_signal_connect (G_OBJECT (self->priv->viewer),
"popup-menu",
G_CALLBACK (viewer_popup_menu_cb),
self);
g_signal_connect_after (G_OBJECT (self->priv->viewer),
"button_press_event",
G_CALLBACK (viewer_button_press_event_cb),
self);
g_signal_connect_after (G_OBJECT (self->priv->viewer),
"scroll_event",
G_CALLBACK (viewer_scroll_event_cb),
self);
g_signal_connect_after (G_OBJECT (self->priv->viewer),
"map_event",
G_CALLBACK (viewer_image_map_event_cb),
self);
g_signal_connect (G_OBJECT (self->priv->viewer),
"realize",
G_CALLBACK (viewer_realize_cb),
self);
g_signal_connect (G_OBJECT (self->priv->viewer),
"unrealize",
G_CALLBACK (viewer_unrealize_cb),
self);
self->priv->image_navigator = gtk_overlay_new ();
g_signal_connect (self->priv->image_navigator,
"get-child-position",
G_CALLBACK (image_navigator_get_child_position_cb),
self);
gtk_container_add (GTK_CONTAINER (self->priv->image_navigator), self->priv->viewer);
gtk_widget_show (self->priv->image_navigator);
self->priv->overview_revealer = gtk_revealer_new ();
gtk_revealer_set_transition_duration (GTK_REVEALER (self->priv->overview_revealer), 200);
gtk_revealer_set_transition_type (GTK_REVEALER (self->priv->overview_revealer), GTK_REVEALER_TRANSITION_TYPE_SLIDE_DOWN);
gtk_widget_show (self->priv->overview_revealer);
gtk_overlay_add_overlay (GTK_OVERLAY (self->priv->image_navigator), self->priv->overview_revealer);
self->priv->overview = gth_image_overview_new (GTH_IMAGE_VIEWER (self->priv->viewer));
gtk_widget_add_events (self->priv->overview, GDK_POINTER_MOTION_HINT_MASK | GDK_LEAVE_NOTIFY_MASK);
gtk_widget_show (self->priv->overview);
gtk_container_add (GTK_CONTAINER (self->priv->overview_revealer), self->priv->overview);
g_signal_connect_after (G_OBJECT (self->priv->overview),
"motion-notify-event",
G_CALLBACK (overview_motion_notify_event_cb),
self);
g_signal_connect_after (G_OBJECT (self->priv->overview),
"leave-notify-event",
G_CALLBACK (overview_leave_notify_event_cb),
self);
gth_browser_set_viewer_widget (browser, self->priv->image_navigator);
gth_viewer_page_focus (GTH_VIEWER_PAGE (self));
}
static void
gth_image_viewer_page_real_deactivate (GthViewerPage *base)
{
GthImageViewerPage *self;
int i;
self = (GthImageViewerPage*) base;
for (i = 0; i < N_HEADER_BAR_BUTTONS; i++) {
if (self->priv->buttons[i] != NULL) {
gtk_widget_destroy (self->priv->buttons[i]);
self->priv->buttons[i] = NULL;
}
}
_g_object_unref (self->priv->builder);
self->priv->builder = NULL;
_g_object_unref (self->priv->preloader);
self->priv->preloader = NULL;
self->priv->active = FALSE;
gth_browser_set_viewer_widget (self->priv->browser, NULL);
}
static void
gth_image_viewer_page_real_show (GthViewerPage *base)
{
GthImageViewerPage *self = (GthImageViewerPage*) base;
if (self->priv->file_popup_merge_id == 0)
self->priv->file_popup_merge_id =
gth_menu_manager_append_entries (gth_browser_get_menu_manager (self->priv->browser, GTH_BROWSER_MENU_MANAGER_FILE_EDIT_ACTIONS),
file_popup_entries,
G_N_ELEMENTS (file_popup_entries));
gth_viewer_page_focus (GTH_VIEWER_PAGE (self));
}
static void
gth_image_viewer_page_real_hide (GthViewerPage *base)
{
GthImageViewerPage *self = (GthImageViewerPage*) base;
gth_menu_manager_remove_entries (gth_browser_get_menu_manager (self->priv->browser, GTH_BROWSER_MENU_MANAGER_FILE_EDIT_ACTIONS), self->priv->file_popup_merge_id);
self->priv->file_popup_merge_id = 0;
}
static gboolean
gth_image_viewer_page_real_can_view (GthViewerPage *base,
GthFileData *file_data)
{
g_return_val_if_fail (file_data != NULL, FALSE);
return _g_mime_type_is_image (gth_file_data_get_mime_type (file_data));
}
static void
preloader_load_ready_cb (GObject *source_object,
GAsyncResult *result,
gpointer user_data)
{
GthImageViewerPage *self = user_data;
GthFileData *requested;
GthImage *image;
int requested_size;
int original_width;
int original_height;
GError *error = NULL;
self->priv->loading_image = FALSE;
if (! _gth_image_viewer_page_load_with_preloader_finish (self))
return;
if (! gth_image_preloader_load_finish (GTH_IMAGE_PRELOADER (source_object),
result,
&requested,
&image,
&requested_size,
&original_width,
&original_height,
&error))
{
if (! g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
gth_image_viewer_page_file_loaded (self, FALSE);
g_clear_error (&error);
return;
}
if (! _g_file_equal (requested->file, self->priv->file_data->file))
goto clear_data;
if (image == NULL) {
gth_image_viewer_page_file_loaded (self, FALSE);
goto clear_data;
}
gth_viewer_page_focus (GTH_VIEWER_PAGE (self));
gth_image_viewer_set_image (GTH_IMAGE_VIEWER (self->priv->viewer),
image,
original_width,
original_height);
gth_image_viewer_set_requested_size (GTH_IMAGE_VIEWER (self->priv->viewer), requested_size);
gtk_widget_queue_draw (self->priv->viewer);
gth_image_history_clear (self->priv->history);
gth_image_history_add_image (self->priv->history,
image,
requested_size,
FALSE);
if ((original_width == -1) || (original_height == -1))
/* In this case the image was loaded at its original size,
* so we get the original size from the image surface size. */
gth_image_viewer_get_original_size (GTH_IMAGE_VIEWER (self->priv->viewer),
&original_width,
&original_height);
g_file_info_set_attribute_int32 (self->priv->updated_info,
"frame::width",
original_width);
g_file_info_set_attribute_int32 (self->priv->updated_info,
"frame::height",
original_height);
{
GthICCProfile *profile;
profile = gth_image_get_icc_profile (image);
if (profile != NULL) {
char *desc = gth_icc_profile_get_description (profile);
if (desc != NULL) {
g_file_info_set_attribute_string (self->priv->updated_info,
"Loaded::Image::ColorProfile",
desc);
g_free (desc);
}
}
}
gth_image_viewer_page_file_loaded (self, TRUE);
update_image_quality_if_required (self);
clear_data:
_g_object_unref (requested);
_g_object_unref (image);
g_clear_error (&error);
return;
}
static void
_gth_image_viewer_page_load (GthImageViewerPage *self,
GthFileData *file_data)
{
GthFileStore *file_store;
GtkTreeIter iter;
int i;
if (self->priv->file_data != file_data) {
_g_object_unref (self->priv->file_data);
self->priv->file_data = gth_file_data_dup (file_data);
_g_object_unref (self->priv->updated_info);
self->priv->updated_info = g_file_info_new ();
}
self->priv->image_changed = FALSE;
self->priv->loading_image = TRUE;
for (i = 0; i < N_FORWARD_PRELOADERS; i++)
_g_clear_object (&self->priv->next_file_data[i]);
for (i = 0; i < N_BACKWARD_PRELOADERS; i++)
_g_clear_object (&self->priv->prev_file_data[i]);
file_store = gth_browser_get_file_store (self->priv->browser);
if (gth_file_store_find_visible (file_store, self->priv->file_data->file, &iter)) {
GtkTreeIter next_iter;
next_iter = iter;
for (i = 0; i < N_FORWARD_PRELOADERS; i++) {
if (! gth_file_store_get_next_visible (file_store, &next_iter))
break;
self->priv->next_file_data[i] = g_object_ref (gth_file_store_get_file (file_store, &next_iter));
}
next_iter = iter;
for (i = 0; i < N_BACKWARD_PRELOADERS; i++) {
if (! gth_file_store_get_prev_visible (file_store, &next_iter))
break;
self->priv->prev_file_data[i] = g_object_ref (gth_file_store_get_file (file_store, &next_iter));
}
gth_image_viewer_set_void (GTH_IMAGE_VIEWER (self->priv->viewer));
}
_gth_image_viewer_page_load_with_preloader (self,
self->priv->file_data,
#ifdef ALWAYS_LOAD_ORIGINAL_SIZE
GTH_ORIGINAL_SIZE,
#else
_gth_image_preloader_get_requested_size_for_next_images (self),
#endif
NULL,
preloader_load_ready_cb,
self);
}
static void
gth_image_viewer_page_real_view (GthViewerPage *base,
GthFileData *file_data)
{
GthImageViewerPage *self;
self = (GthImageViewerPage*) base;
g_return_if_fail (file_data != NULL);
g_return_if_fail (self->priv->active);
gth_viewer_page_focus (GTH_VIEWER_PAGE (self));
_g_clear_object (&self->priv->last_loaded);
if ((self->priv->file_data != NULL)
&& g_file_equal (file_data->file, self->priv->file_data->file)
&& (gth_file_data_get_mtime (file_data) == gth_file_data_get_mtime (self->priv->file_data))
&& ! self->priv->image_changed)
{
gth_image_viewer_page_file_loaded (self, TRUE);
return;
}
_gth_image_viewer_page_load (self, file_data);
}
static void
gth_image_viewer_page_real_focus (GthViewerPage *base)
{
GtkWidget *widget;
widget = GTH_IMAGE_VIEWER_PAGE (base)->priv->viewer;
if (gtk_widget_get_realized (widget) && gtk_widget_get_mapped (widget))
gtk_widget_grab_focus (widget);
}
static void
gth_image_viewer_page_real_fullscreen (GthViewerPage *base,
gboolean active)
{
GthImageViewerPage *self;
GthImageViewerTool *tool;
self = (GthImageViewerPage *) base;
tool = gth_image_viewer_get_tool (GTH_IMAGE_VIEWER (self->priv->viewer));
if (! GTH_IS_IMAGE_DRAGGER (tool))
return;
g_object_set (tool, "show-frame", ! active, NULL);
}
static void
gth_image_viewer_page_real_show_pointer (GthViewerPage *base,
gboolean show)
{
GthImageViewerPage *self;
self = (GthImageViewerPage *) base;
if (show)
gth_image_viewer_show_cursor (GTH_IMAGE_VIEWER (self->priv->viewer));
else if (gth_browser_get_is_fullscreen (self->priv->browser))
gth_image_viewer_hide_cursor (GTH_IMAGE_VIEWER (self->priv->viewer));
if (self->priv->hide_overview_id != 0) {
g_source_remove (self->priv->hide_overview_id);
self->priv->hide_overview_id = 0;
}
self->priv->pointer_on_viewer = show;
update_overview_visibility (self);
}
static void
gth_image_viewer_page_real_update_sensitivity (GthViewerPage *base)
{
GthImageViewerPage *self;
GthImage *image;
gboolean enable_action;
gboolean is_animation;
self = (GthImageViewerPage*) base;
gth_window_enable_action (GTH_WINDOW (self->priv->browser), "image-undo", gth_image_history_can_undo (self->priv->history));
gth_window_enable_action (GTH_WINDOW (self->priv->browser), "image-redo", gth_image_history_can_redo (self->priv->history));
image = gth_image_viewer_get_image (GTH_IMAGE_VIEWER (self->priv->viewer));
enable_action = (image != NULL) && (gth_image_get_icc_profile (image) != NULL);
gtk_widget_set_visible (self->priv->buttons[APPLY_ICC_PROFILE_BUTTON], enable_action);
gth_window_enable_action (GTH_WINDOW (self->priv->browser), "apply-icc-profile", enable_action);
enable_action = (self->priv->file_data != NULL) && _g_mime_type_has_transparency (gth_file_data_get_mime_type (self->priv->file_data));
gtk_widget_set_visible (self->priv->buttons[TRANSPARENCY_STYLE_BUTTON], enable_action);
gth_window_enable_action (GTH_WINDOW (self->priv->browser), "transparency-style", enable_action);
is_animation = gth_image_viewer_is_animation (GTH_IMAGE_VIEWER (self->priv->viewer));
gtk_widget_set_visible (self->priv->buttons[TOGGLE_ANIMATION_BUTTON], is_animation);
gtk_widget_set_visible (self->priv->buttons[STEP_ANIMATION_BUTTON], is_animation);
gth_window_enable_action (GTH_WINDOW (self->priv->browser), "step-animation", ! gth_image_viewer_is_playing_animation (GTH_IMAGE_VIEWER (self->priv->viewer)));
_gth_image_viewer_page_update_paste_command_sensitivity (self, NULL);
update_zoom_info (self);
}
typedef struct {
GthImageViewerPage *self;
GthFileData *file_to_save;
GthFileData *original_file;
FileSavedFunc func;
gpointer user_data;
} SaveData;
static void
save_data_free (SaveData *data)
{
g_object_unref (data->file_to_save);
g_object_unref (data->original_file);
g_free (data);
}
static void
save_image_task_completed_cb (GthTask *task,
GError *error,
gpointer user_data)
{
SaveData *data = user_data;
GthImageViewerPage *self = data->self;
gboolean error_occurred;
error_occurred = error != NULL;
if (error_occurred) {
gth_file_data_set_file (data->file_to_save, data->original_file->file);
g_file_info_set_attribute_boolean (data->file_to_save->info, "gth::file::is-modified", FALSE);
}
if (data->func != NULL)
(data->func) ((GthViewerPage *) self, data->file_to_save, error, data->user_data);
else if (error != NULL)
_gtk_error_dialog_from_gerror_show (GTK_WINDOW (self->priv->browser), _("Could not save the file"), error);
if (! error_occurred) {
GFile *folder;
GList *file_list;
folder = g_file_get_parent (data->file_to_save->file);
file_list = g_list_prepend (NULL, g_object_ref (data->file_to_save->file));
gth_monitor_folder_changed (gth_main_get_default_monitor (),
folder,
file_list,
GTH_MONITOR_EVENT_CHANGED);
_g_object_list_unref (file_list);
g_object_unref (folder);
}
save_data_free (data);
_g_object_unref (task);
}
static void
_gth_image_viewer_page_real_save (GthViewerPage *base,
GFile *file,
const char *mime_type,
FileSavedFunc func,
gpointer user_data)
{
GthImageViewerPage *self;
SaveData *data;
GthFileData *current_file;
GthTask *task;
self = (GthImageViewerPage *) base;
data = g_new0 (SaveData, 1);
data->self = self;
data->func = func;
data->user_data = user_data;
if (mime_type == NULL)
mime_type = gth_file_data_get_mime_type (self->priv->file_data);
current_file = gth_browser_get_current_file (self->priv->browser);
if (current_file == NULL)
return;
data->file_to_save = g_object_ref (current_file);
data->original_file = gth_file_data_dup (current_file);
if (file != NULL)
gth_file_data_set_file (data->file_to_save, file);
/* save the value of 'gth::file::is-modified' into 'gth::file::image-changed'
* to allow the exiv2 metadata writer to not change some fields if the
* content wasn't modified. */
g_file_info_set_attribute_boolean (data->file_to_save->info,
"gth::file::image-changed",
g_file_info_get_attribute_boolean (data->file_to_save->info, "gth::file::is-modified"));
/* the 'gth::file::is-modified' attribute must be set to false before
* saving the file to avoid a scenario where the user is asked whether
* he wants to save the file after saving it.
* This is because when a file is modified in the current folder the
* folder_changed_cb function in gth-browser.c is called automatically
* and if the current file has been modified it is reloaded
* (see file_attributes_ready_cb in gth-browser.c) and if it has been
* modified ('gth::file::is-modified' is TRUE) the user is asked if he
* wants to save (see load_file_delayed_cb in gth-browser.c). */
g_file_info_set_attribute_boolean (data->file_to_save->info, "gth::file::is-modified", FALSE);
task = gth_image_task_chain_new (_("Saving"),
gth_original_image_task_new (self),
gth_save_image_task_new (NULL, mime_type, data->file_to_save, GTH_OVERWRITE_RESPONSE_YES),
NULL);
g_signal_connect (task,
"completed",
G_CALLBACK (save_image_task_completed_cb),
data);
gth_browser_exec_task (GTH_BROWSER (self->priv->browser), task, GTH_TASK_FLAGS_DEFAULT);
}
static gboolean
gth_image_viewer_page_real_can_save (GthViewerPage *base)
{
GArray *savers;
savers = gth_main_get_type_set ("image-saver");
return (savers != NULL) && (savers->len > 0);
}
static void
gth_image_viewer_page_real_save (GthViewerPage *base,
GFile *file,
FileSavedFunc func,
gpointer user_data)
{
_gth_image_viewer_page_real_save (base, file, NULL, func, user_data);
}
/* -- gth_image_viewer_page_real_save_as -- */
typedef struct {
GthImageViewerPage *self;
FileSavedFunc func;
gpointer user_data;
GthFileData *file_data;
GtkWidget *file_sel;
} SaveAsData;
static void
save_as_destroy_cb (GtkWidget *w,
SaveAsData *data)
{
g_object_unref (data->file_data);
g_free (data);
}
static void
save_as_response_cb (GtkDialog *file_sel,
int response,
SaveAsData *data)
{
GFile *file;
const char *mime_type;
if (response != GTK_RESPONSE_OK) {
if (data->func != NULL) {
(*data->func) ((GthViewerPage *) data->self,
data->file_data,
g_error_new_literal (G_IO_ERROR, G_IO_ERROR_CANCELLED, ""),
data->user_data);
}
gtk_widget_destroy (GTK_WIDGET (file_sel));
return;
}
if (! gth_file_chooser_dialog_get_file (GTH_FILE_CHOOSER_DIALOG (file_sel), &file, &mime_type))
return;
gtk_widget_hide (GTK_WIDGET (data->file_sel));
gth_file_data_set_file (data->file_data, file);
_gth_image_viewer_page_real_save ((GthViewerPage *) data->self,
file,
mime_type,
data->func,
data->user_data);
gtk_widget_destroy (GTK_WIDGET (data->file_sel));
g_object_unref (file);
}
static void
gth_image_viewer_page_real_save_as (GthViewerPage *base,
FileSavedFunc func,
gpointer user_data)
{
GthImageViewerPage *self;
GtkWidget *file_sel;
char *uri;
SaveAsData *data;
self = GTH_IMAGE_VIEWER_PAGE (base);
file_sel = gth_file_chooser_dialog_new (_("Save Image"),
GTK_WINDOW (self->priv->browser),
"image-saver");
_gtk_dialog_add_class_to_response (GTK_DIALOG (file_sel), GTK_RESPONSE_OK, GTK_STYLE_CLASS_SUGGESTED_ACTION);
uri = g_file_get_uri (self->priv->file_data->file);
gtk_file_chooser_set_uri (GTK_FILE_CHOOSER (file_sel), uri);
data = g_new0 (SaveAsData, 1);
data->self = self;
data->func = func;
data->file_data = gth_file_data_dup (self->priv->file_data);
data->user_data = user_data;
data->file_sel = file_sel;
g_signal_connect (GTK_DIALOG (file_sel),
"response",
G_CALLBACK (save_as_response_cb),
data);
g_signal_connect (G_OBJECT (file_sel),
"destroy",
G_CALLBACK (save_as_destroy_cb),
data);
gtk_window_set_transient_for (GTK_WINDOW (file_sel), GTK_WINDOW (self->priv->browser));
gtk_window_set_modal (GTK_WINDOW (file_sel), TRUE);
gtk_widget_show (file_sel);
g_free (uri);
}
static void
_gth_image_viewer_page_set_image (GthImageViewerPage *self,
GthImage *image,
int requested_size,
gboolean modified)
{
GthFileData *file_data;
int width;
int height;
if (image == NULL)
return;
if (modified)
gth_image_preloader_set_modified_image (self->priv->preloader, image);
gth_image_viewer_set_image (GTH_IMAGE_VIEWER (self->priv->viewer), image, -1, -1);
gth_image_viewer_set_requested_size (GTH_IMAGE_VIEWER (self->priv->viewer), requested_size);
file_data = gth_browser_get_current_file (GTH_BROWSER (self->priv->browser));
self->priv->image_changed = modified;
g_file_info_set_attribute_boolean (file_data->info, "gth::file::is-modified", modified);
if (gth_image_get_original_size (image, &width, &height)) {
char *size;
g_file_info_set_attribute_int32 (file_data->info, "image::width", width);
g_file_info_set_attribute_int32 (file_data->info, "image::height", height);
size = g_strdup_printf (_("%d × %d"), width, height);
g_file_info_set_attribute_string (file_data->info, "general::dimensions", size);
g_free (size);
}
gth_monitor_metadata_changed (gth_main_get_default_monitor (), file_data);
update_image_quality_if_required (self);
}
static void
_gth_image_viewer_page_set_surface (GthImageViewerPage *self,
cairo_surface_t *surface,
int requested_size,
gboolean modified)
{
GthImage *image;
image = gth_image_new_for_surface (surface);
_gth_image_viewer_page_set_image (self, image, requested_size, modified);
g_object_unref (image);
}
static void
gth_image_viewer_page_real_revert (GthViewerPage *base)
{
GthImageViewerPage *self = GTH_IMAGE_VIEWER_PAGE (base);
GthImageData *last_saved;
last_saved = gth_image_history_revert (self->priv->history);
if (last_saved == NULL)
return;
gth_image_history_add_image (self->priv->history,
last_saved->image,
last_saved->requested_size,
last_saved->unsaved);
_gth_image_viewer_page_set_image (self,
last_saved->image,
last_saved->requested_size,
last_saved->unsaved);
gth_image_data_unref (last_saved);
}
static void
gth_image_viewer_page_real_update_info (GthViewerPage *base,
GthFileData *file_data)
{
GthImageViewerPage *self = GTH_IMAGE_VIEWER_PAGE (base);
if (! _g_file_equal (self->priv->file_data->file, file_data->file))
return;
_g_object_unref (self->priv->file_data);
self->priv->file_data = gth_file_data_dup (file_data);
if (self->priv->viewer == NULL)
return;
gtk_widget_queue_draw (self->priv->viewer);
}
static gboolean
gth_image_viewer_page_real_zoom_from_scroll (GthViewerPage *base,
GdkEventScroll *event)
{
GthImageViewerPage *self = GTH_IMAGE_VIEWER_PAGE (base);
return gth_image_viewer_zoom_from_scroll (GTH_IMAGE_VIEWER (self->priv->viewer), event);
}
static void
gth_image_viewer_page_real_show_properties (GthViewerPage *base,
gboolean show)
{
GthImageViewerPage *self;
self = GTH_IMAGE_VIEWER_PAGE (base);
if (show)
gth_image_viewer_add_painter (GTH_IMAGE_VIEWER (self->priv->viewer), paint_comment_over_image_func, self);
else
gth_image_viewer_remove_painter (GTH_IMAGE_VIEWER (self->priv->viewer), paint_comment_over_image_func, self);
gtk_widget_queue_draw (self->priv->viewer);
}
static const char *
gth_image_viewer_page_shortcut_context (GthViewerPage *base)
{
return GTH_SHORTCUT_VIEWER_CONTEXT_IMAGE;
}
static void
gth_image_viewer_page_finalize (GObject *obj)
{
GthImageViewerPage *self;
int i;
self = GTH_IMAGE_VIEWER_PAGE (obj);
if (self->priv->update_quality_id != 0) {
g_source_remove (self->priv->update_quality_id);
self->priv->update_quality_id = 0;
}
if (self->priv->update_visibility_id != 0) {
g_source_remove (self->priv->update_visibility_id);
self->priv->update_visibility_id = 0;
}
if (self->priv->hide_overview_id != 0) {
g_source_remove (self->priv->hide_overview_id);
self->priv->hide_overview_id = 0;
}
g_object_unref (self->priv->settings);
g_object_unref (self->priv->history);
_g_object_unref (self->priv->file_data);
_g_object_unref (self->priv->last_loaded);
for (i = 0; i < N_FORWARD_PRELOADERS; i++)
_g_clear_object (&self->priv->next_file_data[i]);
for (i = 0; i < N_BACKWARD_PRELOADERS; i++)
_g_clear_object (&self->priv->prev_file_data[i]);
G_OBJECT_CLASS (gth_image_viewer_page_parent_class)->finalize (obj);
}
static void
gth_image_viewer_page_class_init (GthImageViewerPageClass *klass)
{
G_OBJECT_CLASS (klass)->finalize = gth_image_viewer_page_finalize;
}
static void
gth_viewer_page_interface_init (GthViewerPageInterface *iface)
{
iface->activate = gth_image_viewer_page_real_activate;
iface->deactivate = gth_image_viewer_page_real_deactivate;
iface->show = gth_image_viewer_page_real_show;
iface->hide = gth_image_viewer_page_real_hide;
iface->can_view = gth_image_viewer_page_real_can_view;
iface->view = gth_image_viewer_page_real_view;
iface->focus = gth_image_viewer_page_real_focus;
iface->fullscreen = gth_image_viewer_page_real_fullscreen;
iface->show_pointer = gth_image_viewer_page_real_show_pointer;
iface->update_sensitivity = gth_image_viewer_page_real_update_sensitivity;
iface->can_save = gth_image_viewer_page_real_can_save;
iface->save = gth_image_viewer_page_real_save;
iface->save_as = gth_image_viewer_page_real_save_as;
iface->revert = gth_image_viewer_page_real_revert;
iface->update_info = gth_image_viewer_page_real_update_info;
iface->zoom_from_scroll = gth_image_viewer_page_real_zoom_from_scroll;
iface->show_properties = gth_image_viewer_page_real_show_properties;
iface->shortcut_context = gth_image_viewer_page_shortcut_context;
}
static void
gth_image_viewer_page_init (GthImageViewerPage *self)
{
int i;
self->priv = gth_image_viewer_page_get_instance_private (self);
self->priv->settings = g_settings_new (PIX_IMAGE_VIEWER_SCHEMA);
self->priv->preloader = NULL;
self->priv->file_popup_merge_id = 0;
self->priv->history = gth_image_history_new ();
self->priv->file_data = NULL;
self->priv->updated_info = NULL;
self->priv->active = FALSE;
self->priv->image_changed = FALSE;
self->priv->loading_image = FALSE;
self->priv->last_loaded = NULL;
self->priv->can_paste = FALSE;
self->priv->update_quality_id = 0;
self->priv->update_visibility_id = 0;
for (i = 0; i < N_HEADER_BAR_BUTTONS; i++)
self->priv->buttons[i] = NULL;
self->priv->builder = NULL;
self->priv->pointer_on_overview = FALSE;
self->priv->pointer_on_viewer = FALSE;
self->priv->hide_overview_id = 0;
self->priv->apply_icc_profile = TRUE;
for (i = 0; i < N_FORWARD_PRELOADERS; i++)
self->priv->next_file_data[i] = NULL;
for (i = 0; i < N_BACKWARD_PRELOADERS; i++)
self->priv->prev_file_data[i] = NULL;
self->priv->drag_data_get_event = 0;
/* settings notifications */
g_signal_connect (self->priv->settings,
"changed::" PREF_IMAGE_VIEWER_ZOOM_QUALITY,
G_CALLBACK (pref_zoom_quality_changed),
self);
g_signal_connect (self->priv->settings,
"changed::" PREF_IMAGE_VIEWER_ZOOM_CHANGE,
G_CALLBACK (pref_zoom_change_changed),
self);
g_signal_connect (self->priv->settings,
"changed::" PREF_IMAGE_VIEWER_RESET_SCROLLBARS,
G_CALLBACK (pref_reset_scrollbars_changed),
self);
g_signal_connect (self->priv->settings,
"changed::" PREF_IMAGE_VIEWER_TRANSPARENCY_STYLE,
G_CALLBACK (pref_transparency_style_changed),
self);
}
GtkWidget *
gth_image_viewer_page_get_image_viewer (GthImageViewerPage *self)
{
return self->priv->viewer;
}
GdkPixbuf *
gth_image_viewer_page_get_pixbuf (GthImageViewerPage *self)
{
return gth_image_viewer_get_current_pixbuf (GTH_IMAGE_VIEWER (self->priv->viewer));
}
void
gth_image_viewer_page_set_pixbuf (GthImageViewerPage *self,
GdkPixbuf *pixbuf,
gboolean add_to_history)
{
cairo_surface_t *image;
image = _cairo_image_surface_create_from_pixbuf (pixbuf);
gth_image_viewer_page_set_image (self, image, add_to_history);
cairo_surface_destroy (image);
}
cairo_surface_t *
gth_image_viewer_page_get_current_image (GthImageViewerPage *self)
{
return gth_image_viewer_get_current_image (GTH_IMAGE_VIEWER (self->priv->viewer));
}
cairo_surface_t *
gth_image_viewer_page_get_modified_image (GthImageViewerPage *self)
{
return gth_image_preloader_get_modified_image (self->priv->preloader);
}
void
gth_image_viewer_page_set_image (GthImageViewerPage *self,
cairo_surface_t *image,
gboolean add_to_history)
{
if (gth_image_viewer_page_get_current_image (self) == image)
return;
if (add_to_history)
gth_image_history_add_surface (self->priv->history, image, -1, TRUE);
_gth_image_viewer_page_set_surface (self, image, -1, TRUE);
if (add_to_history)
gth_viewer_page_focus (GTH_VIEWER_PAGE (self));
}
void
gth_image_viewer_page_undo (GthImageViewerPage *self)
{
GthImageData *idata;
idata = gth_image_history_undo (self->priv->history);
if (idata != NULL)
_gth_image_viewer_page_set_image (self, idata->image, idata->requested_size, idata->unsaved);
}
void
gth_image_viewer_page_redo (GthImageViewerPage *self)
{
GthImageData *idata;
idata = gth_image_history_redo (self->priv->history);
if (idata != NULL)
_gth_image_viewer_page_set_image (self, idata->image, idata->requested_size, idata->unsaved);
}
GthImageHistory *
gth_image_viewer_page_get_history (GthImageViewerPage *self)
{
return self->priv->history;
}
void
gth_image_viewer_page_reset (GthImageViewerPage *self)
{
GthImageData *last_image;
last_image = gth_image_history_get_last (self->priv->history);
if (last_image == NULL)
return;
_gth_image_viewer_page_set_image (self, last_image->image, last_image->requested_size, last_image->unsaved);
}
static void
viewer_drag_data_get_cb (GtkWidget *widget,
GdkDragContext *context,
GtkSelectionData *data,
guint info,
guint time,
gpointer user_data)
{
GthImageViewerPage *self = user_data;
char *uris[2];
if (self->priv->file_data == NULL)
return;
uris[0] = g_file_get_uri (self->priv->file_data->file);
uris[1] = NULL;
gtk_selection_data_set_uris (data, (char **) uris);
g_free (uris[0]);
}
static void
_gth_image_viewer_page_enable_drag_source (GthImageViewerPage *self,
gboolean enable)
{
GthImageViewerTool *dragger;
GtkTargetList *source_target_list;
GtkTargetEntry *source_targets;
int n_source_targets;
dragger = gth_image_viewer_get_tool (GTH_IMAGE_VIEWER (self->priv->viewer));
if (! GTH_IS_IMAGE_DRAGGER (dragger))
return;
if (! enable) {
if (self->priv->drag_data_get_event > 0) {
g_signal_handler_disconnect (self->priv->viewer, self->priv->drag_data_get_event);
self->priv->drag_data_get_event = 0;
}
gth_image_dragger_disable_drag_source (GTH_IMAGE_DRAGGER (dragger));
return;
}
source_target_list = gtk_target_list_new (NULL, 0);
gtk_target_list_add_uri_targets (source_target_list, 0);
gtk_target_list_add_text_targets (source_target_list, 0);
source_targets = gtk_target_table_new_from_list (source_target_list, &n_source_targets);
gth_image_dragger_enable_drag_source (GTH_IMAGE_DRAGGER (dragger),
GDK_BUTTON1_MASK,
source_targets,
n_source_targets,
GDK_ACTION_COPY | GDK_ACTION_MOVE);
gtk_target_table_free (source_targets, n_source_targets);
gtk_target_list_unref (source_target_list);
if (self->priv->drag_data_get_event == 0)
self->priv->drag_data_get_event = g_signal_connect (self->priv->viewer,
"drag-data-get",
G_CALLBACK (viewer_drag_data_get_cb),
self);
}
void
gth_image_viewer_page_reset_viewer_tool (GthImageViewerPage *self)
{
GthImageViewerTool *dragger;
dragger = gth_image_dragger_new (TRUE);
gth_image_viewer_set_tool (GTH_IMAGE_VIEWER (self->priv->viewer), dragger);
g_object_unref (dragger);
gth_image_viewer_set_fit_mode (GTH_IMAGE_VIEWER (self->priv->viewer), GTH_FIT_SIZE_IF_LARGER);
gth_image_viewer_set_zoom_quality (GTH_IMAGE_VIEWER (self->priv->viewer),
g_settings_get_enum (self->priv->settings, PREF_IMAGE_VIEWER_ZOOM_QUALITY));
gth_image_viewer_set_zoom_change (GTH_IMAGE_VIEWER (self->priv->viewer),
g_settings_get_enum (self->priv->settings, PREF_IMAGE_VIEWER_ZOOM_CHANGE));
gth_image_viewer_set_reset_scrollbars (GTH_IMAGE_VIEWER (self->priv->viewer),
g_settings_get_boolean (self->priv->settings, PREF_IMAGE_VIEWER_RESET_SCROLLBARS));
gth_image_viewer_enable_key_bindings (GTH_IMAGE_VIEWER (self->priv->viewer), FALSE);
pref_transparency_style_changed (self->priv->settings, NULL, self);
_gth_image_viewer_page_enable_drag_source (self, TRUE);
}
gboolean
gth_image_viewer_page_get_is_modified (GthImageViewerPage *self)
{
return self->priv->image_changed;
}
/* -- gth_image_viewer_page_copy_image -- */
static void
copy_image_original_image_ready_cb (GthTask *task,
GError *error,
gpointer user_data)
{
GthImageViewerPage *self = user_data;
cairo_surface_t *image;
image = gth_original_image_task_get_image (task);
if (image != NULL) {
GtkClipboard *clipboard;
GdkPixbuf *pixbuf;
clipboard = gtk_clipboard_get_for_display (gtk_widget_get_display (self->priv->viewer), GDK_SELECTION_CLIPBOARD);
pixbuf = _gdk_pixbuf_new_from_cairo_surface (image);
gtk_clipboard_set_image (clipboard, pixbuf);
g_object_unref (pixbuf);
}
cairo_surface_destroy (image);
g_object_unref (task);
}
void
gth_image_viewer_page_copy_image (GthImageViewerPage *self)
{
GthTask *task;
task = gth_original_image_task_new (self);
g_signal_connect (task,
"completed",
G_CALLBACK (copy_image_original_image_ready_cb),
self);
gth_browser_exec_task (self->priv->browser, task, GTH_TASK_FLAGS_DEFAULT);
}
static void
clipboard_image_received_cb (GtkClipboard *clipboard,
GdkPixbuf *pixbuf,
gpointer user_data)
{
GthImageViewerPage *self = user_data;
if (pixbuf != NULL)
gth_image_viewer_page_set_pixbuf (self, pixbuf, TRUE);
g_object_unref (self);
}
void
gth_image_viewer_page_paste_image (GthImageViewerPage *self)
{
GtkClipboard *clipboard;
clipboard = gtk_clipboard_get_for_display (gtk_widget_get_display (self->priv->viewer), GDK_SELECTION_CLIPBOARD);
gtk_clipboard_request_image (clipboard,
clipboard_image_received_cb,
g_object_ref (self));
}
/* -- gth_image_viewer_page_get_original -- */
typedef struct {
GthImageViewerPage *viewer_page;
GTask *task;
GCancellable *cancellable;
} OriginalImageData;
static OriginalImageData *
get_original_data_new (void)
{
OriginalImageData *data;
data = g_new0 (OriginalImageData, 1);
data->cancellable = NULL;
data->task = NULL;
return data;
}
static void
get_original_data_free (OriginalImageData *data)
{
if (data == NULL)
return;
_g_object_unref (data->viewer_page);
_g_object_unref (data->cancellable);
_g_object_unref (data->task);
g_free (data);
}
static void
original_image_ready_cb (GObject *source_object,
GAsyncResult *result,
gpointer user_data)
{
OriginalImageData *data = user_data;
GthImage *image = NULL;
GError *error = NULL;
if (! _gth_image_viewer_page_load_with_preloader_finish (data->viewer_page)) {
g_task_return_error (data->task, g_error_new_literal (G_IO_ERROR, G_IO_ERROR_CANCELLED, ""));
get_original_data_free (data);
return;
}
if (! gth_image_preloader_load_finish (GTH_IMAGE_PRELOADER (source_object),
result,
NULL,
&image,
NULL,
NULL,
NULL,
&error))
{
g_task_return_error (data->task, error);
}
else
g_task_return_pointer (data->task, image, (GDestroyNotify) g_object_unref);
get_original_data_free (data);
}
void
gth_image_viewer_page_get_original (GthImageViewerPage *self,
GCancellable *cancellable,
GAsyncReadyCallback ready_callback,
gpointer user_data)
{
OriginalImageData *data;
data = get_original_data_new ();
data->viewer_page = g_object_ref (self);
data->cancellable = (cancellable != NULL) ? g_object_ref (cancellable) : g_cancellable_new ();
data->task = g_task_new (G_OBJECT (self),
data->cancellable,
ready_callback,
user_data);
if (gth_image_viewer_is_animation (GTH_IMAGE_VIEWER (self->priv->viewer))) {
GthImage *image;
image = gth_image_new_for_surface (gth_image_viewer_get_current_image (GTH_IMAGE_VIEWER (self->priv->viewer)));
g_task_return_pointer (data->task, image, (GDestroyNotify) g_object_unref);
get_original_data_free (data);
}
else {
_gth_image_viewer_page_load_with_preloader (self,
self->priv->image_changed ? GTH_MODIFIED_IMAGE : self->priv->file_data,
GTH_ORIGINAL_SIZE,
data->cancellable,
original_image_ready_cb,
data);
}
}
gboolean
gth_image_viewer_page_get_original_finish (GthImageViewerPage *self,
GAsyncResult *result,
cairo_surface_t **image_p,
GError **error)
{
GthImage *image;
g_return_val_if_fail (g_task_is_valid (G_TASK (result), G_OBJECT (self)), FALSE);
image = g_task_propagate_pointer (G_TASK (result), error);
if (image == NULL)
return FALSE;
if (image_p != NULL)
*image_p = gth_image_get_cairo_surface (image);
g_object_unref (image);
return TRUE;
}
/* -- GthOriginalImageTask -- */
#define GTH_TYPE_ORIGINAL_IMAGE_TASK (gth_original_image_task_get_type ())
#define GTH_ORIGINAL_IMAGE_TASK(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GTH_TYPE_ORIGINAL_IMAGE_TASK, GthOriginalImageTask))
typedef struct _GthOriginalImageTask GthOriginalImageTask;
typedef struct _GthOriginalImageTaskClass GthOriginalImageTaskClass;
struct _GthOriginalImageTask {
GthImageTask __parent;
GthImageViewerPage *viewer_page;
};
struct _GthOriginalImageTaskClass {
GthImageTaskClass __parent;
};
GType gth_original_image_task_get_type (void);
G_DEFINE_TYPE (GthOriginalImageTask, gth_original_image_task, GTH_TYPE_IMAGE_TASK)
static void
get_original_image_ready_cb (GObject *source_object,
GAsyncResult *result,
gpointer user_data)
{
GthOriginalImageTask *self = user_data;
cairo_surface_t *image = NULL;
GError *error = NULL;
if (gth_image_viewer_page_get_original_finish (self->viewer_page,
result,
&image,
&error))
{
gth_image_task_set_destination_surface (GTH_IMAGE_TASK (self), image);
}
gth_task_completed (GTH_TASK (self), error);
cairo_surface_destroy (image);
_g_error_free (error);
}
static void
gth_original_image_task_exec (GthTask *base)
{
GthOriginalImageTask *self = GTH_ORIGINAL_IMAGE_TASK (base);
gth_task_progress (base, _("Loading the original image"), NULL, TRUE, 0.0);
gth_image_viewer_page_get_original (self->viewer_page,
gth_task_get_cancellable (base),
get_original_image_ready_cb,
self);
}
static void
gth_original_image_task_class_init (GthOriginalImageTaskClass *class)
{
GthTaskClass *task_class;
task_class = GTH_TASK_CLASS (class);
task_class->exec = gth_original_image_task_exec;
}
static void
gth_original_image_task_init (GthOriginalImageTask *self)
{
self->viewer_page = NULL;
gth_task_set_for_viewer (GTH_TASK (self), TRUE);
}
GthTask *
gth_original_image_task_new (GthImageViewerPage *self)
{
GthOriginalImageTask *task;
task = g_object_new (GTH_TYPE_ORIGINAL_IMAGE_TASK, NULL);
task->viewer_page = self;
return GTH_TASK (task);
}
cairo_surface_t *
gth_original_image_task_get_image (GthTask *task)
{
return gth_image_task_get_destination_surface (GTH_IMAGE_TASK (task));
}
void
gth_image_viewer_page_apply_icc_profile (GthImageViewerPage *self,
gboolean apply)
{
GthFileData *file_data;
g_return_if_fail (self->priv->active);
self->priv->apply_icc_profile = apply;
gth_image_preloader_clear_cache (self->priv->preloader);
file_data = gth_browser_get_current_file (self->priv->browser);
if (file_data == NULL)
return;
/* force a complete reload */
_g_object_unref (self->priv->last_loaded);
self->priv->last_loaded = NULL;
g_object_ref (file_data);
_gth_image_viewer_page_load (self, file_data);
g_object_unref (file_data);
}