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