Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/shell
diff options
context:
space:
mode:
authorCarlos Garcia Campos <carlosgc@gnome.org>2007-01-07 16:28:00 (GMT)
committer Carlos Garcia Campos <carlosgc@src.gnome.org>2007-01-07 16:28:00 (GMT)
commit6b9aeb5d0b86d0002db107ad79af550a4e39f07a (patch)
tree1a26d73986f440d54647da1e1064b79990415859 /shell
parent560065af6f0b02dcb360c3115398f992354865e8 (diff)
Add image handling support. Fixes bugs #310008 and #325047. Images
2007-01-07 Carlos Garcia Campos <carlosgc@gnome.org> * configure.ac: * data/evince-ui.xml: * pdf/ev-poppler.cc: (pdf_document_images_get_images), (pdf_document_document_images_iface_init): * backend/Makefile.am: * backend/ev-document-images.[ch]: * backend/ev-image.[ch]: * lib/ev-file-helpers.[ch]: (ev_tmp_filename): * shell/ev-jobs.[ch]: (ev_job_render_new), (ev_job_render_run), (ev_job_xfer_run): * shell/ev-pixbuf-cache.[ch]: (dispose_cache_job_info), (move_one_job), (copy_job_to_job_info), (add_job_if_needed), (ev_pixbuf_cache_get_image_mapping): * shell/ev-window.c: (view_menu_link_popup), (view_menu_image_popup), (view_menu_popup_cb), (ev_window_dispose), (image_save_dialog_response_cb), (ev_view_popup_cmd_save_image_as), (ev_view_popup_cmd_copy_image): * shell/ev-view-private.h: * shell/ev-view.c: (ev_view_get_image_at_location), (ev_view_do_popup_menu), (ev_view_popup_menu), (ev_view_button_press_event), (ev_view_drag_data_get), (ev_view_drag_motion), (ev_view_drag_data_received), (ev_view_motion_notify_event), (ev_view_button_release_event), (ev_view_finalize), (ev_view_class_init): Add image handling support. Fixes bugs #310008 and #325047. Images selection is not supported yet. svn path=/trunk/; revision=2194
Diffstat (limited to 'shell')
-rw-r--r--shell/ev-jobs.c9
-rw-r--r--shell/ev-jobs.h3
-rw-r--r--shell/ev-pixbuf-cache.c49
-rw-r--r--shell/ev-pixbuf-cache.h4
-rw-r--r--shell/ev-view-private.h11
-rw-r--r--shell/ev-view.c191
-rw-r--r--shell/ev-window.c186
7 files changed, 390 insertions, 63 deletions
diff --git a/shell/ev-jobs.c b/shell/ev-jobs.c
index e54812c..21beaea 100644
--- a/shell/ev-jobs.c
+++ b/shell/ev-jobs.c
@@ -2,6 +2,7 @@
#include "ev-job-queue.h"
#include "ev-document-thumbnails.h"
#include "ev-document-links.h"
+#include "ev-document-images.h"
#include "ev-document-factory.h"
#include "ev-file-helpers.h"
#include "ev-document-fonts.h"
@@ -260,6 +261,7 @@ ev_job_render_new (EvDocument *document,
GdkColor *text,
GdkColor *base,
gboolean include_links,
+ gboolean include_images,
gboolean include_text,
gboolean include_selection)
{
@@ -278,6 +280,7 @@ ev_job_render_new (EvDocument *document,
job->text = *text;
job->base = *base;
job->include_links = include_links;
+ job->include_images = include_images;
job->include_text = include_text;
job->include_selection = include_selection;
@@ -323,6 +326,10 @@ ev_job_render_run (EvJobRender *job)
job->link_mapping =
ev_document_links_get_links (EV_DOCUMENT_LINKS (EV_JOB (job)->document),
job->rc->page);
+ if (job->include_images && EV_IS_DOCUMENT_IMAGES (EV_JOB (job)->document))
+ job->image_mapping =
+ ev_document_images_get_images (EV_DOCUMENT_IMAGES (EV_JOB (job)->document),
+ job->rc->page);
if (job->include_text && EV_IS_SELECTION (EV_JOB (job)->document))
job->text_mapping =
ev_selection_get_selection_map (EV_SELECTION (EV_JOB (job)->document),
@@ -507,7 +514,7 @@ ev_job_xfer_run (EvJobXfer *job)
/* We'd like to keep extension of source uri since
* it helps to resolve some mime types, say cbz */
- tmp_name = ev_tmp_filename ();
+ tmp_name = ev_tmp_filename (NULL);
base_name = gnome_vfs_uri_extract_short_name (source_uri);
job->local_uri = g_strconcat ("file:", tmp_name, "-", base_name, NULL);
g_free (base_name);
diff --git a/shell/ev-jobs.h b/shell/ev-jobs.h
index 46a7938..bf37b67 100644
--- a/shell/ev-jobs.h
+++ b/shell/ev-jobs.h
@@ -125,6 +125,7 @@ struct _EvJobRender
GList *link_mapping;
GdkRegion *text_mapping;
+ GList *image_mapping;
GdkPixbuf *selection;
GdkRegion *selection_region;
@@ -135,6 +136,7 @@ struct _EvJobRender
gint include_links : 1;
gint include_text : 1;
gint include_selection : 1;
+ gint include_images : 1;
};
struct _EvJobRenderClass
@@ -224,6 +226,7 @@ EvJob *ev_job_render_new (EvDocument *document,
GdkColor *text,
GdkColor *base,
gboolean include_links,
+ gboolean include_images,
gboolean include_text,
gboolean include_selection);
void ev_job_render_run (EvJobRender *thumbnail);
diff --git a/shell/ev-pixbuf-cache.c b/shell/ev-pixbuf-cache.c
index 630bdde..885c11a 100644
--- a/shell/ev-pixbuf-cache.c
+++ b/shell/ev-pixbuf-cache.c
@@ -2,6 +2,8 @@
#include "ev-job-queue.h"
#include "ev-page-cache.h"
#include "ev-selection.h"
+#include "ev-document-images.h"
+#include "ev-image.h"
typedef struct _CacheJobInfo
{
@@ -11,6 +13,7 @@ typedef struct _CacheJobInfo
/* Data we get from rendering */
GdkPixbuf *pixbuf;
GList *link_mapping;
+ GList *image_mapping;
GdkRegion *text_mapping;
/* Selection data.
@@ -152,6 +155,10 @@ dispose_cache_job_info (CacheJobInfo *job_info,
ev_link_mapping_free (job_info->link_mapping);
job_info->link_mapping = NULL;
}
+ if (job_info->image_mapping) {
+ ev_image_mapping_free (job_info->image_mapping);
+ job_info->image_mapping = NULL;
+ }
if (job_info->text_mapping) {
gdk_region_destroy (job_info->text_mapping);
job_info->text_mapping = NULL;
@@ -312,6 +319,7 @@ move_one_job (CacheJobInfo *job_info,
job_info->job = NULL;
job_info->pixbuf = NULL;
job_info->link_mapping = NULL;
+ job_info->image_mapping = NULL;
if (new_priority != priority && target_page->job) {
ev_job_queue_update_job (target_page->job, new_priority);
@@ -415,15 +423,19 @@ copy_job_to_job_info (EvJobRender *job_render,
job_info->link_mapping = job_render->link_mapping;
}
+ if (job_render->include_images) {
+ if (job_info->image_mapping)
+ ev_image_mapping_free (job_info->image_mapping);
+ job_info->image_mapping = job_render->image_mapping;
+ }
+
if (job_render->include_text) {
if (job_info->text_mapping)
gdk_region_destroy (job_info->text_mapping);
job_info->text_mapping = job_render->text_mapping;
}
-
if (job_render->include_selection) {
-
if (job_info->selection) {
g_object_unref (G_OBJECT (job_info->selection));
job_info->selection = NULL;
@@ -447,10 +459,9 @@ copy_job_to_job_info (EvJobRender *job_render,
g_object_unref (G_OBJECT (job_info->job));
job_info->job = NULL;
}
-
}
-static CacheJobInfo*
+static CacheJobInfo *
find_job_cache (EvPixbufCache *pixbuf_cache,
int page)
{
@@ -528,6 +539,7 @@ add_job_if_needed (EvPixbufCache *pixbuf_cache,
gboolean include_links = FALSE;
gboolean include_text = FALSE;
gboolean include_selection = FALSE;
+ gboolean include_images = FALSE;
int width, height;
GdkColor *text, *base;
@@ -554,6 +566,8 @@ add_job_if_needed (EvPixbufCache *pixbuf_cache,
/* Figure out what else we need for this job */
if (job_info->link_mapping == NULL)
include_links = TRUE;
+ if (job_info->image_mapping == NULL)
+ include_images = TRUE;
if (job_info->text_mapping == NULL)
include_text = TRUE;
if (new_selection_pixbuf_needed (pixbuf_cache, job_info, page, scale)) {
@@ -570,6 +584,7 @@ add_job_if_needed (EvPixbufCache *pixbuf_cache,
&(job_info->target_points),
text, base,
include_links,
+ include_images,
include_text,
include_selection);
ev_job_queue_add_job (job_info->job, priority);
@@ -690,6 +705,28 @@ ev_pixbuf_cache_get_link_mapping (EvPixbufCache *pixbuf_cache,
return job_info->link_mapping;
}
+GList *
+ev_pixbuf_cache_get_image_mapping (EvPixbufCache *pixbuf_cache,
+ gint page)
+{
+ CacheJobInfo *job_info;
+
+ if (!EV_IS_DOCUMENT_IMAGES (pixbuf_cache->document))
+ return NULL;
+
+ job_info = find_job_cache (pixbuf_cache, page);
+ if (job_info == NULL)
+ return NULL;
+
+ /* We don't need to wait for the idle to handle the callback */
+ if (job_info->job &&
+ EV_JOB (job_info->job)->finished) {
+ copy_job_to_job_info (EV_JOB_RENDER (job_info->job), job_info, pixbuf_cache);
+ }
+
+ return job_info->image_mapping;
+}
+
static gboolean
new_selection_pixbuf_needed (EvPixbufCache *pixbuf_cache,
CacheJobInfo *job_info,
@@ -729,8 +766,8 @@ clear_selection_if_needed (EvPixbufCache *pixbuf_cache,
}
GdkRegion *
-ev_pixbuf_cache_get_text_mapping (EvPixbufCache *pixbuf_cache,
- gint page)
+ev_pixbuf_cache_get_text_mapping (EvPixbufCache *pixbuf_cache,
+ gint page)
{
CacheJobInfo *job_info;
diff --git a/shell/ev-pixbuf-cache.h b/shell/ev-pixbuf-cache.h
index c956832..6f96dc1 100644
--- a/shell/ev-pixbuf-cache.h
+++ b/shell/ev-pixbuf-cache.h
@@ -48,7 +48,7 @@ typedef struct {
typedef struct _EvPixbufCache EvPixbufCache;
typedef struct _EvPixbufCacheClass EvPixbufCacheClass;
-GType ev_pixbuf_cache_get_type (void) G_GNUC_CONST;
+GType ev_pixbuf_cache_get_type (void) G_GNUC_CONST;
EvPixbufCache *ev_pixbuf_cache_new (GtkWidget *view,
EvDocument *document);
void ev_pixbuf_cache_set_page_range (EvPixbufCache *pixbuf_cache,
@@ -61,6 +61,8 @@ GdkPixbuf *ev_pixbuf_cache_get_pixbuf (EvPixbufCache *pixbuf_cache
gint page);
GList *ev_pixbuf_cache_get_link_mapping (EvPixbufCache *pixbuf_cache,
gint page);
+GList *ev_pixbuf_cache_get_image_mapping (EvPixbufCache *pixbuf_cache,
+ gint page);
GdkRegion *ev_pixbuf_cache_get_text_mapping (EvPixbufCache *pixbuf_cache,
gint page);
void ev_pixbuf_cache_clear (EvPixbufCache *pixbuf_cache);
diff --git a/shell/ev-view-private.h b/shell/ev-view-private.h
index f1f5ad5..ec1fa02 100644
--- a/shell/ev-view-private.h
+++ b/shell/ev-view-private.h
@@ -24,6 +24,7 @@
#include "ev-view.h"
#include "ev-pixbuf-cache.h"
#include "ev-page-cache.h"
+#include "ev-image.h"
/* Information for middle clicking and moving around the doc */
typedef struct {
@@ -41,6 +42,13 @@ typedef struct {
GList *selections;
} SelectionInfo;
+/* Information for handling images DND */
+typedef struct {
+ gboolean in_drag;
+ GdkPoint start;
+ EvImage *image;
+} ImageDNDInfo;
+
typedef enum {
SCROLL_TO_KEEP_POSITION,
SCROLL_TO_PAGE_POSITION,
@@ -127,6 +135,9 @@ struct _EvView {
EvViewSelectionMode selection_mode;
SelectionInfo selection_info;
+ /* Image DND */
+ ImageDNDInfo image_dnd_info;
+
/* Links */
GtkWidget *link_tooltip;
EvLink *hovered_link;
diff --git a/shell/ev-view.c b/shell/ev-view.c
index f353f47..1e156d5 100644
--- a/shell/ev-view.c
+++ b/shell/ev-view.c
@@ -35,6 +35,7 @@
#include "ev-utils.h"
#include "ev-selection.h"
#include "ev-document-links.h"
+#include "ev-document-images.h"
#include "ev-document-find.h"
#include "ev-document-transition.h"
#include "ev-document-misc.h"
@@ -73,6 +74,12 @@ enum {
};
enum {
+ TARGET_DND_URI,
+ TARGET_DND_TEXT,
+ TARGET_DND_IMAGE
+};
+
+enum {
TARGET_STRING,
TARGET_TEXT,
TARGET_COMPOUND_TEXT,
@@ -1113,6 +1120,9 @@ ev_view_get_link_at_location (EvView *view,
gint x_offset = 0, y_offset = 0;
gint x_new = 0, y_new = 0;
GList *link_mapping;
+
+ if (!EV_IS_DOCUMENT_LINKS (view->document))
+ return NULL;
x += view->scroll_x;
y += view->scroll_y;
@@ -1508,6 +1518,40 @@ handle_link_over_xy (EvView *view, gint x, gint y)
return;
}
+/*** Images ***/
+static EvImage *
+ev_view_get_image_at_location (EvView *view,
+ gdouble x,
+ gdouble y)
+{
+ gint page = -1;
+ gint x_offset = 0, y_offset = 0;
+ gint x_new = 0, y_new = 0;
+ GList *image_mapping;
+
+ if (!EV_IS_DOCUMENT_IMAGES (view->document))
+ return NULL;
+
+ x += view->scroll_x;
+ y += view->scroll_y;
+
+ find_page_at_location (view, x, y, &page, &x_offset, &y_offset);
+
+ if (page == -1)
+ return NULL;
+
+ if (get_doc_point_from_offset (view, page, x_offset,
+ y_offset, &x_new, &y_new) == FALSE)
+ return NULL;
+
+ image_mapping = ev_pixbuf_cache_get_image_mapping (view->pixbuf_cache, page);
+
+ if (image_mapping)
+ return ev_image_mapping_find (image_mapping, x_new, y_new);
+ else
+ return NULL;
+}
+
/*** GtkWidget implementation ***/
static void
@@ -1900,16 +1944,37 @@ ev_view_expose_event (GtkWidget *widget,
}
static gboolean
+ev_view_do_popup_menu (EvView *view,
+ gdouble x,
+ gdouble y)
+{
+ EvLink *link;
+ EvImage *image;
+
+ image = ev_view_get_image_at_location (view, x, y);
+ if (image) {
+ g_signal_emit (view, signals[SIGNAL_POPUP_MENU], 0, image);
+ return TRUE;
+ }
+
+ link = ev_view_get_link_at_location (view, x, y);
+ if (link) {
+ g_signal_emit (view, signals[SIGNAL_POPUP_MENU], 0, link);
+ return TRUE;
+ }
+
+ g_signal_emit (view, signals[SIGNAL_POPUP_MENU], 0, NULL);
+
+ return TRUE;
+}
+
+static gboolean
ev_view_popup_menu (GtkWidget *widget)
{
- gint x, y;
- EvLink *link;
- EvView *view = EV_VIEW (widget);
-
- gtk_widget_get_pointer (widget, &x, &y);
- link = ev_view_get_link_at_location (view, x, y);
- g_signal_emit (view, signals[SIGNAL_POPUP_MENU], 0, link);
- return TRUE;
+ gint x, y;
+
+ gtk_widget_get_pointer (widget, &x, &y);
+ return ev_view_do_popup_menu (EV_VIEW (widget), x, y);
}
static gboolean
@@ -1917,7 +1982,6 @@ ev_view_button_press_event (GtkWidget *widget,
GdkEventButton *event)
{
EvView *view = EV_VIEW (widget);
- EvLink *link;
if (!GTK_WIDGET_HAS_FOCUS (widget)) {
gtk_widget_grab_focus (widget);
@@ -1927,7 +1991,9 @@ ev_view_button_press_event (GtkWidget *widget,
view->selection_info.in_drag = FALSE;
switch (event->button) {
- case 1:
+ case 1: {
+ EvImage *image;
+
if (view->selection_info.selections) {
if (location_in_selected_text (view,
event->x + view->scroll_x,
@@ -1938,11 +2004,19 @@ ev_view_button_press_event (GtkWidget *widget,
}
gtk_widget_queue_draw (widget);
+ } else if ((image = ev_view_get_image_at_location (view, event->x, event->y))) {
+ if (view->image_dnd_info.image)
+ g_object_unref (view->image_dnd_info.image);
+ view->image_dnd_info.image = g_object_ref (image);
+ view->image_dnd_info.in_drag = TRUE;
+
+ view->image_dnd_info.start.x = event->x + view->scroll_x;
+ view->image_dnd_info.start.y = event->y + view->scroll_y;
}
view->selection_info.start.x = event->x + view->scroll_x;
view->selection_info.start.y = event->y + view->scroll_y;
-
+ }
return TRUE;
case 2:
/* use root coordinates as reference point because
@@ -1956,9 +2030,7 @@ ev_view_button_press_event (GtkWidget *widget,
return TRUE;
case 3:
- link = ev_view_get_link_at_location (view, event->x, event->y);
- g_signal_emit (view, signals[SIGNAL_POPUP_MENU], 0, link);
- return TRUE;
+ return ev_view_do_popup_menu (view, event->x, event->y);
}
return FALSE;
@@ -1974,18 +2046,62 @@ ev_view_drag_data_get (GtkWidget *widget,
{
EvView *view = EV_VIEW (widget);
- if (view->selection_info.selections &&
- ev_document_can_get_text (view->document)) {
- gchar *text;
+ switch (info) {
+ case TARGET_DND_TEXT:
+ if (view->selection_info.selections &&
+ ev_document_can_get_text (view->document)) {
+ gchar *text;
- text = get_selected_text (view);
+ text = get_selected_text (view);
+
+ gtk_selection_data_set_text (selection_data,
+ text,
+ strlen (text));
+
+ g_free (text);
+ }
+ break;
+ case TARGET_DND_IMAGE:
+ if (view->image_dnd_info.image) {
+ GdkPixbuf *pixbuf;
+
+ pixbuf = ev_image_get_pixbuf (view->image_dnd_info.image);
+ gtk_selection_data_set_pixbuf (selection_data, pixbuf);
+ }
+ break;
+ case TARGET_DND_URI:
+ if (view->image_dnd_info.image) {
+ const gchar *tmp_uri;
+ gchar **uris;
- gtk_selection_data_set_text (selection_data, text, strlen (text));
+ tmp_uri = ev_image_save_tmp (view->image_dnd_info.image);
- g_free (text);
+ uris = g_new0 (gchar *, 2);
+ uris[0] = (gchar *)tmp_uri;
+
+ gtk_selection_data_set_uris (selection_data, uris);
+
+ /* g_free instead of g_strfreev since tmp_uri is const */
+ g_free (uris);
+ }
}
}
+static gboolean
+ev_view_drag_motion (GtkWidget *widget,
+ GdkDragContext *context,
+ gint x,
+ gint y,
+ guint time)
+{
+ if (gtk_drag_get_source_widget (context) == widget)
+ gdk_drag_status (context, 0, time);
+ else
+ gdk_drag_status (context, context->suggested_action, time);
+
+ return TRUE;
+}
+
static void
ev_view_drag_data_received (GtkWidget *widget,
GdkDragContext *context,
@@ -2086,14 +2202,13 @@ ev_view_motion_notify_event (GtkWidget *widget,
view->selection_info.start.x,
view->selection_info.start.y,
x, y)) {
- GdkDragContext *context;
GtkTargetList *target_list = gtk_target_list_new (NULL, 0);
- gtk_target_list_add_text_targets (target_list, 0);
+ gtk_target_list_add_text_targets (target_list, TARGET_DND_TEXT);
- context = gtk_drag_begin (widget, target_list,
- GDK_ACTION_COPY,
- 1, (GdkEvent *)event);
+ gtk_drag_begin (widget, target_list,
+ GDK_ACTION_COPY,
+ 1, (GdkEvent *)event);
view->selection_info.in_drag = FALSE;
@@ -2101,6 +2216,26 @@ ev_view_motion_notify_event (GtkWidget *widget,
return TRUE;
}
+ } else if (view->image_dnd_info.in_drag) {
+ if (gtk_drag_check_threshold (widget,
+ view->selection_info.start.x,
+ view->selection_info.start.y,
+ x, y)) {
+ GtkTargetList *target_list = gtk_target_list_new (NULL, 0);
+
+ gtk_target_list_add_uri_targets (target_list, TARGET_DND_URI);
+ gtk_target_list_add_image_targets (target_list, TARGET_DND_IMAGE, TRUE);
+
+ gtk_drag_begin (widget, target_list,
+ GDK_ACTION_COPY,
+ 1, (GdkEvent *)event);
+
+ view->image_dnd_info.in_drag = FALSE;
+
+ gtk_target_list_unref (target_list);
+
+ return TRUE;
+ }
}
/* For the Evince 0.4.x release, we limit selection to un-rotated
@@ -2191,6 +2326,7 @@ ev_view_button_release_event (GtkWidget *widget,
view->pressed_button = -1;
view->drag_info.in_drag = FALSE;
+ view->image_dnd_info.in_drag = FALSE;
if (view->selection_scroll_id) {
g_source_remove (view->selection_scroll_id);
@@ -2818,6 +2954,10 @@ ev_view_finalize (GObject *object)
clear_selection (view);
+ if (view->image_dnd_info.image)
+ g_object_unref (view->image_dnd_info.image);
+ view->image_dnd_info.image = NULL;
+
G_OBJECT_CLASS (ev_view_parent_class)->finalize (object);
}
@@ -3003,6 +3143,7 @@ ev_view_class_init (EvViewClass *class)
widget_class->leave_notify_event = ev_view_leave_notify_event;
widget_class->style_set = ev_view_style_set;
widget_class->drag_data_get = ev_view_drag_data_get;
+ widget_class->drag_motion = ev_view_drag_motion;
widget_class->drag_data_received = ev_view_drag_data_received;
widget_class->popup_menu = ev_view_popup_menu;
gtk_object_class->destroy = ev_view_destroy;
diff --git a/shell/ev-window.c b/shell/ev-window.c
index ec97987..c0adf8e 100644
--- a/shell/ev-window.c
+++ b/shell/ev-window.c
@@ -70,6 +70,7 @@
#include "ev-utils.h"
#include "ev-debug.h"
#include "ev-history.h"
+#include "ev-image.h"
#ifdef WITH_GNOME_PRINT
#include "ev-print-job.h"
@@ -151,9 +152,10 @@ struct _EvWindowPrivate {
GtkWidget *fullscreen_popup;
guint fullscreen_timeout_id;
- /* Popup link */
+ /* Popup view */
GtkWidget *view_popup;
EvLink *link;
+ EvImage *image;
/* Popup attachment */
GtkWidget *attachment_popup;
@@ -242,6 +244,10 @@ static void ev_view_popup_cmd_open_link_new_window (GtkAction *actio
EvWindow *window);
static void ev_view_popup_cmd_copy_link_address (GtkAction *action,
EvWindow *window);
+static void ev_view_popup_cmd_save_image_as (GtkAction *action,
+ EvWindow *window);
+static void ev_view_popup_cmd_copy_image (GtkAction *action,
+ EvWindow *window);
static void ev_attachment_popup_cmd_open_attachment (GtkAction *action,
EvWindow *window);
static void ev_attachment_popup_cmd_save_attachment_as (GtkAction *action,
@@ -3290,18 +3296,13 @@ ev_window_sidebar_visibility_changed_cb (EvSidebar *ev_sidebar,
}
}
-static gboolean
-view_menu_popup_cb (EvView *view,
- EvLink *link,
- EvWindow *ev_window)
+static void
+view_menu_link_popup (EvWindow *ev_window,
+ EvLink *link)
{
- GtkWidget *popup;
gboolean show_external = FALSE;
gboolean show_internal = FALSE;
GtkAction *action;
-
- if (ev_view_get_presentation (EV_VIEW (ev_window->priv->view)))
- return FALSE;
if (ev_window->priv->link)
g_object_unref (ev_window->priv->link);
@@ -3311,26 +3312,23 @@ view_menu_popup_cb (EvView *view,
else
ev_window->priv->link = NULL;
- popup = ev_window->priv->view_popup;
-
if (ev_window->priv->link) {
EvLinkAction *ev_action;
ev_action = ev_link_get_action (link);
- if (!ev_action)
- return FALSE;
-
- switch (ev_link_action_get_action_type (ev_action)) {
- case EV_LINK_ACTION_TYPE_GOTO_DEST:
- case EV_LINK_ACTION_TYPE_GOTO_REMOTE:
- show_internal = TRUE;
- break;
- case EV_LINK_ACTION_TYPE_EXTERNAL_URI:
- case EV_LINK_ACTION_TYPE_LAUNCH:
- show_external = TRUE;
- break;
- default:
- break;
+ if (ev_action) {
+ switch (ev_link_action_get_action_type (ev_action)) {
+ case EV_LINK_ACTION_TYPE_GOTO_DEST:
+ case EV_LINK_ACTION_TYPE_GOTO_REMOTE:
+ show_internal = TRUE;
+ break;
+ case EV_LINK_ACTION_TYPE_EXTERNAL_URI:
+ case EV_LINK_ACTION_TYPE_LAUNCH:
+ show_external = TRUE;
+ break;
+ default:
+ break;
+ }
}
}
@@ -3349,9 +3347,49 @@ view_menu_popup_cb (EvView *view,
action = gtk_action_group_get_action (ev_window->priv->view_popup_action_group,
"OpenLinkNewWindow");
gtk_action_set_visible (action, show_internal);
+}
- gtk_menu_popup (GTK_MENU (popup), NULL, NULL,
- NULL, NULL,
+static void
+view_menu_image_popup (EvWindow *ev_window,
+ EvImage *image)
+{
+ GtkAction *action;
+ gboolean show_image = FALSE;
+
+ if (ev_window->priv->image)
+ g_object_unref (ev_window->priv->image);
+
+ if (image)
+ ev_window->priv->image = g_object_ref (image);
+ else
+ ev_window->priv->image = NULL;
+
+ show_image = (ev_window->priv->image != NULL);
+
+ action = gtk_action_group_get_action (ev_window->priv->view_popup_action_group,
+ "SaveImageAs");
+ gtk_action_set_visible (action, show_image);
+
+ action = gtk_action_group_get_action (ev_window->priv->view_popup_action_group,
+ "CopyImage");
+ gtk_action_set_visible (action, show_image);
+}
+
+static gboolean
+view_menu_popup_cb (EvView *view,
+ GObject *object,
+ EvWindow *ev_window)
+{
+ if (ev_view_get_presentation (EV_VIEW (ev_window->priv->view)))
+ return FALSE;
+
+ view_menu_link_popup (ev_window,
+ EV_IS_LINK (object) ? EV_LINK (object) : NULL);
+ view_menu_image_popup (ev_window,
+ EV_IS_IMAGE (object) ? EV_IMAGE (object) : NULL);
+
+ gtk_menu_popup (GTK_MENU (ev_window->priv->view_popup),
+ NULL, NULL, NULL, NULL,
3, gtk_get_current_event_time ());
return TRUE;
}
@@ -3617,6 +3655,11 @@ ev_window_dispose (GObject *object)
priv->link = NULL;
}
+ if (priv->image) {
+ g_object_unref (priv->image);
+ priv->image = NULL;
+ }
+
if (priv->attach_list) {
g_list_foreach (priv->attach_list,
(GFunc) g_object_unref,
@@ -3848,8 +3891,11 @@ static const GtkActionEntry view_popup_entries [] = {
{ "OpenLinkNewWindow", NULL, N_("Open in New _Window"), NULL,
NULL, G_CALLBACK (ev_view_popup_cmd_open_link_new_window) },
{ "CopyLinkAddress", NULL, N_("_Copy Link Address"), NULL,
- NULL,
- G_CALLBACK (ev_view_popup_cmd_copy_link_address) },
+ NULL, G_CALLBACK (ev_view_popup_cmd_copy_link_address) },
+ { "SaveImageAs", NULL, N_("_Save Image As..."), NULL,
+ NULL, G_CALLBACK (ev_view_popup_cmd_save_image_as) },
+ { "CopyImage", NULL, N_("Copy _Image"), NULL,
+ NULL, G_CALLBACK (ev_view_popup_cmd_copy_image) },
};
static const GtkActionEntry attachment_popup_entries [] = {
@@ -4229,6 +4275,86 @@ ev_view_popup_cmd_copy_link_address (GtkAction *action, EvWindow *window)
}
static void
+image_save_dialog_response_cb (GtkWidget *fc,
+ gint response_id,
+ EvWindow *ev_window)
+{
+ gchar *uri;
+ gchar *filename;
+ GError *error = NULL;
+
+ if (response_id != GTK_RESPONSE_OK) {
+ gtk_widget_destroy (fc);
+ return;
+ }
+
+ uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (fc));
+ filename = g_filename_from_uri (uri, NULL, NULL);
+ g_free (uri);
+
+ /* FIXME: allow saving in other image formats than png */
+ gdk_pixbuf_save (ev_image_get_pixbuf (ev_window->priv->image),
+ filename, "png", &error, NULL);
+
+ if (error) {
+ ev_window_error_dialog (GTK_WINDOW (fc),
+ _("The image could not be saved."),
+ error);
+ g_error_free (error);
+ }
+
+ g_free (filename);
+
+ gtk_widget_destroy (fc);
+}
+
+static void
+ev_view_popup_cmd_save_image_as (GtkAction *action, EvWindow *window)
+{
+ GtkWidget *fc;
+ GtkFileFilter *filter;
+
+ if (!window->priv->image)
+ return;
+
+ fc = gtk_file_chooser_dialog_new (_("Save Image"),
+ GTK_WINDOW (window),
+ GTK_FILE_CHOOSER_ACTION_SAVE,
+ GTK_STOCK_CANCEL,
+ GTK_RESPONSE_CANCEL,
+ GTK_STOCK_SAVE, GTK_RESPONSE_OK,
+ NULL);
+
+ gtk_dialog_set_default_response (GTK_DIALOG (fc), GTK_RESPONSE_OK);
+ gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (fc), TRUE);
+
+ filter = gtk_file_filter_new ();
+ gtk_file_filter_set_name (filter, _("Images"));
+ gtk_file_filter_add_pixbuf_formats (filter);
+ gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (fc), filter);
+
+ g_signal_connect (fc, "response",
+ G_CALLBACK (image_save_dialog_response_cb),
+ window);
+
+ gtk_widget_show (fc);
+}
+
+static void
+ev_view_popup_cmd_copy_image (GtkAction *action, EvWindow *window)
+{
+ GtkClipboard *clipboard;
+
+ if (!window->priv->image)
+ return;
+
+ clipboard = gtk_widget_get_clipboard (GTK_WIDGET (window),
+ GDK_SELECTION_CLIPBOARD);
+ gtk_clipboard_set_image (clipboard,
+ ev_image_get_pixbuf (window->priv->image));
+}
+
+static void
ev_attachment_popup_cmd_open_attachment (GtkAction *action, EvWindow *window)
{
GList *l;
@@ -4317,7 +4443,7 @@ ev_attachment_popup_cmd_save_attachment_as (GtkAction *action, EvWindow *window)
attachment = (EvAttachment *) window->priv->attach_list->data;
fc = gtk_file_chooser_dialog_new (
- _("Save a Copy"),
+ _("Save Attachment"),
GTK_WINDOW (window),
attachment ? GTK_FILE_CHOOSER_ACTION_SAVE : GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
GTK_STOCK_CANCEL,