1 /*
2  * e-attachment-view.c
3  *
4  * This program is free software; you can redistribute it and/or modify it
5  * under the terms of the GNU Lesser General Public License as published by
6  * the Free Software Foundation.
7  *
8  * This program is distributed in the hope that it will be useful, but
9  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
10  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
11  * for more details.
12  *
13  * You should have received a copy of the GNU Lesser General Public License
14  * along with this program; if not, see <http://www.gnu.org/licenses/>.
15  *
16  *
17  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
18  *
19  */
20 
21 #include "evolution-config.h"
22 
23 #include "e-attachment-view.h"
24 
25 #include <glib/gi18n.h>
26 #include <gdk/gdkkeysyms.h>
27 
28 #include "e-attachment-dialog.h"
29 #include "e-attachment-handler-image.h"
30 #include "e-misc-utils.h"
31 #include "e-selection.h"
32 
33 enum {
34 	UPDATE_ACTIONS,
35 	LAST_SIGNAL
36 };
37 
38 /* Note: Do not use the info field. */
39 static GtkTargetEntry target_table[] = {
40 	{ (gchar *) "text/uri-list", 0, 0 },
41 	{ (gchar *) "_NETSCAPE_URL", 0, 0 }
42 };
43 
44 static const gchar *ui =
45 "<ui>"
46 "  <popup name='context'>"
47 "    <menuitem action='cancel'/>"
48 "    <menuitem action='save-as'/>"
49 "    <menuitem action='remove'/>"
50 "    <menuitem action='properties'/>"
51 "    <separator/>"
52 "    <placeholder name='inline-actions'/>"
53 "    <separator/>"
54 "    <placeholder name='custom-actions'/>"
55 "    <separator/>"
56 "    <menuitem action='add'/>"
57 "    <separator/>"
58 "    <placeholder name='open-actions'/>"
59 "    <menuitem action='open-with'/>"
60 "  </popup>"
61 "</ui>";
62 
63 static gulong signals[LAST_SIGNAL];
64 
G_DEFINE_INTERFACE(EAttachmentView,e_attachment_view,GTK_TYPE_WIDGET)65 G_DEFINE_INTERFACE (
66 	EAttachmentView,
67 	e_attachment_view,
68 	GTK_TYPE_WIDGET)
69 
70 static void
71 action_add_cb (GtkAction *action,
72                EAttachmentView *view)
73 {
74 	EAttachmentStore *store;
75 	gpointer parent;
76 
77 	parent = gtk_widget_get_toplevel (GTK_WIDGET (view));
78 	parent = gtk_widget_is_toplevel (parent) ? parent : NULL;
79 
80 	store = e_attachment_view_get_store (view);
81 	e_attachment_store_run_load_dialog (store, parent);
82 }
83 
84 static void
action_cancel_cb(GtkAction * action,EAttachmentView * view)85 action_cancel_cb (GtkAction *action,
86                   EAttachmentView *view)
87 {
88 	EAttachment *attachment;
89 	GList *list;
90 
91 	list = e_attachment_view_get_selected_attachments (view);
92 	g_return_if_fail (g_list_length (list) == 1);
93 	attachment = list->data;
94 
95 	e_attachment_cancel (attachment);
96 
97 	g_list_foreach (list, (GFunc) g_object_unref, NULL);
98 	g_list_free (list);
99 }
100 
101 static void
action_open_with_cb(GtkAction * action,EAttachmentView * view)102 action_open_with_cb (GtkAction *action,
103                      EAttachmentView *view)
104 {
105 	EAttachment *attachment;
106 	EAttachmentStore *store;
107 	GtkWidget *dialog;
108 	GtkTreePath *path;
109 	GtkTreeIter iter;
110 	GAppInfo *app_info = NULL;
111 	GFileInfo *file_info;
112 	GList *list;
113 	gpointer parent;
114 	const gchar *content_type;
115 
116 	parent = gtk_widget_get_toplevel (GTK_WIDGET (view));
117 	parent = gtk_widget_is_toplevel (parent) ? parent : NULL;
118 
119 	list = e_attachment_view_get_selected_paths (view);
120 	g_return_if_fail (g_list_length (list) == 1);
121 	path = list->data;
122 
123 	store = e_attachment_view_get_store (view);
124 	gtk_tree_model_get_iter (GTK_TREE_MODEL (store), &iter, path);
125 	gtk_tree_model_get (
126 		GTK_TREE_MODEL (store), &iter,
127 		E_ATTACHMENT_STORE_COLUMN_ATTACHMENT, &attachment, -1);
128 	g_return_if_fail (E_IS_ATTACHMENT (attachment));
129 
130 	file_info = e_attachment_ref_file_info (attachment);
131 	g_return_if_fail (file_info != NULL);
132 
133 	content_type = g_file_info_get_content_type (file_info);
134 
135 	dialog = gtk_app_chooser_dialog_new_for_content_type (
136 		parent, 0, content_type);
137 	if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK) {
138 		GtkAppChooser *app_chooser = GTK_APP_CHOOSER (dialog);
139 		app_info = gtk_app_chooser_get_app_info (app_chooser);
140 	}
141 	gtk_widget_destroy (dialog);
142 
143 	if (app_info != NULL) {
144 		e_attachment_view_open_path (view, path, app_info);
145 		g_object_unref (app_info);
146 	}
147 
148 	g_object_unref (file_info);
149 
150 	g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL);
151 	g_list_free (list);
152 }
153 
154 static void
action_open_with_app_info_cb(GtkAction * action,EAttachmentView * view)155 action_open_with_app_info_cb (GtkAction *action,
156                               EAttachmentView *view)
157 {
158 	GAppInfo *app_info;
159 	GtkTreePath *path;
160 	GList *list;
161 
162 	list = e_attachment_view_get_selected_paths (view);
163 	g_return_if_fail (g_list_length (list) == 1);
164 	path = list->data;
165 
166 	app_info = g_object_get_data (G_OBJECT (action), "app-info");
167 
168 	e_attachment_view_open_path (view, path, app_info);
169 
170 	g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL);
171 	g_list_free (list);
172 }
173 
174 static void
action_properties_cb(GtkAction * action,EAttachmentView * view)175 action_properties_cb (GtkAction *action,
176                       EAttachmentView *view)
177 {
178 	EAttachment *attachment;
179 	GtkWidget *dialog;
180 	GList *list;
181 	gpointer parent;
182 
183 	list = e_attachment_view_get_selected_attachments (view);
184 	g_return_if_fail (g_list_length (list) == 1);
185 	attachment = list->data;
186 
187 	parent = gtk_widget_get_toplevel (GTK_WIDGET (view));
188 	parent = gtk_widget_is_toplevel (parent) ? parent : NULL;
189 
190 	dialog = e_attachment_dialog_new (parent, attachment);
191 	gtk_dialog_run (GTK_DIALOG (dialog));
192 	gtk_widget_destroy (dialog);
193 
194 	g_list_foreach (list, (GFunc) g_object_unref, NULL);
195 	g_list_free (list);
196 }
197 
198 static void
action_remove_cb(GtkAction * action,EAttachmentView * view)199 action_remove_cb (GtkAction *action,
200                   EAttachmentView *view)
201 {
202 	e_attachment_view_remove_selected (view, FALSE);
203 }
204 
205 static void
call_attachment_save_handle_error(GObject * source_object,GAsyncResult * result,gpointer user_data)206 call_attachment_save_handle_error (GObject *source_object,
207 				   GAsyncResult *result,
208 				   gpointer user_data)
209 {
210 	GtkWindow *window = user_data;
211 
212 	g_return_if_fail (E_IS_ATTACHMENT (source_object));
213 	g_return_if_fail (!window || GTK_IS_WINDOW (window));
214 
215 	e_attachment_save_handle_error (E_ATTACHMENT (source_object), result, window);
216 
217 	g_clear_object (&window);
218 }
219 
220 static void
action_save_all_cb(GtkAction * action,EAttachmentView * view)221 action_save_all_cb (GtkAction *action,
222                     EAttachmentView *view)
223 {
224 	EAttachmentStore *store;
225 	GList *list, *iter;
226 	GFile *destination;
227 	gpointer parent;
228 
229 	store = e_attachment_view_get_store (view);
230 
231 	parent = gtk_widget_get_toplevel (GTK_WIDGET (view));
232 	parent = gtk_widget_is_toplevel (parent) ? parent : NULL;
233 
234 	/* XXX We lose the previous selection. */
235 	e_attachment_view_select_all (view);
236 	list = e_attachment_view_get_selected_attachments (view);
237 	e_attachment_view_unselect_all (view);
238 
239 	destination = e_attachment_store_run_save_dialog (
240 		store, list, parent);
241 
242 	if (destination == NULL)
243 		goto exit;
244 
245 	for (iter = list; iter != NULL; iter = iter->next) {
246 		EAttachment *attachment = iter->data;
247 
248 		e_attachment_save_async (
249 			attachment, destination, (GAsyncReadyCallback)
250 			call_attachment_save_handle_error, parent ? g_object_ref (parent) : NULL);
251 	}
252 
253 	g_object_unref (destination);
254 
255 exit:
256 	g_list_foreach (list, (GFunc) g_object_unref, NULL);
257 	g_list_free (list);
258 }
259 
260 static void
action_save_as_cb(GtkAction * action,EAttachmentView * view)261 action_save_as_cb (GtkAction *action,
262                    EAttachmentView *view)
263 {
264 	EAttachmentStore *store;
265 	GList *list, *iter;
266 	GFile *destination;
267 	gpointer parent;
268 
269 	store = e_attachment_view_get_store (view);
270 
271 	parent = gtk_widget_get_toplevel (GTK_WIDGET (view));
272 	parent = gtk_widget_is_toplevel (parent) ? parent : NULL;
273 
274 	list = e_attachment_view_get_selected_attachments (view);
275 
276 	destination = e_attachment_store_run_save_dialog (
277 		store, list, parent);
278 
279 	if (destination == NULL)
280 		goto exit;
281 
282 	for (iter = list; iter != NULL; iter = iter->next) {
283 		EAttachment *attachment = iter->data;
284 
285 		e_attachment_save_async (
286 			attachment, destination, (GAsyncReadyCallback)
287 			call_attachment_save_handle_error, parent ? g_object_ref (parent) : NULL);
288 	}
289 
290 	g_object_unref (destination);
291 
292 exit:
293 	g_list_foreach (list, (GFunc) g_object_unref, NULL);
294 	g_list_free (list);
295 }
296 
297 static GtkActionEntry standard_entries[] = {
298 
299 	{ "cancel",
300 	  "process-stop",
301 	  N_("_Cancel"),
302 	  NULL,
303 	  NULL,  /* XXX Add a tooltip! */
304 	  G_CALLBACK (action_cancel_cb) },
305 
306 	{ "open-with",
307 	  NULL,
308 	  N_("Open With Other Application…"),
309 	  NULL,
310 	  NULL,  /* XXX Add a tooltip! */
311 	  G_CALLBACK (action_open_with_cb) },
312 
313 	{ "save-all",
314 	  "document-save-as",
315 	  N_("S_ave All"),
316 	  NULL,
317 	  NULL,  /* XXX Add a tooltip! */
318 	  G_CALLBACK (action_save_all_cb) },
319 
320 	{ "save-as",
321 	  "document-save-as",
322 	  N_("Sa_ve As"),
323 	  NULL,
324 	  NULL,  /* XXX Add a tooltip! */
325 	  G_CALLBACK (action_save_as_cb) },
326 
327 	/* Alternate "save-all" label, for when
328 	 * the attachment store has one row. */
329 	{ "save-one",
330 	  "document-save-as",
331 	  N_("Save _As"),
332 	  NULL,
333 	  NULL,  /* XXX Add a tooltip! */
334 	  G_CALLBACK (action_save_all_cb) },
335 };
336 
337 static GtkActionEntry editable_entries[] = {
338 
339 	{ "add",
340 	  "list-add",
341 	  N_("A_dd Attachment…"),
342 	  NULL,
343 	  N_("Attach a file"),
344 	  G_CALLBACK (action_add_cb) },
345 
346 	{ "properties",
347 	  "document-properties",
348 	  N_("_Properties"),
349 	  NULL,
350 	  NULL,  /* XXX Add a tooltip! */
351 	  G_CALLBACK (action_properties_cb) },
352 
353 	{ "remove",
354 	  "list-remove",
355 	  N_("_Remove"),
356 	  NULL,
357 	  NULL,  /* XXX Add a tooltip! */
358 	  G_CALLBACK (action_remove_cb) }
359 };
360 
361 static void
call_attachment_load_handle_error(GObject * source_object,GAsyncResult * result,gpointer user_data)362 call_attachment_load_handle_error (GObject *source_object,
363 				   GAsyncResult *result,
364 				   gpointer user_data)
365 {
366 	GtkWindow *window = user_data;
367 
368 	g_return_if_fail (E_IS_ATTACHMENT (source_object));
369 	g_return_if_fail (!window || GTK_IS_WINDOW (window));
370 
371 	e_attachment_load_handle_error (E_ATTACHMENT (source_object), result, window);
372 
373 	g_clear_object (&window);
374 }
375 
376 static void
attachment_view_netscape_url(EAttachmentView * view,GdkDragContext * drag_context,gint x,gint y,GtkSelectionData * selection_data,guint info,guint time)377 attachment_view_netscape_url (EAttachmentView *view,
378                               GdkDragContext *drag_context,
379                               gint x,
380                               gint y,
381                               GtkSelectionData *selection_data,
382                               guint info,
383                               guint time)
384 {
385 	static GdkAtom atom = GDK_NONE;
386 	EAttachmentStore *store;
387 	EAttachment *attachment;
388 	const gchar *data;
389 	gpointer parent;
390 	gchar *copied_data;
391 	gchar **strv;
392 	gint length;
393 
394 	if (G_UNLIKELY (atom == GDK_NONE))
395 		atom = gdk_atom_intern_static_string ("_NETSCAPE_URL");
396 
397 	if (gtk_selection_data_get_target (selection_data) != atom)
398 		return;
399 
400 	g_signal_stop_emission_by_name (view, "drag-data-received");
401 
402 	/* _NETSCAPE_URL is represented as "URI\nTITLE" */
403 
404 	data = (const gchar *) gtk_selection_data_get_data (selection_data);
405 	length = gtk_selection_data_get_length (selection_data);
406 
407 	copied_data = g_strndup (data, length);
408 	strv = g_strsplit (copied_data, "\n", 2);
409 	g_free (copied_data);
410 
411 	store = e_attachment_view_get_store (view);
412 
413 	parent = gtk_widget_get_toplevel (GTK_WIDGET (view));
414 	parent = gtk_widget_is_toplevel (parent) ? parent : NULL;
415 
416 	attachment = e_attachment_new_for_uri (strv[0]);
417 	e_attachment_store_add_attachment (store, attachment);
418 	e_attachment_load_async (
419 		attachment, (GAsyncReadyCallback)
420 		call_attachment_load_handle_error, parent ? g_object_ref (parent) : NULL);
421 	g_object_unref (attachment);
422 
423 	g_strfreev (strv);
424 
425 	gtk_drag_finish (drag_context, TRUE, FALSE, time);
426 }
427 
428 static void
attachment_view_uri_list(EAttachmentView * view,GdkDragContext * drag_context,gint x,gint y,GtkSelectionData * selection_data,guint info,guint time)429 attachment_view_uri_list (EAttachmentView *view,
430                           GdkDragContext *drag_context,
431                           gint x,
432                           gint y,
433                           GtkSelectionData *selection_data,
434                           guint info,
435                           guint time)
436 {
437 	static GdkAtom atom = GDK_NONE;
438 	EAttachmentStore *store;
439 	EAttachment *attachment;
440 	const gchar *data;
441 	gpointer parent;
442 	gint length = 0, list_length = 0, uri_length = 0;
443 	gchar *uri;
444 
445 
446 	if (G_UNLIKELY (atom == GDK_NONE))
447 		atom = gdk_atom_intern_static_string ("text/uri-list");
448 
449 	if (gtk_selection_data_get_target (selection_data) != atom)
450 		return;
451 
452 	g_signal_stop_emission_by_name (view, "drag-data-received");
453 
454 	data = (const gchar *) gtk_selection_data_get_data (selection_data);
455 	length = gtk_selection_data_get_length (selection_data);
456 
457 	if (!data || length < 0) {
458 		gtk_drag_finish (drag_context, FALSE, FALSE, time);
459 		return;
460 	}
461 
462 	store = e_attachment_view_get_store (view);
463 
464 	parent = gtk_widget_get_toplevel (GTK_WIDGET (view));
465 	parent = gtk_widget_is_toplevel (parent) ? parent : NULL;
466 
467 	list_length = length;
468 	do {
469 		uri = e_util_next_uri_from_uri_list ((guchar **) &data, &uri_length, &list_length);
470 
471 		if (strstr (uri, ";base64,")) {
472 			/* base64 encoded data */
473 			CamelMimePart *mime_part;
474 			gchar *mime_type = NULL, *filename = NULL;
475 			guchar *base64_data;
476 			gsize base64_data_length;
477 
478 			if (g_str_has_prefix (uri, "data:")) {
479 				const gchar *base64 = strstr (uri, ";") + 1;
480 				/* strlen ("data:") == 5 */
481 				mime_type = g_strndup (uri + 5, base64 - uri - 5 - 1);
482 
483 				base64 = strstr (base64, ",") + 1;
484 				base64_data = g_base64_decode (base64, &base64_data_length);
485 			} else if (strstr (uri, ";data")) {
486 				/* CID attachment from mail preview that has
487 				 * the filename prefixed before the base64 data -
488 				 * see EMailDisplay. */
489 				const gchar *base64 = strstr (uri, ";") + 1;
490 				glong filename_length, mime_type_length, base64_length;
491 
492 				base64_length = g_utf8_strlen (base64, -1);
493 
494 				filename_length = uri_length - base64_length - 1;
495 				filename = g_strndup (uri, filename_length);
496 
497 				/* strlen ("data:") == 5 */
498 				mime_type_length = base64_length - g_utf8_strlen (strstr (base64, ";"), -1) - 5;
499 				mime_type = g_strndup (uri + filename_length + 5 + 1, mime_type_length);
500 
501 				base64 = strstr (base64, ",") + 1;
502 				base64_data = g_base64_decode (base64, &base64_data_length);
503 			} else {
504 				g_free (uri);
505 				gtk_drag_finish (drag_context, FALSE, FALSE, time);
506 				return;
507 			}
508 
509 			mime_part = camel_mime_part_new ();
510 
511 			camel_mime_part_set_content (mime_part, (const gchar *) base64_data, base64_data_length, mime_type);
512 			camel_mime_part_set_disposition (mime_part, "inline");
513 			if (filename && *filename)
514 				camel_mime_part_set_filename (mime_part, filename);
515 			camel_mime_part_set_encoding (mime_part, CAMEL_TRANSFER_ENCODING_BASE64);
516 
517 			attachment = e_attachment_new ();
518 			e_attachment_set_mime_part (attachment, mime_part);
519 			e_attachment_store_add_attachment (store, attachment);
520 			e_attachment_load_async (
521 				attachment, (GAsyncReadyCallback)
522 				call_attachment_load_handle_error, parent ? g_object_ref (parent) : NULL);
523 
524 			g_object_unref (attachment);
525 			g_object_unref (mime_part);
526 			g_free (mime_type);
527 			g_free (filename);
528 			g_free (base64_data);
529 		} else {
530 			/* regular URIs */
531 			attachment = e_attachment_new_for_uri (uri);
532 			e_attachment_store_add_attachment (store, attachment);
533 			e_attachment_load_async (
534 				attachment, (GAsyncReadyCallback)
535 				call_attachment_load_handle_error, parent ? g_object_ref (parent) : NULL);
536 			g_object_unref (attachment);
537 		}
538 
539 		g_free (uri);
540 	} while (list_length);
541 
542 	gtk_drag_finish (drag_context, TRUE, FALSE, time);
543 }
544 
545 static void
attachment_view_text_calendar(EAttachmentView * view,GdkDragContext * drag_context,gint x,gint y,GtkSelectionData * selection_data,guint info,guint time)546 attachment_view_text_calendar (EAttachmentView *view,
547                                GdkDragContext *drag_context,
548                                gint x,
549                                gint y,
550                                GtkSelectionData *selection_data,
551                                guint info,
552                                guint time)
553 {
554 	EAttachmentStore *store;
555 	EAttachment *attachment;
556 	CamelMimePart *mime_part;
557 	GdkAtom data_type;
558 	GdkAtom target;
559 	const gchar *data;
560 	gpointer parent;
561 	gchar *content_type;
562 	gint length;
563 
564 	target = gtk_selection_data_get_target (selection_data);
565 	if (!e_targets_include_calendar (&target, 1))
566 		return;
567 
568 	g_signal_stop_emission_by_name (view, "drag-data-received");
569 
570 	data = (const gchar *) gtk_selection_data_get_data (selection_data);
571 	length = gtk_selection_data_get_length (selection_data);
572 	data_type = gtk_selection_data_get_data_type (selection_data);
573 
574 	mime_part = camel_mime_part_new ();
575 
576 	content_type = gdk_atom_name (data_type);
577 	camel_mime_part_set_content (mime_part, data, length, content_type);
578 	camel_mime_part_set_disposition (mime_part, "inline");
579 	g_free (content_type);
580 
581 	store = e_attachment_view_get_store (view);
582 
583 	parent = gtk_widget_get_toplevel (GTK_WIDGET (view));
584 	parent = gtk_widget_is_toplevel (parent) ? parent : NULL;
585 
586 	attachment = e_attachment_new ();
587 	e_attachment_set_mime_part (attachment, mime_part);
588 	e_attachment_store_add_attachment (store, attachment);
589 	e_attachment_load_async (
590 		attachment, (GAsyncReadyCallback)
591 		call_attachment_load_handle_error, parent ? g_object_ref (parent) : NULL);
592 	g_object_unref (attachment);
593 
594 	g_object_unref (mime_part);
595 
596 	gtk_drag_finish (drag_context, TRUE, FALSE, time);
597 }
598 
599 static void
attachment_view_text_x_vcard(EAttachmentView * view,GdkDragContext * drag_context,gint x,gint y,GtkSelectionData * selection_data,guint info,guint time)600 attachment_view_text_x_vcard (EAttachmentView *view,
601                               GdkDragContext *drag_context,
602                               gint x,
603                               gint y,
604                               GtkSelectionData *selection_data,
605                               guint info,
606                               guint time)
607 {
608 	EAttachmentStore *store;
609 	EAttachment *attachment;
610 	CamelMimePart *mime_part;
611 	GdkAtom data_type;
612 	GdkAtom target;
613 	const gchar *data;
614 	gpointer parent;
615 	gchar *content_type;
616 	gint length;
617 
618 	target = gtk_selection_data_get_target (selection_data);
619 	if (!e_targets_include_directory (&target, 1))
620 		return;
621 
622 	g_signal_stop_emission_by_name (view, "drag-data-received");
623 
624 	data = (const gchar *) gtk_selection_data_get_data (selection_data);
625 	length = gtk_selection_data_get_length (selection_data);
626 	data_type = gtk_selection_data_get_data_type (selection_data);
627 
628 	mime_part = camel_mime_part_new ();
629 
630 	content_type = gdk_atom_name (data_type);
631 	camel_mime_part_set_content (mime_part, data, length, content_type);
632 	camel_mime_part_set_disposition (mime_part, "inline");
633 	g_free (content_type);
634 
635 	store = e_attachment_view_get_store (view);
636 
637 	parent = gtk_widget_get_toplevel (GTK_WIDGET (view));
638 	parent = gtk_widget_is_toplevel (parent) ? parent : NULL;
639 
640 	attachment = e_attachment_new ();
641 	e_attachment_set_mime_part (attachment, mime_part);
642 	e_attachment_store_add_attachment (store, attachment);
643 	e_attachment_load_async (
644 		attachment, (GAsyncReadyCallback)
645 		call_attachment_load_handle_error, parent ? g_object_ref (parent) : NULL);
646 	g_object_unref (attachment);
647 
648 	g_object_unref (mime_part);
649 
650 	gtk_drag_finish (drag_context, TRUE, FALSE, time);
651 }
652 
653 static void
attachment_view_uris(EAttachmentView * view,GdkDragContext * drag_context,gint x,gint y,GtkSelectionData * selection_data,guint info,guint time)654 attachment_view_uris (EAttachmentView *view,
655                       GdkDragContext *drag_context,
656                       gint x,
657                       gint y,
658                       GtkSelectionData *selection_data,
659                       guint info,
660                       guint time)
661 {
662 	EAttachmentStore *store;
663 	gpointer parent;
664 	gchar **uris;
665 	gint ii;
666 
667 	uris = gtk_selection_data_get_uris (selection_data);
668 
669 	if (uris == NULL)
670 		return;
671 
672 	g_signal_stop_emission_by_name (view, "drag-data-received");
673 
674 	store = e_attachment_view_get_store (view);
675 
676 	parent = gtk_widget_get_toplevel (GTK_WIDGET (view));
677 	parent = gtk_widget_is_toplevel (parent) ? parent : NULL;
678 
679 	for (ii = 0; uris[ii] != NULL; ii++) {
680 		EAttachment *attachment;
681 
682 		attachment = e_attachment_new_for_uri (uris[ii]);
683 		e_attachment_store_add_attachment (store, attachment);
684 		e_attachment_load_async (
685 			attachment, (GAsyncReadyCallback)
686 			call_attachment_load_handle_error, parent ? g_object_ref (parent) : NULL);
687 		g_object_unref (attachment);
688 	}
689 
690 	g_strfreev (uris);
691 
692 	gtk_drag_finish (drag_context, TRUE, FALSE, time);
693 }
694 
695 static void
attachment_view_update_actions(EAttachmentView * view)696 attachment_view_update_actions (EAttachmentView *view)
697 {
698 	EAttachmentViewPrivate *priv;
699 	EAttachment *attachment;
700 	GtkActionGroup *action_group;
701 	GtkAction *action;
702 	GList *list, *iter;
703 	guint n_selected;
704 	gboolean busy = FALSE;
705 
706 	g_return_if_fail (E_IS_ATTACHMENT_VIEW (view));
707 
708 	priv = e_attachment_view_get_private (view);
709 
710 	list = e_attachment_view_get_selected_attachments (view);
711 	n_selected = g_list_length (list);
712 
713 	if (n_selected == 1) {
714 		attachment = g_object_ref (list->data);
715 
716 		busy |= e_attachment_get_loading (attachment);
717 		busy |= e_attachment_get_saving (attachment);
718 	} else
719 		attachment = NULL;
720 
721 	g_list_free_full (list, g_object_unref);
722 
723 	action = e_attachment_view_get_action (view, "cancel");
724 	gtk_action_set_visible (action, busy);
725 
726 	action = e_attachment_view_get_action (view, "open-with");
727 	gtk_action_set_visible (action, !busy && n_selected == 1 && !e_util_is_running_flatpak ());
728 
729 	action = e_attachment_view_get_action (view, "properties");
730 	gtk_action_set_visible (action, !busy && n_selected == 1);
731 
732 	action = e_attachment_view_get_action (view, "remove");
733 	gtk_action_set_visible (action, !busy && n_selected > 0);
734 
735 	action = e_attachment_view_get_action (view, "save-as");
736 	gtk_action_set_visible (action, !busy && n_selected > 0);
737 
738 	/* Clear out the "openwith" action group. */
739 	gtk_ui_manager_remove_ui (priv->ui_manager, priv->merge_id);
740 	action_group = e_attachment_view_get_action_group (view, "openwith");
741 	e_action_group_remove_all_actions (action_group);
742 	gtk_ui_manager_ensure_update (priv->ui_manager);
743 
744 	if (!attachment || busy) {
745 		g_clear_object (&attachment);
746 		return;
747 	}
748 
749 	list = e_attachment_list_apps (attachment);
750 
751 	if (!list && e_util_is_running_flatpak ())
752 		list = g_list_prepend (list, NULL);
753 
754 	for (iter = list; iter != NULL; iter = iter->next) {
755 		GAppInfo *app_info = iter->data;
756 		GtkAction *action;
757 		GIcon *app_icon;
758 		const gchar *app_id;
759 		const gchar *app_name;
760 		gchar *action_tooltip;
761 		gchar *action_label;
762 		gchar *action_name;
763 
764 		if (app_info) {
765 			app_id = g_app_info_get_id (app_info);
766 			app_icon = g_app_info_get_icon (app_info);
767 			app_name = g_app_info_get_name (app_info);
768 		} else {
769 			app_id = "org.gnome.evolution.flatpak.default-app";
770 			app_icon = NULL;
771 			app_name = NULL;
772 		}
773 
774 		if (app_id == NULL)
775 			continue;
776 
777 		/* Don't list 'Open With "Evolution"'. */
778 		if (g_str_equal (app_id, "org.gnome.Evolution.desktop"))
779 			continue;
780 
781 		action_name = g_strdup_printf ("open-with-%s", app_id);
782 
783 		if (app_info) {
784 			action_label = g_strdup_printf (_("Open With “%s”"), app_name);
785 			action_tooltip = g_strdup_printf (_("Open this attachment in %s"), app_name);
786 		} else {
787 			action_label = g_strdup (_("Open With Default Application"));
788 			action_tooltip = g_strdup (_("Open this attachment in default application"));
789 		}
790 
791 		action = gtk_action_new (
792 			action_name, action_label, action_tooltip, NULL);
793 
794 		gtk_action_set_gicon (action, app_icon);
795 
796 		if (app_info) {
797 			g_object_set_data_full (
798 				G_OBJECT (action),
799 				"app-info", g_object_ref (app_info),
800 				(GDestroyNotify) g_object_unref);
801 		}
802 
803 		g_object_set_data_full (
804 			G_OBJECT (action),
805 			"attachment", g_object_ref (attachment),
806 			(GDestroyNotify) g_object_unref);
807 
808 		g_signal_connect (
809 			action, "activate",
810 			G_CALLBACK (action_open_with_app_info_cb), view);
811 
812 		gtk_action_group_add_action (action_group, action);
813 
814 		gtk_ui_manager_add_ui (
815 			priv->ui_manager, priv->merge_id,
816 			"/context/open-actions", action_name,
817 			action_name, GTK_UI_MANAGER_AUTO, FALSE);
818 
819 		g_free (action_name);
820 		g_free (action_label);
821 		g_free (action_tooltip);
822 
823 		if (!app_info) {
824 			list = g_list_remove (list, app_info);
825 			break;
826 		}
827 	}
828 
829 	g_list_free_full (list, g_object_unref);
830 	g_object_unref (attachment);
831 }
832 
833 static void
attachment_view_init_drag_dest(EAttachmentView * view)834 attachment_view_init_drag_dest (EAttachmentView *view)
835 {
836 	EAttachmentViewPrivate *priv;
837 	GtkTargetList *target_list;
838 
839 	priv = e_attachment_view_get_private (view);
840 
841 	target_list = gtk_target_list_new (
842 		target_table, G_N_ELEMENTS (target_table));
843 
844 	gtk_target_list_add_uri_targets (target_list, 0);
845 	e_target_list_add_calendar_targets (target_list, 0);
846 	e_target_list_add_directory_targets (target_list, 0);
847 
848 	priv->target_list = target_list;
849 	priv->drag_actions = GDK_ACTION_COPY;
850 }
851 
852 static void
e_attachment_view_default_init(EAttachmentViewInterface * iface)853 e_attachment_view_default_init (EAttachmentViewInterface *iface)
854 {
855 	iface->update_actions = attachment_view_update_actions;
856 
857 	g_object_interface_install_property (
858 		iface,
859 		g_param_spec_boolean (
860 			"dragging",
861 			"Dragging",
862 			NULL,
863 			FALSE,
864 			G_PARAM_READWRITE));
865 
866 	g_object_interface_install_property (
867 		iface,
868 		g_param_spec_boolean (
869 			"editable",
870 			"Editable",
871 			NULL,
872 			TRUE,
873 			G_PARAM_READWRITE |
874 			G_PARAM_CONSTRUCT));
875 
876 	signals[UPDATE_ACTIONS] = g_signal_new (
877 		"update-actions",
878 		G_TYPE_FROM_INTERFACE (iface),
879 		G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
880 		G_STRUCT_OFFSET (EAttachmentViewInterface, update_actions),
881 		NULL, NULL,
882 		g_cclosure_marshal_VOID__VOID,
883 		G_TYPE_NONE, 0);
884 
885 	/* Register known handler types. */
886 	g_type_ensure (E_TYPE_ATTACHMENT_HANDLER_IMAGE);
887 }
888 
889 void
e_attachment_view_init(EAttachmentView * view)890 e_attachment_view_init (EAttachmentView *view)
891 {
892 	EAttachmentViewPrivate *priv;
893 	GtkUIManager *ui_manager;
894 	GtkActionGroup *action_group;
895 	GError *error = NULL;
896 
897 	priv = e_attachment_view_get_private (view);
898 
899 	ui_manager = gtk_ui_manager_new ();
900 	priv->merge_id = gtk_ui_manager_new_merge_id (ui_manager);
901 	priv->ui_manager = ui_manager;
902 
903 	action_group = e_attachment_view_add_action_group (view, "standard");
904 
905 	gtk_action_group_add_actions (
906 		action_group, standard_entries,
907 		G_N_ELEMENTS (standard_entries), view);
908 
909 	action_group = e_attachment_view_add_action_group (view, "editable");
910 
911 	e_binding_bind_property (
912 		view, "editable",
913 		action_group, "visible",
914 		G_BINDING_BIDIRECTIONAL |
915 		G_BINDING_SYNC_CREATE);
916 	gtk_action_group_add_actions (
917 		action_group, editable_entries,
918 		G_N_ELEMENTS (editable_entries), view);
919 
920 	e_attachment_view_add_action_group (view, "openwith");
921 
922 	/* Because we are loading from a hard-coded string, there is
923 	 * no chance of I/O errors.  Failure here implies a malformed
924 	 * UI definition.  Full stop. */
925 	gtk_ui_manager_add_ui_from_string (ui_manager, ui, -1, &error);
926 	if (error != NULL)
927 		g_error ("%s", error->message);
928 
929 	attachment_view_init_drag_dest (view);
930 
931 	e_attachment_view_drag_source_set (view);
932 
933 	/* Connect built-in drag and drop handlers. */
934 
935 	g_signal_connect (
936 		view, "drag-data-received",
937 		G_CALLBACK (attachment_view_netscape_url), NULL);
938 
939 	g_signal_connect (
940 		view, "drag-data-received",
941 		G_CALLBACK (attachment_view_text_calendar), NULL);
942 
943 	g_signal_connect (
944 		view, "drag-data-received",
945 		G_CALLBACK (attachment_view_uri_list), NULL);
946 
947 	g_signal_connect (
948 		view, "drag-data-received",
949 		G_CALLBACK (attachment_view_text_x_vcard), NULL);
950 
951 	g_signal_connect (
952 		view, "drag-data-received",
953 		G_CALLBACK (attachment_view_uris), NULL);
954 }
955 
956 void
e_attachment_view_dispose(EAttachmentView * view)957 e_attachment_view_dispose (EAttachmentView *view)
958 {
959 	EAttachmentViewPrivate *priv;
960 
961 	priv = e_attachment_view_get_private (view);
962 
963 	g_clear_pointer (&priv->target_list, gtk_target_list_unref);
964 	g_clear_object (&priv->ui_manager);
965 }
966 
967 void
e_attachment_view_finalize(EAttachmentView * view)968 e_attachment_view_finalize (EAttachmentView *view)
969 {
970 	EAttachmentViewPrivate *priv;
971 
972 	priv = e_attachment_view_get_private (view);
973 
974 	g_list_foreach (priv->event_list, (GFunc) gdk_event_free, NULL);
975 	g_list_free (priv->event_list);
976 
977 	g_list_foreach (priv->selected, (GFunc) g_object_unref, NULL);
978 	g_list_free (priv->selected);
979 }
980 
981 EAttachmentViewPrivate *
e_attachment_view_get_private(EAttachmentView * view)982 e_attachment_view_get_private (EAttachmentView *view)
983 {
984 	EAttachmentViewInterface *iface;
985 
986 	g_return_val_if_fail (E_IS_ATTACHMENT_VIEW (view), NULL);
987 
988 	iface = E_ATTACHMENT_VIEW_GET_INTERFACE (view);
989 	g_return_val_if_fail (iface->get_private != NULL, NULL);
990 
991 	return iface->get_private (view);
992 }
993 
994 EAttachmentStore *
e_attachment_view_get_store(EAttachmentView * view)995 e_attachment_view_get_store (EAttachmentView *view)
996 {
997 	EAttachmentViewInterface *iface;
998 
999 	g_return_val_if_fail (E_IS_ATTACHMENT_VIEW (view), NULL);
1000 
1001 	iface = E_ATTACHMENT_VIEW_GET_INTERFACE (view);
1002 	g_return_val_if_fail (iface->get_store != NULL, NULL);
1003 
1004 	return iface->get_store (view);
1005 }
1006 
1007 gboolean
e_attachment_view_get_editable(EAttachmentView * view)1008 e_attachment_view_get_editable (EAttachmentView *view)
1009 {
1010 	EAttachmentViewPrivate *priv;
1011 
1012 	g_return_val_if_fail (E_IS_ATTACHMENT_VIEW (view), FALSE);
1013 
1014 	priv = e_attachment_view_get_private (view);
1015 
1016 	return priv->editable;
1017 }
1018 
1019 void
e_attachment_view_set_editable(EAttachmentView * view,gboolean editable)1020 e_attachment_view_set_editable (EAttachmentView *view,
1021                                 gboolean editable)
1022 {
1023 	EAttachmentViewPrivate *priv;
1024 
1025 	g_return_if_fail (E_IS_ATTACHMENT_VIEW (view));
1026 
1027 	priv = e_attachment_view_get_private (view);
1028 
1029 	priv->editable = editable;
1030 
1031 	if (editable)
1032 		e_attachment_view_drag_dest_set (view);
1033 	else
1034 		e_attachment_view_drag_dest_unset (view);
1035 
1036 	g_object_notify (G_OBJECT (view), "editable");
1037 }
1038 
1039 gboolean
e_attachment_view_get_dragging(EAttachmentView * view)1040 e_attachment_view_get_dragging (EAttachmentView *view)
1041 {
1042 	EAttachmentViewPrivate *priv;
1043 
1044 	g_return_val_if_fail (E_IS_ATTACHMENT_VIEW (view), FALSE);
1045 
1046 	priv = e_attachment_view_get_private (view);
1047 
1048 	return priv->dragging;
1049 }
1050 
1051 void
e_attachment_view_set_dragging(EAttachmentView * view,gboolean dragging)1052 e_attachment_view_set_dragging (EAttachmentView *view,
1053                                 gboolean dragging)
1054 {
1055 	EAttachmentViewPrivate *priv;
1056 
1057 	g_return_if_fail (E_IS_ATTACHMENT_VIEW (view));
1058 
1059 	priv = e_attachment_view_get_private (view);
1060 
1061 	priv->dragging = dragging;
1062 
1063 	g_object_notify (G_OBJECT (view), "dragging");
1064 }
1065 
1066 GtkTargetList *
e_attachment_view_get_target_list(EAttachmentView * view)1067 e_attachment_view_get_target_list (EAttachmentView *view)
1068 {
1069 	EAttachmentViewPrivate *priv;
1070 
1071 	g_return_val_if_fail (E_IS_ATTACHMENT_VIEW (view), NULL);
1072 
1073 	priv = e_attachment_view_get_private (view);
1074 
1075 	return priv->target_list;
1076 }
1077 
1078 GdkDragAction
e_attachment_view_get_drag_actions(EAttachmentView * view)1079 e_attachment_view_get_drag_actions (EAttachmentView *view)
1080 {
1081 	EAttachmentViewPrivate *priv;
1082 
1083 	g_return_val_if_fail (E_IS_ATTACHMENT_VIEW (view), 0);
1084 
1085 	priv = e_attachment_view_get_private (view);
1086 
1087 	return priv->drag_actions;
1088 }
1089 
1090 void
e_attachment_view_add_drag_actions(EAttachmentView * view,GdkDragAction drag_actions)1091 e_attachment_view_add_drag_actions (EAttachmentView *view,
1092                                     GdkDragAction drag_actions)
1093 {
1094 	EAttachmentViewPrivate *priv;
1095 
1096 	g_return_if_fail (E_IS_ATTACHMENT_VIEW (view));
1097 
1098 	priv = e_attachment_view_get_private (view);
1099 
1100 	priv->drag_actions |= drag_actions;
1101 }
1102 
1103 GList *
e_attachment_view_get_selected_attachments(EAttachmentView * view)1104 e_attachment_view_get_selected_attachments (EAttachmentView *view)
1105 {
1106 	EAttachmentStore *store;
1107 	GtkTreeModel *model;
1108 	GList *list, *item;
1109 	gint column_id;
1110 
1111 	g_return_val_if_fail (E_IS_ATTACHMENT_VIEW (view), NULL);
1112 
1113 	column_id = E_ATTACHMENT_STORE_COLUMN_ATTACHMENT;
1114 	list = e_attachment_view_get_selected_paths (view);
1115 	store = e_attachment_view_get_store (view);
1116 	model = GTK_TREE_MODEL (store);
1117 
1118 	/* Convert the GtkTreePaths to EAttachments. */
1119 	for (item = list; item != NULL; item = item->next) {
1120 		EAttachment *attachment;
1121 		GtkTreePath *path;
1122 		GtkTreeIter iter;
1123 
1124 		path = item->data;
1125 
1126 		gtk_tree_model_get_iter (model, &iter, path);
1127 		gtk_tree_model_get (model, &iter, column_id, &attachment, -1);
1128 		gtk_tree_path_free (path);
1129 
1130 		item->data = attachment;
1131 	}
1132 
1133 	return list;
1134 }
1135 
1136 void
e_attachment_view_open_path(EAttachmentView * view,GtkTreePath * path,GAppInfo * app_info)1137 e_attachment_view_open_path (EAttachmentView *view,
1138                              GtkTreePath *path,
1139                              GAppInfo *app_info)
1140 {
1141 	EAttachmentStore *store;
1142 	EAttachment *attachment;
1143 	GtkTreeModel *model;
1144 	GtkTreeIter iter;
1145 	gboolean iter_valid;
1146 	gpointer parent;
1147 	gint column_id;
1148 
1149 	g_return_if_fail (E_IS_ATTACHMENT_VIEW (view));
1150 	g_return_if_fail (path != NULL);
1151 
1152 	column_id = E_ATTACHMENT_STORE_COLUMN_ATTACHMENT;
1153 	store = e_attachment_view_get_store (view);
1154 	model = GTK_TREE_MODEL (store);
1155 
1156 	iter_valid = gtk_tree_model_get_iter (model, &iter, path);
1157 	g_return_if_fail (iter_valid);
1158 
1159 	gtk_tree_model_get (model, &iter, column_id, &attachment, -1);
1160 
1161 	parent = gtk_widget_get_toplevel (GTK_WIDGET (view));
1162 	parent = gtk_widget_is_toplevel (parent) ? parent : NULL;
1163 
1164 	e_attachment_open_async (
1165 		attachment, app_info, (GAsyncReadyCallback)
1166 		e_attachment_open_handle_error, parent);
1167 
1168 	g_object_unref (attachment);
1169 }
1170 
1171 void
e_attachment_view_remove_selected(EAttachmentView * view,gboolean select_next)1172 e_attachment_view_remove_selected (EAttachmentView *view,
1173                                    gboolean select_next)
1174 {
1175 	EAttachmentStore *store;
1176 	GtkTreeModel *model;
1177 	GList *list, *item;
1178 	gint column_id;
1179 
1180 	g_return_if_fail (E_IS_ATTACHMENT_VIEW (view));
1181 
1182 	column_id = E_ATTACHMENT_STORE_COLUMN_ATTACHMENT;
1183 	list = e_attachment_view_get_selected_paths (view);
1184 	store = e_attachment_view_get_store (view);
1185 	model = GTK_TREE_MODEL (store);
1186 
1187 	/* Remove attachments in reverse order to avoid invalidating
1188 	 * tree paths as we iterate over the list.  Note, the list is
1189 	 * probably already sorted but we sort again just to be safe. */
1190 	list = g_list_reverse (g_list_sort (
1191 		list, (GCompareFunc) gtk_tree_path_compare));
1192 
1193 	for (item = list; item != NULL; item = item->next) {
1194 		EAttachment *attachment;
1195 		GtkTreePath *path = item->data;
1196 		GtkTreeIter iter;
1197 
1198 		gtk_tree_model_get_iter (model, &iter, path);
1199 		gtk_tree_model_get (model, &iter, column_id, &attachment, -1);
1200 		e_attachment_store_remove_attachment (store, attachment);
1201 		g_object_unref (attachment);
1202 	}
1203 
1204 	/* If we only removed one attachment, try to select another. */
1205 	if (select_next && list && list->data && !list->next) {
1206 		GtkTreePath *path = list->data;
1207 
1208 		e_attachment_view_select_path (view, path);
1209 		if (!e_attachment_view_path_is_selected (view, path))
1210 			if (gtk_tree_path_prev (path))
1211 				e_attachment_view_select_path (view, path);
1212 	}
1213 
1214 	g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL);
1215 	g_list_free (list);
1216 }
1217 
1218 static gboolean
attachment_view_any_popup_item_visible(GtkWidget * widget)1219 attachment_view_any_popup_item_visible (GtkWidget *widget)
1220 {
1221 	GList *items, *link;
1222 	gboolean any_visible = FALSE;
1223 
1224 	g_return_val_if_fail (GTK_IS_MENU (widget), FALSE);
1225 
1226 	items = gtk_container_get_children (GTK_CONTAINER (widget));
1227 	for (link = items; link && !any_visible; link = g_list_next (link)) {
1228 		any_visible = gtk_widget_get_visible (link->data);
1229 	}
1230 
1231 	g_list_free (items);
1232 
1233 	return any_visible;
1234 }
1235 
1236 gboolean
e_attachment_view_button_press_event(EAttachmentView * view,GdkEventButton * event)1237 e_attachment_view_button_press_event (EAttachmentView *view,
1238                                       GdkEventButton *event)
1239 {
1240 	EAttachmentViewPrivate *priv;
1241 	GtkTreePath *path;
1242 	GtkWidget *menu;
1243 	gboolean editable;
1244 	gboolean handled = FALSE;
1245 	gboolean path_is_selected = FALSE;
1246 
1247 	g_return_val_if_fail (E_IS_ATTACHMENT_VIEW (view), FALSE);
1248 	g_return_val_if_fail (event != NULL, FALSE);
1249 
1250 	priv = e_attachment_view_get_private (view);
1251 
1252 	if (g_list_find (priv->event_list, event) != NULL)
1253 		return FALSE;
1254 
1255 	if (priv->event_list != NULL) {
1256 		/* Save the event to be propagated in order. */
1257 		priv->event_list = g_list_append (
1258 			priv->event_list,
1259 			gdk_event_copy ((GdkEvent *) event));
1260 		return TRUE;
1261 	}
1262 
1263 	editable = e_attachment_view_get_editable (view);
1264 	path = e_attachment_view_get_path_at_pos (view, event->x, event->y);
1265 	path_is_selected = e_attachment_view_path_is_selected (view, path);
1266 
1267 	if (event->button == 1 && event->type == GDK_BUTTON_PRESS) {
1268 		GList *list, *iter;
1269 		gboolean busy = FALSE;
1270 
1271 		list = e_attachment_view_get_selected_attachments (view);
1272 
1273 		for (iter = list; iter != NULL; iter = iter->next) {
1274 			EAttachment *attachment = iter->data;
1275 			busy |= e_attachment_get_loading (attachment);
1276 			busy |= e_attachment_get_saving (attachment);
1277 		}
1278 
1279 		/* Prepare for dragging if the clicked item is selected
1280 		 * and none of the selected items are loading or saving. */
1281 		if (path_is_selected && !busy) {
1282 			priv->start_x = event->x;
1283 			priv->start_y = event->y;
1284 			priv->event_list = g_list_append (
1285 				priv->event_list,
1286 				gdk_event_copy ((GdkEvent *) event));
1287 			handled = TRUE;
1288 		}
1289 
1290 		g_list_foreach (list, (GFunc) g_object_unref, NULL);
1291 		g_list_free (list);
1292 	}
1293 
1294 	if (event->button == 3 && event->type == GDK_BUTTON_PRESS) {
1295 		/* If the user clicked on a selected item, retain the
1296 		 * current selection.  If the user clicked on an unselected
1297 		 * item, select the clicked item only.  If the user did not
1298 		 * click on an item, clear the current selection. */
1299 		if (path == NULL)
1300 			e_attachment_view_unselect_all (view);
1301 		else if (!path_is_selected) {
1302 			e_attachment_view_unselect_all (view);
1303 			e_attachment_view_select_path (view, path);
1304 		}
1305 
1306 		/* Non-editable attachment views should only show a
1307 		 * popup menu when right-clicking on an attachment,
1308 		 * but editable views can show the menu any time. */
1309 		if (path != NULL || editable) {
1310 			e_attachment_view_update_actions (view);
1311 			menu = e_attachment_view_get_popup_menu (view);
1312 			if (attachment_view_any_popup_item_visible (menu))
1313 				gtk_menu_popup_at_pointer (GTK_MENU (menu), (const GdkEvent *) event);
1314 			else
1315 				g_signal_emit_by_name (menu, "deactivate", NULL);
1316 			handled = TRUE;
1317 		}
1318 	}
1319 
1320 	if (path != NULL)
1321 		gtk_tree_path_free (path);
1322 
1323 	return handled;
1324 }
1325 
1326 gboolean
e_attachment_view_button_release_event(EAttachmentView * view,GdkEventButton * event)1327 e_attachment_view_button_release_event (EAttachmentView *view,
1328                                         GdkEventButton *event)
1329 {
1330 	EAttachmentViewPrivate *priv;
1331 	GtkWidget *widget = GTK_WIDGET (view);
1332 	GList *iter;
1333 
1334 	g_return_val_if_fail (E_IS_ATTACHMENT_VIEW (view), FALSE);
1335 	g_return_val_if_fail (event != NULL, FALSE);
1336 
1337 	priv = e_attachment_view_get_private (view);
1338 
1339 	for (iter = priv->event_list; iter != NULL; iter = iter->next) {
1340 		GdkEvent *an_event = iter->data;
1341 
1342 		gtk_propagate_event (widget, an_event);
1343 		gdk_event_free (an_event);
1344 	}
1345 
1346 	g_list_free (priv->event_list);
1347 	priv->event_list = NULL;
1348 
1349 	return FALSE;
1350 }
1351 
1352 gboolean
e_attachment_view_motion_notify_event(EAttachmentView * view,GdkEventMotion * event)1353 e_attachment_view_motion_notify_event (EAttachmentView *view,
1354                                        GdkEventMotion *event)
1355 {
1356 	EAttachmentViewPrivate *priv;
1357 	GtkWidget *widget = GTK_WIDGET (view);
1358 	GtkTargetList *targets;
1359 
1360 	g_return_val_if_fail (E_IS_ATTACHMENT_VIEW (view), FALSE);
1361 	g_return_val_if_fail (event != NULL, FALSE);
1362 
1363 	priv = e_attachment_view_get_private (view);
1364 
1365 	if (priv->event_list == NULL)
1366 		return FALSE;
1367 
1368 	if (!gtk_drag_check_threshold (
1369 		widget, priv->start_x, priv->start_y, event->x, event->y))
1370 		return TRUE;
1371 
1372 	g_list_foreach (priv->event_list, (GFunc) gdk_event_free, NULL);
1373 	g_list_free (priv->event_list);
1374 	priv->event_list = NULL;
1375 
1376 	targets = gtk_drag_source_get_target_list (widget);
1377 
1378 	gtk_drag_begin (
1379 		widget, targets, GDK_ACTION_COPY, 1, (GdkEvent *) event);
1380 
1381 	return TRUE;
1382 }
1383 
1384 gboolean
e_attachment_view_key_press_event(EAttachmentView * view,GdkEventKey * event)1385 e_attachment_view_key_press_event (EAttachmentView *view,
1386                                    GdkEventKey *event)
1387 {
1388 	gboolean editable;
1389 
1390 	g_return_val_if_fail (E_IS_ATTACHMENT_VIEW (view), FALSE);
1391 	g_return_val_if_fail (event != NULL, FALSE);
1392 
1393 	editable = e_attachment_view_get_editable (view);
1394 
1395 	if (event->keyval == GDK_KEY_Delete && editable) {
1396 		e_attachment_view_remove_selected (view, TRUE);
1397 		return TRUE;
1398 	}
1399 
1400 	return FALSE;
1401 }
1402 
1403 GtkTreePath *
e_attachment_view_get_path_at_pos(EAttachmentView * view,gint x,gint y)1404 e_attachment_view_get_path_at_pos (EAttachmentView *view,
1405                                    gint x,
1406                                    gint y)
1407 {
1408 	EAttachmentViewInterface *iface;
1409 
1410 	g_return_val_if_fail (E_IS_ATTACHMENT_VIEW (view), NULL);
1411 
1412 	iface = E_ATTACHMENT_VIEW_GET_INTERFACE (view);
1413 	g_return_val_if_fail (iface->get_path_at_pos != NULL, NULL);
1414 
1415 	return iface->get_path_at_pos (view, x, y);
1416 }
1417 
1418 GList *
e_attachment_view_get_selected_paths(EAttachmentView * view)1419 e_attachment_view_get_selected_paths (EAttachmentView *view)
1420 {
1421 	EAttachmentViewInterface *iface;
1422 
1423 	g_return_val_if_fail (E_IS_ATTACHMENT_VIEW (view), NULL);
1424 
1425 	iface = E_ATTACHMENT_VIEW_GET_INTERFACE (view);
1426 	g_return_val_if_fail (iface->get_selected_paths != NULL, NULL);
1427 
1428 	return iface->get_selected_paths (view);
1429 }
1430 
1431 gboolean
e_attachment_view_path_is_selected(EAttachmentView * view,GtkTreePath * path)1432 e_attachment_view_path_is_selected (EAttachmentView *view,
1433                                     GtkTreePath *path)
1434 {
1435 	EAttachmentViewInterface *iface;
1436 
1437 	g_return_val_if_fail (E_IS_ATTACHMENT_VIEW (view), FALSE);
1438 
1439 	/* Handle NULL paths gracefully. */
1440 	if (path == NULL)
1441 		return FALSE;
1442 
1443 	iface = E_ATTACHMENT_VIEW_GET_INTERFACE (view);
1444 	g_return_val_if_fail (iface->path_is_selected != NULL, FALSE);
1445 
1446 	return iface->path_is_selected (view, path);
1447 }
1448 
1449 void
e_attachment_view_select_path(EAttachmentView * view,GtkTreePath * path)1450 e_attachment_view_select_path (EAttachmentView *view,
1451                                GtkTreePath *path)
1452 {
1453 	EAttachmentViewInterface *iface;
1454 
1455 	g_return_if_fail (E_IS_ATTACHMENT_VIEW (view));
1456 	g_return_if_fail (path != NULL);
1457 
1458 	iface = E_ATTACHMENT_VIEW_GET_INTERFACE (view);
1459 	g_return_if_fail (iface->select_path != NULL);
1460 
1461 	iface->select_path (view, path);
1462 }
1463 
1464 void
e_attachment_view_unselect_path(EAttachmentView * view,GtkTreePath * path)1465 e_attachment_view_unselect_path (EAttachmentView *view,
1466                                  GtkTreePath *path)
1467 {
1468 	EAttachmentViewInterface *iface;
1469 
1470 	g_return_if_fail (E_IS_ATTACHMENT_VIEW (view));
1471 	g_return_if_fail (path != NULL);
1472 
1473 	iface = E_ATTACHMENT_VIEW_GET_INTERFACE (view);
1474 	g_return_if_fail (iface->unselect_path != NULL);
1475 
1476 	iface->unselect_path (view, path);
1477 }
1478 
1479 void
e_attachment_view_select_all(EAttachmentView * view)1480 e_attachment_view_select_all (EAttachmentView *view)
1481 {
1482 	EAttachmentViewInterface *iface;
1483 
1484 	g_return_if_fail (E_IS_ATTACHMENT_VIEW (view));
1485 
1486 	iface = E_ATTACHMENT_VIEW_GET_INTERFACE (view);
1487 	g_return_if_fail (iface->select_all != NULL);
1488 
1489 	iface->select_all (view);
1490 }
1491 
1492 void
e_attachment_view_unselect_all(EAttachmentView * view)1493 e_attachment_view_unselect_all (EAttachmentView *view)
1494 {
1495 	EAttachmentViewInterface *iface;
1496 
1497 	g_return_if_fail (E_IS_ATTACHMENT_VIEW (view));
1498 
1499 	iface = E_ATTACHMENT_VIEW_GET_INTERFACE (view);
1500 	g_return_if_fail (iface->unselect_all != NULL);
1501 
1502 	iface->unselect_all (view);
1503 }
1504 
1505 void
e_attachment_view_sync_selection(EAttachmentView * view,EAttachmentView * target)1506 e_attachment_view_sync_selection (EAttachmentView *view,
1507                                   EAttachmentView *target)
1508 {
1509 	GList *list, *iter;
1510 
1511 	g_return_if_fail (E_IS_ATTACHMENT_VIEW (view));
1512 	g_return_if_fail (E_IS_ATTACHMENT_VIEW (target));
1513 
1514 	list = e_attachment_view_get_selected_paths (view);
1515 	e_attachment_view_unselect_all (target);
1516 
1517 	for (iter = list; iter != NULL; iter = iter->next)
1518 		e_attachment_view_select_path (target, iter->data);
1519 
1520 	g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL);
1521 	g_list_free (list);
1522 }
1523 
1524 void
e_attachment_view_drag_source_set(EAttachmentView * view)1525 e_attachment_view_drag_source_set (EAttachmentView *view)
1526 {
1527 	EAttachmentViewInterface *iface;
1528 	GtkTargetEntry *targets;
1529 	GtkTargetList *list;
1530 	gint n_targets;
1531 
1532 	g_return_if_fail (E_IS_ATTACHMENT_VIEW (view));
1533 
1534 	iface = E_ATTACHMENT_VIEW_GET_INTERFACE (view);
1535 	if (iface->drag_source_set == NULL)
1536 		return;
1537 
1538 	list = gtk_target_list_new (NULL, 0);
1539 	gtk_target_list_add_uri_targets (list, 0);
1540 	targets = gtk_target_table_new_from_list (list, &n_targets);
1541 
1542 	iface->drag_source_set (
1543 		view, GDK_BUTTON1_MASK,
1544 		targets, n_targets, GDK_ACTION_COPY);
1545 
1546 	gtk_target_table_free (targets, n_targets);
1547 	gtk_target_list_unref (list);
1548 }
1549 
1550 void
e_attachment_view_drag_source_unset(EAttachmentView * view)1551 e_attachment_view_drag_source_unset (EAttachmentView *view)
1552 {
1553 	EAttachmentViewInterface *iface;
1554 
1555 	g_return_if_fail (E_IS_ATTACHMENT_VIEW (view));
1556 
1557 	iface = E_ATTACHMENT_VIEW_GET_INTERFACE (view);
1558 	if (iface->drag_source_unset == NULL)
1559 		return;
1560 
1561 	iface->drag_source_unset (view);
1562 }
1563 
1564 void
e_attachment_view_drag_begin(EAttachmentView * view,GdkDragContext * context)1565 e_attachment_view_drag_begin (EAttachmentView *view,
1566                               GdkDragContext *context)
1567 {
1568 	EAttachmentViewPrivate *priv;
1569 	guint n_selected;
1570 
1571 	g_return_if_fail (E_IS_ATTACHMENT_VIEW (view));
1572 	g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
1573 
1574 	priv = e_attachment_view_get_private (view);
1575 
1576 	e_attachment_view_set_dragging (view, TRUE);
1577 
1578 	g_warn_if_fail (priv->selected == NULL);
1579 	priv->selected = e_attachment_view_get_selected_attachments (view);
1580 	n_selected = g_list_length (priv->selected);
1581 
1582 	if (n_selected == 1) {
1583 		EAttachment *attachment;
1584 		GtkIconTheme *icon_theme;
1585 		GtkIconInfo *icon_info;
1586 		GIcon *icon;
1587 		gint width, height;
1588 
1589 		attachment = E_ATTACHMENT (priv->selected->data);
1590 		icon = e_attachment_ref_icon (attachment);
1591 		g_return_if_fail (icon != NULL);
1592 
1593 		icon_theme = gtk_icon_theme_get_default ();
1594 		gtk_icon_size_lookup (GTK_ICON_SIZE_DND, &width, &height);
1595 
1596 		icon_info = gtk_icon_theme_lookup_by_gicon (
1597 			icon_theme, icon, MIN (width, height),
1598 			GTK_ICON_LOOKUP_USE_BUILTIN);
1599 
1600 		if (icon_info != NULL) {
1601 			GdkPixbuf *pixbuf;
1602 			GError *error = NULL;
1603 
1604 			pixbuf = gtk_icon_info_load_icon (icon_info, &error);
1605 
1606 			if (pixbuf != NULL) {
1607 				gtk_drag_set_icon_pixbuf (
1608 					context, pixbuf, 0, 0);
1609 				g_object_unref (pixbuf);
1610 			} else if (error != NULL) {
1611 				g_warning ("%s", error->message);
1612 				g_error_free (error);
1613 			}
1614 
1615 			gtk_icon_info_free (icon_info);
1616 		}
1617 
1618 		g_object_unref (icon);
1619 	}
1620 }
1621 
1622 void
e_attachment_view_drag_end(EAttachmentView * view,GdkDragContext * context)1623 e_attachment_view_drag_end (EAttachmentView *view,
1624                             GdkDragContext *context)
1625 {
1626 	EAttachmentViewPrivate *priv;
1627 
1628 	g_return_if_fail (E_IS_ATTACHMENT_VIEW (view));
1629 	g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
1630 
1631 	priv = e_attachment_view_get_private (view);
1632 
1633 	e_attachment_view_set_dragging (view, FALSE);
1634 
1635 	g_list_foreach (priv->selected, (GFunc) g_object_unref, NULL);
1636 	g_list_free (priv->selected);
1637 	priv->selected = NULL;
1638 }
1639 
1640 static void
attachment_view_got_uris_cb(EAttachmentStore * store,GAsyncResult * result,gpointer user_data)1641 attachment_view_got_uris_cb (EAttachmentStore *store,
1642                              GAsyncResult *result,
1643                              gpointer user_data)
1644 {
1645 	struct {
1646 		gchar **uris;
1647 		gboolean done;
1648 	} *status = user_data;
1649 
1650 	/* XXX Since this is a best-effort function,
1651 	 *     should we care about errors? */
1652 	status->uris = e_attachment_store_get_uris_finish (
1653 		store, result, NULL);
1654 
1655 	status->done = TRUE;
1656 }
1657 
1658 void
e_attachment_view_drag_data_get(EAttachmentView * view,GdkDragContext * context,GtkSelectionData * selection,guint info,guint time)1659 e_attachment_view_drag_data_get (EAttachmentView *view,
1660                                  GdkDragContext *context,
1661                                  GtkSelectionData *selection,
1662                                  guint info,
1663                                  guint time)
1664 {
1665 	EAttachmentViewPrivate *priv;
1666 	EAttachmentStore *store;
1667 
1668 	struct {
1669 		gchar **uris;
1670 		gboolean done;
1671 	} status;
1672 
1673 	g_return_if_fail (E_IS_ATTACHMENT_VIEW (view));
1674 	g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
1675 	g_return_if_fail (selection != NULL);
1676 
1677 	status.uris = NULL;
1678 	status.done = FALSE;
1679 
1680 	priv = e_attachment_view_get_private (view);
1681 	store = e_attachment_view_get_store (view);
1682 
1683 	if (priv->selected == NULL)
1684 		return;
1685 
1686 	e_attachment_store_get_uris_async (
1687 		store, priv->selected, (GAsyncReadyCallback)
1688 		attachment_view_got_uris_cb, &status);
1689 
1690 	/* We can't return until we have results, so crank
1691 	 * the main loop until the callback gets triggered. */
1692 	while (!status.done)
1693 		if (gtk_main_iteration ())
1694 			break;
1695 
1696 	if (status.uris != NULL)
1697 		gtk_selection_data_set_uris (selection, status.uris);
1698 
1699 	g_strfreev (status.uris);
1700 }
1701 
1702 void
e_attachment_view_drag_dest_set(EAttachmentView * view)1703 e_attachment_view_drag_dest_set (EAttachmentView *view)
1704 {
1705 	EAttachmentViewPrivate *priv;
1706 	EAttachmentViewInterface *iface;
1707 	GtkTargetEntry *targets;
1708 	gint n_targets;
1709 
1710 	g_return_if_fail (E_IS_ATTACHMENT_VIEW (view));
1711 
1712 	iface = E_ATTACHMENT_VIEW_GET_INTERFACE (view);
1713 	if (iface->drag_dest_set == NULL)
1714 		return;
1715 
1716 	priv = e_attachment_view_get_private (view);
1717 
1718 	targets = gtk_target_table_new_from_list (
1719 		priv->target_list, &n_targets);
1720 
1721 	iface->drag_dest_set (
1722 		view, targets, n_targets, priv->drag_actions);
1723 
1724 	gtk_target_table_free (targets, n_targets);
1725 }
1726 
1727 void
e_attachment_view_drag_dest_unset(EAttachmentView * view)1728 e_attachment_view_drag_dest_unset (EAttachmentView *view)
1729 {
1730 	EAttachmentViewInterface *iface;
1731 
1732 	g_return_if_fail (E_IS_ATTACHMENT_VIEW (view));
1733 
1734 	iface = E_ATTACHMENT_VIEW_GET_INTERFACE (view);
1735 	if (iface->drag_dest_unset == NULL)
1736 		return;
1737 
1738 	iface->drag_dest_unset (view);
1739 }
1740 
1741 gboolean
e_attachment_view_drag_motion(EAttachmentView * view,GdkDragContext * context,gint x,gint y,guint time)1742 e_attachment_view_drag_motion (EAttachmentView *view,
1743                                GdkDragContext *context,
1744                                gint x,
1745                                gint y,
1746                                guint time)
1747 {
1748 	EAttachmentViewPrivate *priv;
1749 	GdkDragAction actions;
1750 	GdkDragAction chosen_action;
1751 
1752 	g_return_val_if_fail (E_IS_ATTACHMENT_VIEW (view), FALSE);
1753 	g_return_val_if_fail (GDK_IS_DRAG_CONTEXT (context), FALSE);
1754 
1755 	priv = e_attachment_view_get_private (view);
1756 
1757 	/* Disallow drops if we're not editable. */
1758 	if (!e_attachment_view_get_editable (view))
1759 		return FALSE;
1760 
1761 	/* Disallow drops if we initiated the drag.
1762 	 * This helps prevent duplicate attachments. */
1763 	if (e_attachment_view_get_dragging (view))
1764 		return FALSE;
1765 
1766 	actions = gdk_drag_context_get_actions (context);
1767 	actions &= priv->drag_actions;
1768 	chosen_action = gdk_drag_context_get_suggested_action (context);
1769 
1770 	if (chosen_action == GDK_ACTION_ASK) {
1771 		GdkDragAction mask;
1772 
1773 		mask = GDK_ACTION_COPY | GDK_ACTION_MOVE;
1774 		if ((actions & mask) != mask)
1775 			chosen_action = GDK_ACTION_COPY;
1776 	}
1777 
1778 	gdk_drag_status (context, chosen_action, time);
1779 
1780 	return (chosen_action != 0);
1781 }
1782 
1783 gboolean
e_attachment_view_drag_drop(EAttachmentView * view,GdkDragContext * context,gint x,gint y,guint time)1784 e_attachment_view_drag_drop (EAttachmentView *view,
1785                              GdkDragContext *context,
1786                              gint x,
1787                              gint y,
1788                              guint time)
1789 {
1790 	g_return_val_if_fail (E_IS_ATTACHMENT_VIEW (view), FALSE);
1791 	g_return_val_if_fail (GDK_IS_DRAG_CONTEXT (context), FALSE);
1792 
1793 	/* Disallow drops if we initiated the drag.
1794 	 * This helps prevent duplicate attachments. */
1795 	return !e_attachment_view_get_dragging (view);
1796 }
1797 
1798 void
e_attachment_view_drag_data_received(EAttachmentView * view,GdkDragContext * drag_context,gint x,gint y,GtkSelectionData * selection_data,guint info,guint time)1799 e_attachment_view_drag_data_received (EAttachmentView *view,
1800                                       GdkDragContext *drag_context,
1801                                       gint x,
1802                                       gint y,
1803                                       GtkSelectionData *selection_data,
1804                                       guint info,
1805                                       guint time)
1806 {
1807 	GdkAtom atom;
1808 	gchar *name;
1809 
1810 	g_return_if_fail (E_IS_ATTACHMENT_VIEW (view));
1811 	g_return_if_fail (GDK_IS_DRAG_CONTEXT (drag_context));
1812 
1813 	/* Drop handlers are supposed to stop further emission of the
1814 	 * "drag-data-received" signal if they can handle the data.  If
1815 	 * we get this far it means none of the handlers were successful,
1816 	 * so report the drop as failed. */
1817 
1818 	atom = gtk_selection_data_get_target (selection_data);
1819 
1820 	name = gdk_atom_name (atom);
1821 	g_warning ("Unknown selection target: %s", name);
1822 	g_free (name);
1823 
1824 	gtk_drag_finish (drag_context, FALSE, FALSE, time);
1825 }
1826 
1827 GtkAction *
e_attachment_view_get_action(EAttachmentView * view,const gchar * action_name)1828 e_attachment_view_get_action (EAttachmentView *view,
1829                               const gchar *action_name)
1830 {
1831 	GtkUIManager *ui_manager;
1832 
1833 	g_return_val_if_fail (E_IS_ATTACHMENT_VIEW (view), NULL);
1834 	g_return_val_if_fail (action_name != NULL, NULL);
1835 
1836 	ui_manager = e_attachment_view_get_ui_manager (view);
1837 
1838 	return e_lookup_action (ui_manager, action_name);
1839 }
1840 
1841 GtkActionGroup *
e_attachment_view_add_action_group(EAttachmentView * view,const gchar * group_name)1842 e_attachment_view_add_action_group (EAttachmentView *view,
1843                                     const gchar *group_name)
1844 {
1845 	GtkActionGroup *action_group;
1846 	GtkUIManager *ui_manager;
1847 	const gchar *domain;
1848 
1849 	g_return_val_if_fail (E_IS_ATTACHMENT_VIEW (view), NULL);
1850 	g_return_val_if_fail (group_name != NULL, NULL);
1851 
1852 	ui_manager = e_attachment_view_get_ui_manager (view);
1853 	domain = GETTEXT_PACKAGE;
1854 
1855 	action_group = gtk_action_group_new (group_name);
1856 	gtk_action_group_set_translation_domain (action_group, domain);
1857 	gtk_ui_manager_insert_action_group (ui_manager, action_group, 0);
1858 	g_object_unref (action_group);
1859 
1860 	return action_group;
1861 }
1862 
1863 GtkActionGroup *
e_attachment_view_get_action_group(EAttachmentView * view,const gchar * group_name)1864 e_attachment_view_get_action_group (EAttachmentView *view,
1865                                     const gchar *group_name)
1866 {
1867 	GtkUIManager *ui_manager;
1868 
1869 	g_return_val_if_fail (E_IS_ATTACHMENT_VIEW (view), NULL);
1870 	g_return_val_if_fail (group_name != NULL, NULL);
1871 
1872 	ui_manager = e_attachment_view_get_ui_manager (view);
1873 
1874 	return e_lookup_action_group (ui_manager, group_name);
1875 }
1876 
1877 static void
e_attachment_view_menu_deactivate_cb(GtkMenu * popup_menu,gpointer user_data)1878 e_attachment_view_menu_deactivate_cb (GtkMenu *popup_menu,
1879 				      gpointer user_data)
1880 {
1881 	g_return_if_fail (GTK_IS_MENU (popup_menu));
1882 
1883 	g_signal_handlers_disconnect_by_func (popup_menu, e_attachment_view_menu_deactivate_cb, user_data);
1884 	gtk_menu_detach (popup_menu);
1885 }
1886 
1887 GtkWidget *
e_attachment_view_get_popup_menu(EAttachmentView * view)1888 e_attachment_view_get_popup_menu (EAttachmentView *view)
1889 {
1890 	GtkUIManager *ui_manager;
1891 	GtkWidget *menu;
1892 
1893 	g_return_val_if_fail (E_IS_ATTACHMENT_VIEW (view), NULL);
1894 
1895 	ui_manager = e_attachment_view_get_ui_manager (view);
1896 	menu = gtk_ui_manager_get_widget (ui_manager, "/context");
1897 	g_return_val_if_fail (GTK_IS_MENU (menu), NULL);
1898 
1899 	if (!gtk_menu_get_attach_widget (GTK_MENU (menu))) {
1900 		gtk_menu_attach_to_widget (GTK_MENU (menu),
1901 					   GTK_WIDGET (view),
1902 					   NULL);
1903 		g_signal_connect (
1904 			menu, "deactivate",
1905 			G_CALLBACK (e_attachment_view_menu_deactivate_cb), NULL);
1906 	}
1907 
1908 	return menu;
1909 }
1910 
1911 GtkUIManager *
e_attachment_view_get_ui_manager(EAttachmentView * view)1912 e_attachment_view_get_ui_manager (EAttachmentView *view)
1913 {
1914 	EAttachmentViewPrivate *priv;
1915 
1916 	g_return_val_if_fail (E_IS_ATTACHMENT_VIEW (view), NULL);
1917 
1918 	priv = e_attachment_view_get_private (view);
1919 
1920 	return priv->ui_manager;
1921 }
1922 
1923 void
e_attachment_view_update_actions(EAttachmentView * view)1924 e_attachment_view_update_actions (EAttachmentView *view)
1925 {
1926 	g_return_if_fail (E_IS_ATTACHMENT_VIEW (view));
1927 
1928 	g_signal_emit (view, signals[UPDATE_ACTIONS], 0);
1929 }
1930