]> www.fi.muni.cz Git - evince.git/blob - shell/ev-attachment-bar.c
Added missing files.
[evince.git] / shell / ev-attachment-bar.c
1 /* ev-attachment-bar.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 #include <glib/gi18n.h>
25 #include <glib/gstdio.h>
26 #include <gtk/gtk.h>
27 #include <string.h>
28 #include <libgnomeui/gnome-icon-lookup.h>
29
30 #include "ev-attachment-bar.h"
31
32 #define MIN_HEIGHT 92
33
34 enum {
35         COLUMN_ICON,
36         COLUMN_NAME,
37         COLUMN_DESCRIPTION,
38         COLUMN_ATTACHMENT,
39         N_COLS
40 };
41
42 enum {
43         SIGNAL_POPUP_MENU,
44         N_SIGNALS
45 };
46
47 static const GtkTargetEntry drag_targets[] = {
48         { "text/uri-list", 0, 0 }
49 };
50
51 static guint signals[N_SIGNALS];
52
53 struct _EvAttachmentBarPrivate {
54         GtkWidget      *label;
55         GtkWidget      *icon_view;
56         GtkListStore   *model;
57
58         /* Icons */
59         GtkIconTheme   *icon_theme;
60         GHashTable     *icon_cache;
61 };
62
63 G_DEFINE_TYPE (EvAttachmentBar, ev_attachment_bar, GTK_TYPE_EXPANDER)
64
65 #define EV_ATTACHMENT_BAR_GET_PRIVATE(object) \
66                 (G_TYPE_INSTANCE_GET_PRIVATE ((object), EV_TYPE_ATTACHMENT_BAR, EvAttachmentBarPrivate))
67
68 /* Icon cache */
69 static void
70 ev_attachment_bar_icon_cache_add (EvAttachmentBar *ev_attachbar,
71                                   const gchar     *mime_type,
72                                   const GdkPixbuf *pixbuf)
73 {
74         g_assert (mime_type != NULL);
75         g_assert (GDK_IS_PIXBUF (pixbuf));
76
77         g_hash_table_insert (ev_attachbar->priv->icon_cache,
78                              (gpointer)g_strdup (mime_type),
79                              (gpointer)pixbuf);
80                              
81 }
82
83 static GdkPixbuf *
84 icon_theme_get_pixbuf_from_mime_type (GtkIconTheme *icon_theme,
85                                       const gchar  *mime_type)
86 {
87         GdkPixbuf *pixbuf = NULL;
88         gchar     *icon;
89
90         icon = gnome_icon_lookup (icon_theme,
91                                   NULL, NULL,
92                                   NULL, NULL,
93                                   mime_type,
94                                   GNOME_ICON_LOOKUP_FLAGS_NONE,
95                                   NULL);
96
97         pixbuf = gtk_icon_theme_load_icon (icon_theme,
98                                            icon, 48, 0, NULL);
99         g_free (icon);
100
101         return pixbuf;
102 }
103
104 static GdkPixbuf *
105 ev_attachment_bar_icon_cache_get (EvAttachmentBar *ev_attachbar,
106                                   const gchar     *mime_type)
107 {
108         GdkPixbuf *pixbuf = NULL;
109         
110         g_assert (mime_type != NULL);
111
112         pixbuf = g_hash_table_lookup (ev_attachbar->priv->icon_cache,
113                                       mime_type);
114
115         if (GDK_IS_PIXBUF (pixbuf))
116                 return pixbuf;
117
118         pixbuf = icon_theme_get_pixbuf_from_mime_type (ev_attachbar->priv->icon_theme,
119                                                        mime_type);
120
121         if (GDK_IS_PIXBUF (pixbuf))
122                 ev_attachment_bar_icon_cache_add (ev_attachbar,
123                                                   mime_type,
124                                                   pixbuf);
125
126         return pixbuf;
127 }
128
129 static gboolean
130 icon_cache_update_icon (gchar           *key,
131                         GdkPixbuf       *value,
132                         EvAttachmentBar *ev_attachbar)
133 {
134         GdkPixbuf *pixbuf = NULL;
135
136         pixbuf = icon_theme_get_pixbuf_from_mime_type (ev_attachbar->priv->icon_theme,
137                                                        key);
138
139         ev_attachment_bar_icon_cache_add (ev_attachbar,
140                                           key,
141                                           pixbuf);
142         
143         return FALSE;
144 }
145
146 static void
147 ev_attachment_bar_icon_cache_refresh (EvAttachmentBar *ev_attachbar)
148 {
149         g_hash_table_foreach_remove (ev_attachbar->priv->icon_cache,
150                                      (GHRFunc) icon_cache_update_icon,
151                                      ev_attachbar);
152 }
153
154 static void
155 ev_attachment_bar_toggled (GObject    *object,
156                            GParamSpec *param_spec,
157                            gpointer    user_data)
158 {
159         EvAttachmentBar *attachbar = EV_ATTACHMENT_BAR (object);
160         GtkExpander     *expander = GTK_EXPANDER (object);
161
162         if (!attachbar->priv->label)
163                 return;
164         
165         if (gtk_expander_get_expanded (expander)) {
166                 gtk_label_set_text (GTK_LABEL (attachbar->priv->label),
167                                     _("Hide attachments bar"));
168         } else {
169                 gtk_label_set_text (GTK_LABEL (attachbar->priv->label),
170                                     _("Show attachments bar"));
171         }
172 }
173
174 static EvAttachment *
175 ev_attachment_bar_get_attachment_at_pos (EvAttachmentBar *ev_attachbar,
176                                          gint             x,
177                                          gint             y)
178 {
179         GtkTreePath  *path = NULL;
180         GtkTreeIter   iter;
181         EvAttachment *attachment = NULL;
182
183         path = gtk_icon_view_get_path_at_pos (GTK_ICON_VIEW (ev_attachbar->priv->icon_view),
184                                               x, y);
185         if (!path) {
186                 return NULL;
187         }
188
189         gtk_tree_model_get_iter (GTK_TREE_MODEL (ev_attachbar->priv->model),
190                                  &iter, path);
191         gtk_tree_model_get (GTK_TREE_MODEL (ev_attachbar->priv->model), &iter,
192                             COLUMN_ATTACHMENT, &attachment,
193                             -1);
194
195         gtk_icon_view_select_path (GTK_ICON_VIEW (ev_attachbar->priv->icon_view),
196                                    path);
197         
198         gtk_tree_path_free (path);
199
200         return attachment;
201 }
202
203 static gboolean
204 ev_attachment_bar_button_press (EvAttachmentBar *ev_attachbar,
205                                 GdkEventButton  *event,
206                                 GtkWidget       *icon_view)
207 {
208         EvAttachment *attachment;
209         gboolean      handled = FALSE;
210
211         if (!GTK_WIDGET_HAS_FOCUS (icon_view)) {
212                 gtk_widget_grab_focus (icon_view);
213         }
214         
215         if (event->button == 2)
216                 return FALSE;
217
218         attachment = ev_attachment_bar_get_attachment_at_pos (ev_attachbar,
219                                                               event->x,
220                                                               event->y);
221         if (!attachment)
222                 return FALSE;
223
224         switch (event->button) {
225         case 1:
226                 if (event->type == GDK_2BUTTON_PRESS) {
227                         GError *error = NULL;
228
229                         ev_attachment_open (attachment, &error);
230
231                         if (error) {
232                                 g_warning (error->message);
233                                 g_error_free (error);
234                         }
235                                             
236                         handled = TRUE;
237                 }
238                 break;
239         case 3:
240                 g_signal_emit (ev_attachbar, signals[SIGNAL_POPUP_MENU], 0, attachment);
241                 handled = TRUE;
242                 
243                 break;
244         }
245
246         g_object_unref (attachment);
247
248         return handled;
249 }
250
251 static gboolean
252 ev_attachment_bar_focus_in (GtkWidget     *widget,
253                             GdkEventFocus *event)
254 {
255         EvAttachmentBar *ev_attachbar = EV_ATTACHMENT_BAR (widget);
256
257         if (gtk_expander_get_expanded (GTK_EXPANDER (ev_attachbar)))
258                 gtk_widget_grab_focus (ev_attachbar->priv->icon_view);
259
260         return TRUE;
261 }
262
263 static gboolean
264 ev_attachment_bar_popup_menu (GtkWidget *widget)
265 {
266         EvAttachmentBar *ev_attachbar = EV_ATTACHMENT_BAR (widget);
267         EvAttachment    *attachment;
268         gint             x, y;
269
270         gtk_widget_get_pointer (widget, &x, &y);
271         attachment = ev_attachment_bar_get_attachment_at_pos (ev_attachbar,
272                                                               x, y);
273         if (!attachment)
274                 return FALSE;
275
276         g_signal_emit (ev_attachbar, signals[SIGNAL_POPUP_MENU], 0, attachment);
277
278         return TRUE;
279 }
280
281 static void
282 ev_attachment_bar_update_icons (EvAttachmentBar *ev_attachbar,
283                                 gpointer         user_data)
284 {
285         GtkTreeIter iter;
286         gboolean    valid;
287
288         ev_attachment_bar_icon_cache_refresh (ev_attachbar);
289         
290         valid = gtk_tree_model_get_iter_first (
291                 GTK_TREE_MODEL (ev_attachbar->priv->model),
292                 &iter);
293
294         while (valid) {
295                 EvAttachment *attachment = NULL;
296                 GdkPixbuf    *pixbuf = NULL;
297                 const gchar  *mime_type;
298
299                 gtk_tree_model_get (GTK_TREE_MODEL (ev_attachbar->priv->model), &iter,
300                                     COLUMN_ATTACHMENT, &attachment,
301                                     -1);
302
303                 mime_type = ev_attachment_get_mime_type (attachment);
304
305                 if (attachment)
306                         g_object_unref (attachment);
307
308                 pixbuf = ev_attachment_bar_icon_cache_get (ev_attachbar,
309                                                            mime_type);
310
311                 gtk_list_store_set (ev_attachbar->priv->model, &iter,
312                                     COLUMN_ICON, pixbuf,
313                                     -1);
314
315                 valid = gtk_tree_model_iter_next (
316                         GTK_TREE_MODEL (ev_attachbar->priv->model),
317                         &iter);
318         }
319 }
320
321 static void
322 ev_attachment_bar_drag_data_get (GtkWidget        *widget,
323                                  GdkDragContext   *drag_context,
324                                  GtkSelectionData *data,
325                                  guint             info,
326                                  guint             time,
327                                  gpointer          user_data)
328 {
329         EvAttachmentBar *ev_attachbar = EV_ATTACHMENT_BAR (user_data);
330         EvAttachment    *attachment;
331         GtkTreePath     *path;
332         GtkTreeIter      iter;
333         GList           *selected = NULL;
334         gchar           *uri, *filename;
335         GError          *error = NULL;
336
337         selected = gtk_icon_view_get_selected_items (GTK_ICON_VIEW (ev_attachbar->priv->icon_view));
338         if (!selected)
339                 return;
340
341         path = (GtkTreePath *) selected->data;
342
343         gtk_tree_model_get_iter (GTK_TREE_MODEL (ev_attachbar->priv->model),
344                                  &iter, path);
345         gtk_tree_model_get (GTK_TREE_MODEL (ev_attachbar->priv->model), &iter,
346                             COLUMN_ATTACHMENT, &attachment,
347                             -1);
348
349         filename = g_build_filename (g_get_tmp_dir (),
350                                      ev_attachment_get_name (attachment),
351                                      NULL);
352         uri = g_filename_to_uri (filename, NULL, NULL);
353
354         g_object_set_data_full (G_OBJECT (drag_context),
355                                 "tmp-filename", filename,
356                                 g_free);
357         
358         
359         if (ev_attachment_save (attachment, uri, &error)) {
360                 gtk_selection_data_set (data,
361                                         data->target,
362                                         8,
363                                         (guchar *)uri,
364                                         strlen (uri));
365         }
366         
367         if (error) {
368                 g_warning (error->message);
369                 g_error_free (error);
370         }
371
372         g_free (uri);
373         g_object_unref (attachment);
374         g_list_foreach (selected,
375                         (GFunc) gtk_tree_path_free,
376                         NULL);
377         g_list_free (selected);
378 }
379
380 static void
381 ev_attachment_bar_drag_data_delete (GtkWidget      *widget,
382                                     GdkDragContext *drag_context,
383                                     gpointer        user_data)
384 {
385         gchar *filename;
386
387         filename = g_object_get_data (G_OBJECT (drag_context), "tmp-filename");
388         
389         if (filename && g_file_test (filename, G_FILE_TEST_EXISTS))
390                 g_unlink (filename);
391 }
392
393 static void
394 ev_attachment_bar_destroy (GtkObject *object)
395 {
396         EvAttachmentBar *ev_attachbar = EV_ATTACHMENT_BAR (object);
397
398         if (ev_attachbar->priv->model) {
399                 g_object_unref (ev_attachbar->priv->model);
400                 ev_attachbar->priv->model = NULL;
401         }
402
403         if (ev_attachbar->priv->icon_cache) {
404                 g_hash_table_destroy (ev_attachbar->priv->icon_cache);
405                 ev_attachbar->priv->icon_cache = NULL;
406         }
407
408         (* GTK_OBJECT_CLASS (ev_attachment_bar_parent_class)->destroy) (object);
409 }
410
411 static void
412 ev_attachment_bar_class_init (EvAttachmentBarClass *ev_attachbar_class)
413 {
414         GObjectClass   *g_object_class;
415         GtkObjectClass *gtk_object_class;
416         GtkWidgetClass *gtk_widget_class;
417
418         g_object_class = G_OBJECT_CLASS (ev_attachbar_class);
419         gtk_object_class = GTK_OBJECT_CLASS (ev_attachbar_class);
420         gtk_widget_class = GTK_WIDGET_CLASS (ev_attachbar_class);
421
422         gtk_object_class->destroy = ev_attachment_bar_destroy;
423         gtk_widget_class->popup_menu = ev_attachment_bar_popup_menu;
424         gtk_widget_class->focus_in_event = ev_attachment_bar_focus_in;
425
426         g_type_class_add_private (g_object_class, sizeof (EvAttachmentBarPrivate));
427
428         /* Signals */
429         signals[SIGNAL_POPUP_MENU] =
430                 g_signal_new ("popup",
431                               G_TYPE_FROM_CLASS (g_object_class),
432                               G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
433                               G_STRUCT_OFFSET (EvAttachmentBarClass, popup_menu),
434                               NULL, NULL,
435                               g_cclosure_marshal_VOID__OBJECT,
436                               G_TYPE_NONE, 1,
437                               G_TYPE_OBJECT);
438 }
439
440 static void
441 ev_attachment_bar_init (EvAttachmentBar *ev_attachbar)
442 {
443         GtkWidget *swindow;
444         
445         ev_attachbar->priv = EV_ATTACHMENT_BAR_GET_PRIVATE (ev_attachbar);
446
447         swindow = gtk_scrolled_window_new (NULL, NULL);
448         gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (swindow),
449                                         GTK_POLICY_NEVER,
450                                         GTK_POLICY_AUTOMATIC);
451         gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (swindow),
452                                              GTK_SHADOW_IN);
453         gtk_widget_set_size_request (swindow, -1, MIN_HEIGHT);
454
455         /* Data Model */
456         ev_attachbar->priv->model = gtk_list_store_new (N_COLS,
457                                                         GDK_TYPE_PIXBUF, 
458                                                         G_TYPE_STRING,  
459                                                         G_TYPE_STRING,
460                                                         EV_TYPE_ATTACHMENT);
461
462         /* Icon View */
463         ev_attachbar->priv->icon_view =
464                 gtk_icon_view_new_with_model (GTK_TREE_MODEL (ev_attachbar->priv->model));
465         gtk_icon_view_set_columns (GTK_ICON_VIEW (ev_attachbar->priv->icon_view), -1);
466         g_object_set (G_OBJECT (ev_attachbar->priv->icon_view),
467                       "text-column", COLUMN_NAME,
468                       "pixbuf-column", COLUMN_ICON,
469                       NULL);
470         g_signal_connect_swapped (G_OBJECT (ev_attachbar->priv->icon_view),
471                                   "button-press-event",
472                                   G_CALLBACK (ev_attachment_bar_button_press),
473                                   (gpointer) ev_attachbar);
474
475         gtk_container_add (GTK_CONTAINER (swindow),
476                            ev_attachbar->priv->icon_view);
477         gtk_widget_show (ev_attachbar->priv->icon_view);
478
479         gtk_container_add (GTK_CONTAINER (ev_attachbar),
480                            swindow);
481         gtk_widget_show (swindow);
482
483         /* Icon Theme */
484         ev_attachbar->priv->icon_theme = gtk_icon_theme_get_default ();
485         g_signal_connect_swapped (G_OBJECT (ev_attachbar->priv->icon_theme),
486                                   "changed",
487                                   G_CALLBACK (ev_attachment_bar_update_icons),
488                                   (gpointer) ev_attachbar);
489
490         /* Icon Cache */
491         ev_attachbar->priv->icon_cache = g_hash_table_new_full (g_str_hash,
492                                                                 g_str_equal,
493                                                                 g_free,
494                                                                 g_object_unref);
495
496         /* Drag and Drop */
497         gtk_icon_view_enable_model_drag_source (
498                 GTK_ICON_VIEW (ev_attachbar->priv->icon_view),
499                                GDK_BUTTON1_MASK,
500                                drag_targets,
501                                G_N_ELEMENTS (drag_targets),
502                                GDK_ACTION_MOVE);
503         g_signal_connect (G_OBJECT (ev_attachbar->priv->icon_view),
504                           "drag-data-get",
505                           G_CALLBACK (ev_attachment_bar_drag_data_get),
506                           (gpointer) ev_attachbar);
507         g_signal_connect (G_OBJECT (ev_attachbar->priv->icon_view),
508                           "drag-data-delete",
509                           G_CALLBACK (ev_attachment_bar_drag_data_delete),
510                           (gpointer) ev_attachbar);
511
512         g_signal_connect (G_OBJECT (ev_attachbar),
513                           "notify::expanded",
514                           G_CALLBACK (ev_attachment_bar_toggled),
515                           NULL);
516         
517 }
518
519 static void
520 ev_attachment_bar_setup (EvAttachmentBar *ev_attachbar)
521 {
522         GtkWidget *hbox;
523         GtkWidget *image;
524
525         hbox = gtk_hbox_new (FALSE, 6);
526         image = gtk_image_new_from_stock ("gnome-stock-attach",
527                                           GTK_ICON_SIZE_MENU);
528         gtk_box_pack_start (GTK_BOX (hbox),
529                             image,
530                             FALSE, FALSE, 0);
531         gtk_widget_show (image);
532
533         ev_attachbar->priv->label = gtk_label_new (_("Show attachments bar"));
534         gtk_box_pack_start (GTK_BOX (hbox),
535                             ev_attachbar->priv->label,
536                             FALSE, FALSE, 0);
537         gtk_widget_show (ev_attachbar->priv->label);
538
539         gtk_expander_set_label_widget (GTK_EXPANDER (ev_attachbar), hbox);
540         gtk_widget_show (hbox);
541 }
542
543 GtkWidget *
544 ev_attachment_bar_new (void)
545 {
546         GtkWidget *ev_attachbar;
547
548         ev_attachbar = g_object_new (EV_TYPE_ATTACHMENT_BAR, NULL);
549
550         ev_attachment_bar_setup (EV_ATTACHMENT_BAR (ev_attachbar));
551
552         return ev_attachbar;
553 }
554
555 void
556 ev_attachment_bar_set_document (EvAttachmentBar *ev_attachbar,
557                                 EvDocument      *document)
558 {
559         GList *attachments = NULL;
560         GList *l;
561         
562         if (!ev_document_has_attachments (document))
563                 return;
564
565         attachments = ev_document_get_attachments (document);
566
567         gtk_list_store_clear (ev_attachbar->priv->model);
568                                            
569         for (l = attachments; l && l->data; l = g_list_next (l)) {
570                 EvAttachment *attachment;
571                 GtkTreeIter   iter;
572                 GdkPixbuf    *pixbuf = NULL;
573                 const gchar  *mime_type;
574
575                 attachment = EV_ATTACHMENT (l->data);
576
577                 mime_type = ev_attachment_get_mime_type (attachment);
578                 pixbuf = ev_attachment_bar_icon_cache_get (ev_attachbar,
579                                                            mime_type);
580
581                 gtk_list_store_append (ev_attachbar->priv->model, &iter);
582                 gtk_list_store_set (ev_attachbar->priv->model, &iter,
583                                     COLUMN_NAME, ev_attachment_get_name (attachment),
584                                     COLUMN_ICON, pixbuf,
585                                     COLUMN_ATTACHMENT, attachment, 
586                                     -1);
587
588                 g_object_unref (attachment);
589         }
590
591         g_list_free (attachments);
592 }