Evince
Evince is a document viewer capable of displaying multiple and single page document formats like PDF and Postscript.
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
ev-sidebar-attachments.c
Go to the documentation of this file.
1 /* ev-sidebar-attachments.c
2  * this file is part of evince, a gnome document viewer
3  *
4  * Copyright (C) 2006 Carlos Garcia Campos
5  *
6  * Author:
7  * Carlos Garcia Campos <carlosgc@gnome.org>
8  *
9  * Evince is free software; you can redistribute it and/or modify it
10  * under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * Evince is distributed in the hope that it will be useful, but
15  * WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17  * General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22  */
23 
24 #ifdef HAVE_CONFIG_H
25 #include "config.h"
26 #endif
27 
28 #include <string.h>
29 
30 #include <glib/gi18n.h>
31 #include <glib/gstdio.h>
32 #include <gtk/gtk.h>
33 
35 #include "ev-document-misc.h"
36 #include "ev-jobs.h"
37 #include "ev-job-scheduler.h"
38 #include "ev-file-helpers.h"
39 #include "ev-sidebar-attachments.h"
40 #include "ev-sidebar-page.h"
41 
42 enum {
48 };
49 
50 enum {
53 };
54 
55 enum {
58 };
59 
60 static guint signals[N_SIGNALS];
61 
63  GtkWidget *icon_view;
64  GtkListStore *model;
65 
66  /* Icons */
67  GtkIconTheme *icon_theme;
68  GHashTable *icon_cache;
69 };
70 
72 
74  ev_sidebar_attachments,
75  GTK_TYPE_BOX,
76  0,
77  G_IMPLEMENT_INTERFACE (EV_TYPE_SIDEBAR_PAGE,
79 
80 #define EV_SIDEBAR_ATTACHMENTS_GET_PRIVATE(object) \
81  (G_TYPE_INSTANCE_GET_PRIVATE ((object), EV_TYPE_SIDEBAR_ATTACHMENTS, EvSidebarAttachmentsPrivate))
82 
83 /* Icon cache */
84 static void
85 ev_sidebar_attachments_icon_cache_add (EvSidebarAttachments *ev_attachbar,
86  const gchar *mime_type,
87  const GdkPixbuf *pixbuf)
88 {
89  g_assert (mime_type != NULL);
90  g_assert (GDK_IS_PIXBUF (pixbuf));
91 
92  g_hash_table_insert (ev_attachbar->priv->icon_cache,
93  (gpointer)g_strdup (mime_type),
94  (gpointer)pixbuf);
95 
96 }
97 
98 static GdkPixbuf *
99 icon_theme_get_pixbuf_from_mime_type (GtkIconTheme *icon_theme,
100  const gchar *mime_type)
101 {
102  const char *separator;
103  GString *icon_name;
104  GdkPixbuf *pixbuf;
105 
106  separator = strchr (mime_type, '/');
107  if (!separator)
108  return NULL; /* maybe we should return a GError with "invalid MIME-type" */
109 
110  icon_name = g_string_new ("gnome-mime-");
111  g_string_append_len (icon_name, mime_type, separator - mime_type);
112  g_string_append_c (icon_name, '-');
113  g_string_append (icon_name, separator + 1);
114  pixbuf = gtk_icon_theme_load_icon (icon_theme, icon_name->str, 48, 0, NULL);
115  g_string_free (icon_name, TRUE);
116  if (pixbuf)
117  return pixbuf;
118 
119  icon_name = g_string_new ("gnome-mime-");
120  g_string_append_len (icon_name, mime_type, separator - mime_type);
121  pixbuf = gtk_icon_theme_load_icon (icon_theme, icon_name->str, 48, 0, NULL);
122  g_string_free (icon_name, TRUE);
123 
124  return pixbuf;
125 }
126 
127 static GdkPixbuf *
129  const gchar *mime_type)
130 {
131  GdkPixbuf *pixbuf = NULL;
132 
133  g_assert (mime_type != NULL);
134 
135  pixbuf = g_hash_table_lookup (ev_attachbar->priv->icon_cache,
136  mime_type);
137 
138  if (GDK_IS_PIXBUF (pixbuf))
139  return pixbuf;
140 
141  pixbuf = icon_theme_get_pixbuf_from_mime_type (ev_attachbar->priv->icon_theme,
142  mime_type);
143 
144  if (GDK_IS_PIXBUF (pixbuf))
145  ev_sidebar_attachments_icon_cache_add (ev_attachbar,
146  mime_type,
147  pixbuf);
148 
149  return pixbuf;
150 }
151 
152 static gboolean
154  GdkPixbuf *value,
155  EvSidebarAttachments *ev_attachbar)
156 {
157  GdkPixbuf *pixbuf = NULL;
158 
159  pixbuf = icon_theme_get_pixbuf_from_mime_type (ev_attachbar->priv->icon_theme,
160  key);
161 
162  ev_sidebar_attachments_icon_cache_add (ev_attachbar,
163  key,
164  pixbuf);
165 
166  return FALSE;
167 }
168 
169 static void
171 {
172  g_hash_table_foreach_remove (ev_attachbar->priv->icon_cache,
173  (GHRFunc) icon_cache_update_icon,
174  ev_attachbar);
175 }
176 
177 static EvAttachment *
179  gint x,
180  gint y)
181 {
182  GtkTreePath *path = NULL;
183  GtkTreeIter iter;
184  EvAttachment *attachment = NULL;
185 
186  path = gtk_icon_view_get_path_at_pos (GTK_ICON_VIEW (ev_attachbar->priv->icon_view),
187  x, y);
188  if (!path) {
189  return NULL;
190  }
191 
192  gtk_tree_model_get_iter (GTK_TREE_MODEL (ev_attachbar->priv->model),
193  &iter, path);
194  gtk_tree_model_get (GTK_TREE_MODEL (ev_attachbar->priv->model), &iter,
195  COLUMN_ATTACHMENT, &attachment,
196  -1);
197 
198  gtk_icon_view_select_path (GTK_ICON_VIEW (ev_attachbar->priv->icon_view),
199  path);
200 
201  gtk_tree_path_free (path);
202 
203  return attachment;
204 }
205 
206 static gboolean
208  gint x,
209  gint y)
210 {
211  GtkIconView *icon_view;
212  GtkTreePath *path;
213  GList *selected = NULL, *l;
214  GList *attach_list = NULL;
215 
216  icon_view = GTK_ICON_VIEW (ev_attachbar->priv->icon_view);
217 
218  path = gtk_icon_view_get_path_at_pos (icon_view, x, y);
219  if (!path)
220  return FALSE;
221 
222  if (!gtk_icon_view_path_is_selected (icon_view, path)) {
223  gtk_icon_view_unselect_all (icon_view);
224  gtk_icon_view_select_path (icon_view, path);
225  }
226 
227  gtk_tree_path_free (path);
228 
229  selected = gtk_icon_view_get_selected_items (icon_view);
230  if (!selected)
231  return FALSE;
232 
233  for (l = selected; l && l->data; l = g_list_next (l)) {
234  GtkTreeIter iter;
235  EvAttachment *attachment = NULL;
236 
237  path = (GtkTreePath *) l->data;
238 
239  gtk_tree_model_get_iter (GTK_TREE_MODEL (ev_attachbar->priv->model),
240  &iter, path);
241  gtk_tree_model_get (GTK_TREE_MODEL (ev_attachbar->priv->model), &iter,
242  COLUMN_ATTACHMENT, &attachment,
243  -1);
244 
245  if (attachment)
246  attach_list = g_list_prepend (attach_list, attachment);
247 
248  gtk_tree_path_free (path);
249  }
250 
251  g_list_free (selected);
252 
253  if (!attach_list)
254  return FALSE;
255 
256  g_signal_emit (ev_attachbar, signals[SIGNAL_POPUP_MENU], 0, attach_list);
257 
258  return TRUE;
259 }
260 
261 static gboolean
263 {
264  EvSidebarAttachments *ev_attachbar = EV_SIDEBAR_ATTACHMENTS (widget);
265  gint x, y;
266 
267  ev_document_misc_get_pointer_position (widget, &x, &y);
268 
269  return ev_sidebar_attachments_popup_menu_show (ev_attachbar, x, y);
270 }
271 
272 static gboolean
274  GdkEventButton *event,
275  GtkWidget *icon_view)
276 {
277  if (!gtk_widget_has_focus (icon_view)) {
278  gtk_widget_grab_focus (icon_view);
279  }
280 
281  if (event->button == 2)
282  return FALSE;
283 
284  switch (event->button) {
285  case 1:
286  if (event->type == GDK_2BUTTON_PRESS) {
287  GError *error = NULL;
288  EvAttachment *attachment;
289 
290  attachment = ev_sidebar_attachments_get_attachment_at_pos (ev_attachbar,
291  event->x,
292  event->y);
293  if (!attachment)
294  return FALSE;
295 
296  ev_attachment_open (attachment,
297  gtk_widget_get_screen (GTK_WIDGET (ev_attachbar)),
298  event->time,
299  &error);
300 
301  if (error) {
302  g_warning ("%s", error->message);
303  g_error_free (error);
304  }
305 
306  g_object_unref (attachment);
307 
308  return TRUE;
309  }
310  break;
311  case 3:
312  return ev_sidebar_attachments_popup_menu_show (ev_attachbar, event->x, event->y);
313  }
314 
315  return FALSE;
316 }
317 
318 static void
320  gpointer user_data)
321 {
322  GtkTreeIter iter;
323  gboolean valid;
324 
326 
327  valid = gtk_tree_model_get_iter_first (
328  GTK_TREE_MODEL (ev_attachbar->priv->model),
329  &iter);
330 
331  while (valid) {
332  EvAttachment *attachment = NULL;
333  GdkPixbuf *pixbuf = NULL;
334  const gchar *mime_type;
335 
336  gtk_tree_model_get (GTK_TREE_MODEL (ev_attachbar->priv->model), &iter,
337  COLUMN_ATTACHMENT, &attachment,
338  -1);
339 
340  mime_type = ev_attachment_get_mime_type (attachment);
341 
342  if (attachment)
343  g_object_unref (attachment);
344 
345  pixbuf = ev_sidebar_attachments_icon_cache_get (ev_attachbar,
346  mime_type);
347 
348  gtk_list_store_set (ev_attachbar->priv->model, &iter,
349  COLUMN_ICON, pixbuf,
350  -1);
351 
352  valid = gtk_tree_model_iter_next (
353  GTK_TREE_MODEL (ev_attachbar->priv->model),
354  &iter);
355  }
356 }
357 
358 static void
360  GdkScreen *old_screen)
361 {
362  EvSidebarAttachments *ev_attachbar = EV_SIDEBAR_ATTACHMENTS (widget);
363  GdkScreen *screen;
364 
365  if (!ev_attachbar->priv->icon_theme)
366  return;
367 
368  screen = gtk_widget_get_screen (widget);
369  if (screen == old_screen)
370  return;
371 
372  if (old_screen) {
373  g_signal_handlers_disconnect_by_func (
374  gtk_icon_theme_get_for_screen (old_screen),
376  ev_attachbar);
377  }
378 
379  ev_attachbar->priv->icon_theme = gtk_icon_theme_get_for_screen (screen);
380  g_signal_connect_swapped (ev_attachbar->priv->icon_theme,
381  "changed",
383  (gpointer) ev_attachbar);
384 
385  if (GTK_WIDGET_CLASS (ev_sidebar_attachments_parent_class)->screen_changed) {
386  GTK_WIDGET_CLASS (ev_sidebar_attachments_parent_class)->screen_changed (widget, old_screen);
387  }
388 }
389 
390 static void
392  GdkDragContext *drag_context,
393  GtkSelectionData *data,
394  guint info,
395  guint time,
396  gpointer user_data)
397 {
398  EvSidebarAttachments *ev_attachbar = EV_SIDEBAR_ATTACHMENTS (user_data);
399  GList *selected = NULL, *l;
400  GPtrArray *uris;
401  char **uri_list;
402 
403  selected = gtk_icon_view_get_selected_items (GTK_ICON_VIEW (ev_attachbar->priv->icon_view));
404  if (!selected)
405  return;
406 
407  uris = g_ptr_array_new ();
408 
409  for (l = selected; l && l->data; l = g_list_next (l)) {
410  EvAttachment *attachment;
411  GtkTreePath *path;
412  GtkTreeIter iter;
413  GFile *file;
414  gchar *template;
415  GError *error = NULL;
416 
417  path = (GtkTreePath *) l->data;
418 
419  gtk_tree_model_get_iter (GTK_TREE_MODEL (ev_attachbar->priv->model),
420  &iter, path);
421  gtk_tree_model_get (GTK_TREE_MODEL (ev_attachbar->priv->model), &iter,
422  COLUMN_ATTACHMENT, &attachment,
423  -1);
424 
425  /* FIXMEchpe: convert to filename encoding first! */
426  template = g_strdup_printf ("%s.XXXXXX", ev_attachment_get_name (attachment));
427  file = ev_mkstemp_file (template, &error);
428  g_free (template);
429 
430  if (file != NULL && ev_attachment_save (attachment, file, &error)) {
431  gchar *uri;
432 
433  uri = g_file_get_uri (file);
434  g_ptr_array_add (uris, uri);
435  }
436 
437  if (error) {
438  g_warning ("%s", error->message);
439  g_error_free (error);
440  }
441 
442  gtk_tree_path_free (path);
443  g_object_unref (file);
444  g_object_unref (attachment);
445  }
446 
447  g_ptr_array_add (uris, NULL); /* NULL-terminate */
448  uri_list = (char **) g_ptr_array_free (uris, FALSE);
449  gtk_selection_data_set_uris (data, uri_list);
450  g_strfreev (uri_list);
451 
452  g_list_free (selected);
453 }
454 
455 static void
457  guint prop_id,
458  GValue *value,
459  GParamSpec *pspec)
460 {
461  EvSidebarAttachments *ev_sidebar_attachments;
462 
463  ev_sidebar_attachments = EV_SIDEBAR_ATTACHMENTS (object);
464 
465  switch (prop_id) {
466  case PROP_WIDGET:
467  g_value_set_object (value, ev_sidebar_attachments->priv->icon_view);
468  break;
469  default:
470  G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
471  break;
472  }
473 }
474 
475 static void
477 {
478  EvSidebarAttachments *ev_attachbar = EV_SIDEBAR_ATTACHMENTS (object);
479 
480  if (ev_attachbar->priv->icon_theme) {
481  g_signal_handlers_disconnect_by_func (
482  ev_attachbar->priv->icon_theme,
484  ev_attachbar);
485  ev_attachbar->priv->icon_theme = NULL;
486  }
487 
488  if (ev_attachbar->priv->model) {
489  g_object_unref (ev_attachbar->priv->model);
490  ev_attachbar->priv->model = NULL;
491  }
492 
493  if (ev_attachbar->priv->icon_cache) {
494  g_hash_table_destroy (ev_attachbar->priv->icon_cache);
495  ev_attachbar->priv->icon_cache = NULL;
496  }
497 
498  G_OBJECT_CLASS (ev_sidebar_attachments_parent_class)->dispose (object);
499 }
500 
501 static void
503 {
504  GObjectClass *g_object_class;
505  GtkWidgetClass *gtk_widget_class;
506 
507  g_object_class = G_OBJECT_CLASS (ev_attachbar_class);
508  gtk_widget_class = GTK_WIDGET_CLASS (ev_attachbar_class);
509 
510  g_object_class->get_property = ev_sidebar_attachments_get_property;
511  g_object_class->dispose = ev_sidebar_attachments_dispose;
512  gtk_widget_class->popup_menu = ev_sidebar_attachments_popup_menu;
513  gtk_widget_class->screen_changed = ev_sidebar_attachments_screen_changed;
514 
515  g_type_class_add_private (g_object_class, sizeof (EvSidebarAttachmentsPrivate));
516 
517  /* Signals */
519  g_signal_new ("popup",
520  G_TYPE_FROM_CLASS (g_object_class),
521  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
522  G_STRUCT_OFFSET (EvSidebarAttachmentsClass, popup_menu),
523  NULL, NULL,
524  g_cclosure_marshal_VOID__POINTER,
525  G_TYPE_NONE, 1,
526  G_TYPE_POINTER);
527 
528  g_object_class_override_property (g_object_class,
529  PROP_WIDGET,
530  "main-widget");
531 }
532 
533 static void
535 {
536  GtkWidget *swindow;
537 
538  ev_attachbar->priv = EV_SIDEBAR_ATTACHMENTS_GET_PRIVATE (ev_attachbar);
539 
540  swindow = gtk_scrolled_window_new (NULL, NULL);
541  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (swindow),
542  GTK_POLICY_NEVER,
543  GTK_POLICY_AUTOMATIC);
544  /* Data Model */
545  ev_attachbar->priv->model = gtk_list_store_new (N_COLS,
546  GDK_TYPE_PIXBUF,
547  G_TYPE_STRING,
548  G_TYPE_STRING,
550 
551  /* Icon View */
552  ev_attachbar->priv->icon_view =
553  gtk_icon_view_new_with_model (GTK_TREE_MODEL (ev_attachbar->priv->model));
554  gtk_icon_view_set_selection_mode (GTK_ICON_VIEW (ev_attachbar->priv->icon_view),
555  GTK_SELECTION_MULTIPLE);
556  gtk_icon_view_set_columns (GTK_ICON_VIEW (ev_attachbar->priv->icon_view), -1);
557  g_object_set (G_OBJECT (ev_attachbar->priv->icon_view),
558  "text-column", COLUMN_NAME,
559  "pixbuf-column", COLUMN_ICON,
560  NULL);
561  g_signal_connect_swapped (ev_attachbar->priv->icon_view,
562  "button-press-event",
564  (gpointer) ev_attachbar);
565 
566  gtk_container_add (GTK_CONTAINER (swindow),
567  ev_attachbar->priv->icon_view);
568 
569  gtk_box_pack_start (GTK_BOX (ev_attachbar), swindow, TRUE, TRUE, 0);
570  gtk_widget_show_all (GTK_WIDGET (ev_attachbar));
571 
572  /* Icon Theme */
573  ev_attachbar->priv->icon_theme = NULL;
574 
575  /* Icon Cache */
576  ev_attachbar->priv->icon_cache = g_hash_table_new_full (g_str_hash,
577  g_str_equal,
578  g_free,
579  g_object_unref);
580 
581  /* Drag and Drop */
582  gtk_icon_view_enable_model_drag_source (
583  GTK_ICON_VIEW (ev_attachbar->priv->icon_view),
584  GDK_BUTTON1_MASK,
585  NULL, 0,
586  GDK_ACTION_COPY);
587  gtk_drag_source_add_uri_targets (ev_attachbar->priv->icon_view);
588 
589  g_signal_connect (ev_attachbar->priv->icon_view,
590  "drag-data-get",
592  (gpointer) ev_attachbar);
593 }
594 
595 GtkWidget *
597 {
598  GtkWidget *ev_attachbar;
599 
600  ev_attachbar = g_object_new (EV_TYPE_SIDEBAR_ATTACHMENTS,
601  "orientation", GTK_ORIENTATION_VERTICAL,
602  NULL);
603 
604  return ev_attachbar;
605 }
606 
607 static void
609  EvSidebarAttachments *ev_attachbar)
610 {
611  GList *l;
612 
613  for (l = job->attachments; l && l->data; l = g_list_next (l)) {
614  EvAttachment *attachment;
615  GtkTreeIter iter;
616  GdkPixbuf *pixbuf = NULL;
617  const gchar *mime_type;
618 
619  attachment = EV_ATTACHMENT (l->data);
620 
621  mime_type = ev_attachment_get_mime_type (attachment);
622  pixbuf = ev_sidebar_attachments_icon_cache_get (ev_attachbar,
623  mime_type);
624 
625  gtk_list_store_append (ev_attachbar->priv->model, &iter);
626  gtk_list_store_set (ev_attachbar->priv->model, &iter,
627  COLUMN_NAME, ev_attachment_get_name (attachment),
628  COLUMN_ICON, pixbuf,
629  COLUMN_ATTACHMENT, attachment,
630  -1);
631  }
632 
633  g_object_unref (job);
634 }
635 
636 
637 static void
639  GParamSpec *pspec,
640  EvSidebarAttachments *ev_attachbar)
641 {
642  EvDocument *document = ev_document_model_get_document (model);
643  EvJob *job;
644 
645  if (!EV_IS_DOCUMENT_ATTACHMENTS (document))
646  return;
647 
649  return;
650 
651  if (!ev_attachbar->priv->icon_theme) {
652  GdkScreen *screen;
653 
654  screen = gtk_widget_get_screen (GTK_WIDGET (ev_attachbar));
655  ev_attachbar->priv->icon_theme = gtk_icon_theme_get_for_screen (screen);
656  g_signal_connect_swapped (ev_attachbar->priv->icon_theme,
657  "changed",
659  (gpointer) ev_attachbar);
660  }
661 
662  gtk_list_store_clear (ev_attachbar->priv->model);
663 
664  job = ev_job_attachments_new (document);
665  g_signal_connect (job, "finished",
666  G_CALLBACK (job_finished_callback),
667  ev_attachbar);
668  g_signal_connect (job, "cancelled",
669  G_CALLBACK (g_object_unref),
670  NULL);
671  /* The priority doesn't matter for this job */
673 }
674 
675 static void
677  EvDocumentModel *model)
678 {
679  g_signal_connect (model, "notify::document",
681  page);
682 }
683 
684 static gboolean
686  EvDocument *document)
687 {
688  return (EV_IS_DOCUMENT_ATTACHMENTS (document) &&
690 }
691 
692 static const gchar*
694 {
695  return _("Attachments");
696 }
697 
698 static void
700 {
704 }
705