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