]> www.fi.muni.cz Git - evince.git/blob - shell/ev-sidebar-attachments.c
1533dc838e07c2abd0893d0b373fe2c37030ad9b
[evince.git] / shell / ev-sidebar-attachments.c
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
22  */
23
24 #ifdef HAVE_CONFIG_H
25 #include "config.h"
26 #endif
27
28 #include <glib/gi18n.h>
29 #include <glib/gstdio.h>
30 #include <gtk/gtk.h>
31 #include <string.h>
32 #include <libgnomeui/gnome-icon-lookup.h>
33
34 #include "ev-sidebar-attachments.h"
35 #include "ev-sidebar-page.h"
36
37 enum {
38         COLUMN_ICON,
39         COLUMN_NAME,
40         COLUMN_DESCRIPTION,
41         COLUMN_ATTACHMENT,
42         N_COLS
43 };
44
45
46 enum {
47         PROP_0,
48         PROP_WIDGET,
49 };
50
51 enum {
52         SIGNAL_POPUP_MENU,
53         N_SIGNALS
54 };
55
56 static const GtkTargetEntry drag_targets[] = {
57         { "text/uri-list", 0, 0 }
58 };
59
60 static guint signals[N_SIGNALS];
61
62 struct _EvSidebarAttachmentsPrivate {
63         GtkWidget      *icon_view;
64         GtkListStore   *model;
65
66         /* Icons */
67         GtkIconTheme   *icon_theme;
68         GHashTable     *icon_cache;
69 };
70
71 static void ev_sidebar_attachments_page_iface_init (EvSidebarPageIface *iface);
72
73 G_DEFINE_TYPE_EXTENDED (EvSidebarAttachments,
74                         ev_sidebar_attachments,
75                         GTK_TYPE_VBOX,
76                         0, 
77                         G_IMPLEMENT_INTERFACE (EV_TYPE_SIDEBAR_PAGE, 
78                                                ev_sidebar_attachments_page_iface_init))
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         GdkPixbuf *pixbuf = NULL;
103         gchar     *icon;
104
105         icon = gnome_icon_lookup (icon_theme,
106                                   NULL, NULL,
107                                   NULL, NULL,
108                                   mime_type,
109                                   GNOME_ICON_LOOKUP_FLAGS_NONE,
110                                   NULL);
111
112         pixbuf = gtk_icon_theme_load_icon (icon_theme,
113                                            icon, 48, 0, NULL);
114         g_free (icon);
115
116         return pixbuf;
117 }
118
119 static GdkPixbuf *
120 ev_sidebar_attachments_icon_cache_get (EvSidebarAttachments *ev_attachbar,
121                                   const gchar     *mime_type)
122 {
123         GdkPixbuf *pixbuf = NULL;
124         
125         g_assert (mime_type != NULL);
126
127         pixbuf = g_hash_table_lookup (ev_attachbar->priv->icon_cache,
128                                       mime_type);
129
130         if (GDK_IS_PIXBUF (pixbuf))
131                 return pixbuf;
132
133         pixbuf = icon_theme_get_pixbuf_from_mime_type (ev_attachbar->priv->icon_theme,
134                                                        mime_type);
135
136         if (GDK_IS_PIXBUF (pixbuf))
137                 ev_sidebar_attachments_icon_cache_add (ev_attachbar,
138                                                   mime_type,
139                                                   pixbuf);
140
141         return pixbuf;
142 }
143
144 static gboolean
145 icon_cache_update_icon (gchar           *key,
146                         GdkPixbuf       *value,
147                         EvSidebarAttachments *ev_attachbar)
148 {
149         GdkPixbuf *pixbuf = NULL;
150
151         pixbuf = icon_theme_get_pixbuf_from_mime_type (ev_attachbar->priv->icon_theme,
152                                                        key);
153
154         ev_sidebar_attachments_icon_cache_add (ev_attachbar,
155                                           key,
156                                           pixbuf);
157         
158         return FALSE;
159 }
160
161 static void
162 ev_sidebar_attachments_icon_cache_refresh (EvSidebarAttachments *ev_attachbar)
163 {
164         g_hash_table_foreach_remove (ev_attachbar->priv->icon_cache,
165                                      (GHRFunc) icon_cache_update_icon,
166                                      ev_attachbar);
167 }
168
169 static EvAttachment *
170 ev_sidebar_attachments_get_attachment_at_pos (EvSidebarAttachments *ev_attachbar,
171                                          gint             x,
172                                          gint             y)
173 {
174         GtkTreePath  *path = NULL;
175         GtkTreeIter   iter;
176         EvAttachment *attachment = NULL;
177
178         path = gtk_icon_view_get_path_at_pos (GTK_ICON_VIEW (ev_attachbar->priv->icon_view),
179                                               x, y);
180         if (!path) {
181                 return NULL;
182         }
183
184         gtk_tree_model_get_iter (GTK_TREE_MODEL (ev_attachbar->priv->model),
185                                  &iter, path);
186         gtk_tree_model_get (GTK_TREE_MODEL (ev_attachbar->priv->model), &iter,
187                             COLUMN_ATTACHMENT, &attachment,
188                             -1);
189
190         gtk_icon_view_select_path (GTK_ICON_VIEW (ev_attachbar->priv->icon_view),
191                                    path);
192         
193         gtk_tree_path_free (path);
194
195         return attachment;
196 }
197
198 static gboolean
199 ev_sidebar_attachments_popup_menu_show (EvSidebarAttachments *ev_attachbar,
200                                    gint             x,
201                                    gint             y)
202 {
203         GtkIconView *icon_view;
204         GtkTreePath *path;
205         GList       *selected = NULL, *l;
206         GList       *attach_list = NULL;
207
208         icon_view = GTK_ICON_VIEW (ev_attachbar->priv->icon_view);
209         
210         path = gtk_icon_view_get_path_at_pos (icon_view, x, y);
211         if (!path)
212                 return FALSE;
213
214         if (!gtk_icon_view_path_is_selected (icon_view, path)) {
215                 gtk_icon_view_unselect_all (icon_view);
216                 gtk_icon_view_select_path (icon_view, path);
217         }
218
219         gtk_tree_path_free (path);
220         
221         selected = gtk_icon_view_get_selected_items (icon_view);
222         if (!selected)
223                 return FALSE;
224
225         for (l = selected; l && l->data; l = g_list_next (l)) {
226                 GtkTreeIter   iter;
227                 EvAttachment *attachment = NULL;
228
229                 path = (GtkTreePath *) l->data;
230
231                 gtk_tree_model_get_iter (GTK_TREE_MODEL (ev_attachbar->priv->model),
232                                          &iter, path);
233                 gtk_tree_model_get (GTK_TREE_MODEL (ev_attachbar->priv->model), &iter,
234                                     COLUMN_ATTACHMENT, &attachment,
235                                     -1);
236
237                 if (attachment)
238                         attach_list = g_list_prepend (attach_list, attachment);
239
240                 gtk_tree_path_free (path);
241         }
242
243         g_list_free (selected);
244
245         if (!attach_list)
246                 return FALSE;
247
248         g_signal_emit (ev_attachbar, signals[SIGNAL_POPUP_MENU], 0, attach_list);
249
250         return TRUE;
251 }
252
253 static gboolean
254 ev_sidebar_attachments_popup_menu (GtkWidget *widget)
255 {
256         EvSidebarAttachments *ev_attachbar = EV_SIDEBAR_ATTACHMENTS (widget);
257         gint             x, y;
258
259         gtk_widget_get_pointer (widget, &x, &y);
260
261         return ev_sidebar_attachments_popup_menu_show (ev_attachbar, x, y);
262 }
263
264 static gboolean
265 ev_sidebar_attachments_button_press (EvSidebarAttachments *ev_attachbar,
266                                 GdkEventButton  *event,
267                                 GtkWidget       *icon_view)
268 {
269         if (!GTK_WIDGET_HAS_FOCUS (icon_view)) {
270                 gtk_widget_grab_focus (icon_view);
271         }
272         
273         if (event->button == 2)
274                 return FALSE;
275
276         switch (event->button) {
277         case 1:
278                 if (event->type == GDK_2BUTTON_PRESS) {
279                         GError *error = NULL;
280                         EvAttachment *attachment;
281
282                         attachment = ev_sidebar_attachments_get_attachment_at_pos (ev_attachbar,
283                                                                               event->x,
284                                                                               event->y);
285                         if (!attachment)
286                                 return FALSE;
287                         
288                         ev_attachment_open (attachment, &error);
289
290                         if (error) {
291                                 g_warning (error->message);
292                                 g_error_free (error);
293                         }
294
295                         g_object_unref (attachment);
296                                             
297                         return TRUE;
298                 }
299                 break;
300         case 3: 
301                 return ev_sidebar_attachments_popup_menu_show (ev_attachbar, event->x, event->y);
302         }
303
304         return FALSE;
305 }
306
307 static void
308 ev_sidebar_attachments_update_icons (EvSidebarAttachments *ev_attachbar,
309                                 gpointer         user_data)
310 {
311         GtkTreeIter iter;
312         gboolean    valid;
313
314         ev_sidebar_attachments_icon_cache_refresh (ev_attachbar);
315         
316         valid = gtk_tree_model_get_iter_first (
317                 GTK_TREE_MODEL (ev_attachbar->priv->model),
318                 &iter);
319
320         while (valid) {
321                 EvAttachment *attachment = NULL;
322                 GdkPixbuf    *pixbuf = NULL;
323                 const gchar  *mime_type;
324
325                 gtk_tree_model_get (GTK_TREE_MODEL (ev_attachbar->priv->model), &iter,
326                                     COLUMN_ATTACHMENT, &attachment,
327                                     -1);
328
329                 mime_type = ev_attachment_get_mime_type (attachment);
330
331                 if (attachment)
332                         g_object_unref (attachment);
333
334                 pixbuf = ev_sidebar_attachments_icon_cache_get (ev_attachbar,
335                                                            mime_type);
336
337                 gtk_list_store_set (ev_attachbar->priv->model, &iter,
338                                     COLUMN_ICON, pixbuf,
339                                     -1);
340
341                 valid = gtk_tree_model_iter_next (
342                         GTK_TREE_MODEL (ev_attachbar->priv->model),
343                         &iter);
344         }
345 }
346
347 static void
348 ev_sidebar_attachments_drag_data_get (GtkWidget        *widget,
349                                  GdkDragContext   *drag_context,
350                                  GtkSelectionData *data,
351                                  guint             info,
352                                  guint             time,
353                                  gpointer          user_data)
354 {
355         EvSidebarAttachments *ev_attachbar = EV_SIDEBAR_ATTACHMENTS (user_data);
356         GString         *uri_list;
357         gchar           *uris = NULL;
358         GList           *selected = NULL, *l;
359
360         selected = gtk_icon_view_get_selected_items (GTK_ICON_VIEW (ev_attachbar->priv->icon_view));
361         if (!selected)
362                 return;
363
364         uri_list = g_string_new (NULL);
365         
366         for (l = selected; l && l->data; l = g_list_next (l)) {
367                 EvAttachment *attachment;
368                 GtkTreePath  *path;
369                 GtkTreeIter   iter;
370                 gchar        *uri, *filename;
371                 GError       *error = NULL;
372                 
373                 path = (GtkTreePath *) l->data;
374
375                 gtk_tree_model_get_iter (GTK_TREE_MODEL (ev_attachbar->priv->model),
376                                          &iter, path);
377                 gtk_tree_model_get (GTK_TREE_MODEL (ev_attachbar->priv->model), &iter,
378                                     COLUMN_ATTACHMENT, &attachment,
379                                     -1);
380
381                 filename = g_build_filename (g_get_tmp_dir (),
382                                              ev_attachment_get_name (attachment),
383                                              NULL);
384                 
385                 uri = g_filename_to_uri (filename, NULL, NULL);
386
387                 if (ev_attachment_save (attachment, filename, &error)) {
388                         g_string_append (uri_list, uri);
389                         g_string_append_c (uri_list, '\n');
390                 }
391         
392                 if (error) {
393                         g_warning (error->message);
394                         g_error_free (error);
395                 }
396
397                 g_free (uri);
398                 gtk_tree_path_free (path);
399                 g_object_unref (attachment);
400         }
401
402         uris = g_string_free (uri_list, FALSE);
403
404         if (uris) {
405                 gtk_selection_data_set (data,
406                                         data->target,
407                                         8,
408                                         (guchar *)uris,
409                                         strlen (uris));
410         }
411
412         g_list_free (selected);
413 }
414
415 static void
416 ev_sidebar_attachments_get_property (GObject    *object,
417                                      guint       prop_id,
418                                      GValue     *value,
419                                      GParamSpec *pspec)
420 {
421         EvSidebarAttachments *ev_sidebar_attachments;
422   
423         ev_sidebar_attachments = EV_SIDEBAR_ATTACHMENTS (object);
424
425         switch (prop_id)
426         {
427         case PROP_WIDGET:
428                 g_value_set_object (value, ev_sidebar_attachments->priv->icon_view);
429                 break;
430         default:
431                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
432                 break;
433         }
434 }
435
436 static void
437 ev_sidebar_attachments_destroy (GtkObject *object)
438 {
439         EvSidebarAttachments *ev_attachbar = EV_SIDEBAR_ATTACHMENTS (object);
440
441         if (ev_attachbar->priv->model) {
442                 g_object_unref (ev_attachbar->priv->model);
443                 ev_attachbar->priv->model = NULL;
444         }
445
446         if (ev_attachbar->priv->icon_cache) {
447                 g_hash_table_destroy (ev_attachbar->priv->icon_cache);
448                 ev_attachbar->priv->icon_cache = NULL;
449         }
450
451         (* GTK_OBJECT_CLASS (ev_sidebar_attachments_parent_class)->destroy) (object);
452 }
453
454 static void
455 ev_sidebar_attachments_class_init (EvSidebarAttachmentsClass *ev_attachbar_class)
456 {
457         GObjectClass   *g_object_class;
458         GtkObjectClass *gtk_object_class;
459         GtkWidgetClass *gtk_widget_class;
460
461         g_object_class = G_OBJECT_CLASS (ev_attachbar_class);
462         gtk_object_class = GTK_OBJECT_CLASS (ev_attachbar_class);
463         gtk_widget_class = GTK_WIDGET_CLASS (ev_attachbar_class);
464
465         g_object_class->get_property = ev_sidebar_attachments_get_property;
466         gtk_object_class->destroy = ev_sidebar_attachments_destroy;
467         gtk_widget_class->popup_menu = ev_sidebar_attachments_popup_menu;
468
469         g_type_class_add_private (g_object_class, sizeof (EvSidebarAttachmentsPrivate));
470
471         /* Signals */
472         signals[SIGNAL_POPUP_MENU] =
473                 g_signal_new ("popup",
474                               G_TYPE_FROM_CLASS (g_object_class),
475                               G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
476                               G_STRUCT_OFFSET (EvSidebarAttachmentsClass, popup_menu),
477                               NULL, NULL,
478                               g_cclosure_marshal_VOID__POINTER,
479                               G_TYPE_NONE, 1,
480                               G_TYPE_POINTER);
481
482         g_object_class_override_property (g_object_class,
483                                           PROP_WIDGET,
484                                           "main-widget");
485 }
486
487 static void
488 ev_sidebar_attachments_init (EvSidebarAttachments *ev_attachbar)
489 {
490         GtkWidget *swindow;
491         
492         ev_attachbar->priv = EV_SIDEBAR_ATTACHMENTS_GET_PRIVATE (ev_attachbar);
493
494         swindow = gtk_scrolled_window_new (NULL, NULL);
495         gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (swindow),
496                                         GTK_POLICY_NEVER,
497                                         GTK_POLICY_AUTOMATIC);
498         gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (swindow),
499                                              GTK_SHADOW_IN);
500         /* Data Model */
501         ev_attachbar->priv->model = gtk_list_store_new (N_COLS,
502                                                         GDK_TYPE_PIXBUF, 
503                                                         G_TYPE_STRING,  
504                                                         G_TYPE_STRING,
505                                                         EV_TYPE_ATTACHMENT);
506
507         /* Icon View */
508         ev_attachbar->priv->icon_view =
509                 gtk_icon_view_new_with_model (GTK_TREE_MODEL (ev_attachbar->priv->model));
510         gtk_icon_view_set_selection_mode (GTK_ICON_VIEW (ev_attachbar->priv->icon_view),
511                                           GTK_SELECTION_MULTIPLE);
512         gtk_icon_view_set_columns (GTK_ICON_VIEW (ev_attachbar->priv->icon_view), -1);
513         g_object_set (G_OBJECT (ev_attachbar->priv->icon_view),
514                       "text-column", COLUMN_NAME,
515                       "pixbuf-column", COLUMN_ICON,
516                       NULL);
517         g_signal_connect_swapped (G_OBJECT (ev_attachbar->priv->icon_view),
518                                   "button-press-event",
519                                   G_CALLBACK (ev_sidebar_attachments_button_press),
520                                   (gpointer) ev_attachbar);
521
522         gtk_container_add (GTK_CONTAINER (swindow),
523                            ev_attachbar->priv->icon_view);
524
525         gtk_container_add (GTK_CONTAINER (ev_attachbar),
526                            swindow);
527         gtk_widget_show_all (GTK_WIDGET (ev_attachbar));
528         /* Icon Theme */
529         ev_attachbar->priv->icon_theme = gtk_icon_theme_get_default ();
530         g_signal_connect_swapped (G_OBJECT (ev_attachbar->priv->icon_theme),
531                                   "changed",
532                                   G_CALLBACK (ev_sidebar_attachments_update_icons),
533                                   (gpointer) ev_attachbar);
534
535         /* Icon Cache */
536         ev_attachbar->priv->icon_cache = g_hash_table_new_full (g_str_hash,
537                                                                 g_str_equal,
538                                                                 g_free,
539                                                                 g_object_unref);
540
541         /* Drag and Drop */
542 #ifdef HAVE_GTK_ICON_VIEW_ENABLE_MODEL_DRAG_SOURCE
543         gtk_icon_view_enable_model_drag_source (
544                 GTK_ICON_VIEW (ev_attachbar->priv->icon_view),
545                                GDK_BUTTON1_MASK,
546                                drag_targets,
547                                G_N_ELEMENTS (drag_targets),
548                                GDK_ACTION_COPY);
549 #endif
550         g_signal_connect (G_OBJECT (ev_attachbar->priv->icon_view),
551                           "drag-data-get",
552                           G_CALLBACK (ev_sidebar_attachments_drag_data_get),
553                           (gpointer) ev_attachbar);     
554 }
555
556 GtkWidget *
557 ev_sidebar_attachments_new (void)
558 {
559         GtkWidget *ev_attachbar;
560
561         ev_attachbar = g_object_new (EV_TYPE_SIDEBAR_ATTACHMENTS, NULL);
562
563         return ev_attachbar;
564 }
565
566 static void
567 ev_sidebar_attachments_set_document (EvSidebarPage   *page,
568                                      EvDocument      *document)
569 {
570         EvSidebarAttachments *ev_attachbar = EV_SIDEBAR_ATTACHMENTS (page);
571         GList *attachments = NULL;
572         GList *l;
573         
574         if (!ev_document_has_attachments (document))
575                 return;
576
577         attachments = ev_document_get_attachments (document);
578
579         gtk_list_store_clear (ev_attachbar->priv->model);
580                                            
581         for (l = attachments; l && l->data; l = g_list_next (l)) {
582                 EvAttachment *attachment;
583                 GtkTreeIter   iter;
584                 GdkPixbuf    *pixbuf = NULL;
585                 const gchar  *mime_type;
586
587                 attachment = EV_ATTACHMENT (l->data);
588
589                 mime_type = ev_attachment_get_mime_type (attachment);
590                 pixbuf = ev_sidebar_attachments_icon_cache_get (ev_attachbar,
591                                                            mime_type);
592
593                 gtk_list_store_append (ev_attachbar->priv->model, &iter);
594                 gtk_list_store_set (ev_attachbar->priv->model, &iter,
595                                     COLUMN_NAME, ev_attachment_get_name (attachment),
596                                     COLUMN_ICON, pixbuf,
597                                     COLUMN_ATTACHMENT, attachment, 
598                                     -1);
599
600                 g_object_unref (attachment);
601         }
602
603         g_list_free (attachments);
604 }
605
606 static gboolean
607 ev_sidebar_attachments_support_document (EvSidebarPage   *sidebar_page,
608                                         EvDocument *document)
609 {
610         return ev_document_has_attachments (document);
611 }
612
613 static const gchar*
614 ev_sidebar_attachments_get_label (EvSidebarPage *sidebar_page)
615 {
616     return _("Attachments");
617 }
618
619 static void
620 ev_sidebar_attachments_page_iface_init (EvSidebarPageIface *iface)
621 {
622         iface->support_document = ev_sidebar_attachments_support_document;
623         iface->set_document = ev_sidebar_attachments_set_document;
624         iface->get_label = ev_sidebar_attachments_get_label;
625 }
626