* src/procmsg.c
[claws.git] / src / mimeview.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2003 Hiroyuki Yamamoto
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #  include "config.h"
22 #endif
23
24 #include "defs.h"
25
26 #include <glib.h>
27 #include <gdk/gdkkeysyms.h>
28 #include <gtk/gtknotebook.h>
29 #include <gtk/gtkscrolledwindow.h>
30 #include <gtk/gtkctree.h>
31 #include <gtk/gtkvbox.h>
32 #include <gtk/gtkhpaned.h>
33 #include <gtk/gtktext.h>
34 #include <gtk/gtksignal.h>
35 #include <gtk/gtkmenu.h>
36 #include <gtk/gtkdnd.h>
37 #include <gtk/gtkselection.h>
38 #include <gtk/gtktooltips.h>
39 #include <gtk/gtkcontainer.h>
40 #include <stdio.h>
41
42 #ifndef HAVE_APACHE_FNMATCH
43 /* kludge: apache's fnmatch clashes with <regex.h>, don't include
44  * fnmatch.h */
45 #include <fnmatch.h>
46 #endif
47
48 #include "intl.h"
49 #include "main.h"
50 #include "mimeview.h"
51 #include "textview.h"
52 #include "procmime.h"
53 #include "summaryview.h"
54 #include "menu.h"
55 #include "filesel.h"
56 #include "alertpanel.h"
57 #include "inputdialog.h"
58 #include "utils.h"
59 #include "gtkutils.h"
60 #include "prefs_common.h"
61 #include "rfc2015.h"
62 #include "stock_pixmap.h"
63 #include "gtk/gtkvscrollbutton.h"
64
65
66 typedef enum
67 {
68         COL_MIMETYPE = 0,
69         COL_SIZE     = 1,
70         COL_NAME     = 2
71 } MimeViewColumnPos;
72
73 #define N_MIMEVIEW_COLS 3
74 #define ICONBOX_DEFAULT_WIDTH 36
75
76 static void mimeview_set_multipart_tree         (MimeView       *mimeview,
77                                                  MimeInfo       *mimeinfo,
78                                                  GtkCTreeNode   *parent);
79 static GtkCTreeNode *mimeview_append_part       (MimeView       *mimeview,
80                                                  MimeInfo       *partinfo,
81                                                  GtkCTreeNode   *parent);
82 static void mimeview_show_message_part          (MimeView       *mimeview,
83                                                  MimeInfo       *partinfo);
84 static void mimeview_change_view_type           (MimeView       *mimeview,
85                                                  MimeViewType    type);
86 static void mimeview_clear                      (MimeView       *mimeview);
87
88 static void mimeview_selected           (GtkCTree       *ctree,
89                                          GtkCTreeNode   *node,
90                                          gint            column,
91                                          MimeView       *mimeview);
92 static void mimeview_start_drag         (GtkWidget      *widget,
93                                          gint            button,
94                                          GdkEvent       *event,
95                                          MimeView       *mimeview);
96 static gint mimeview_button_pressed     (GtkWidget      *widget,
97                                          GdkEventButton *event,
98                                          MimeView       *mimeview);
99 static gint mimeview_key_pressed        (GtkWidget      *widget,
100                                          GdkEventKey    *event,
101                                          MimeView       *mimeview);
102
103 static void mimeview_drag_data_get      (GtkWidget        *widget,
104                                          GdkDragContext   *drag_context,
105                                          GtkSelectionData *selection_data,
106                                          guint             info,
107                                          guint             time,
108                                          MimeView         *mimeview);
109
110 static void mimeview_display_as_text    (MimeView       *mimeview);
111 static void mimeview_save_as            (MimeView       *mimeview);
112 static void mimeview_save_all           (MimeView       *mimeview);
113 static void mimeview_launch             (MimeView       *mimeview);
114 static void mimeview_open_with          (MimeView       *mimeview);
115 static void mimeview_view_file          (const gchar    *filename,
116                                          MimeInfo       *partinfo,
117                                          const gchar    *cmdline);
118 static gboolean icon_clicked_cb         (GtkWidget      *button, 
119                                          GdkEventButton *event, 
120                                          MimeView       *mimeview);
121 static void icon_list_append_icon       (MimeView       *mimeview, 
122                                          MimeInfo       *mimeinfo);
123 static void icon_list_create            (MimeView       *mimeview, 
124                                          MimeInfo       *mimeinfo);
125 static void icon_list_clear             (MimeView       *mimeview);
126 static void mime_toggle_button_cb       (GtkWidget      *button,
127                                          MimeView       *mimeview);
128 static void part_button_pressed         (MimeView       *mimeview, 
129                                          GdkEventButton *event, 
130                                          MimeInfo       *partinfo);
131 static void icon_scroll_size_allocate_cb(GtkWidget      *widget, 
132                                          GtkAllocation  *layout_size, 
133                                          MimeView       *mimeview);
134
135 static GtkItemFactoryEntry mimeview_popup_entries[] =
136 {
137         {N_("/_Open"),            NULL, mimeview_launch,          0, NULL},
138         {N_("/Open _with..."),    NULL, mimeview_open_with,       0, NULL},
139         {N_("/_Display as text"), NULL, mimeview_display_as_text, 0, NULL},
140         {N_("/_Save as..."),      NULL, mimeview_save_as,         0, NULL},
141         {N_("/Save _all..."),     NULL, mimeview_save_all,        0, NULL}
142 #if USE_GPGME
143         ,
144         {N_("/_Check signature"), NULL, mimeview_check_signature, 0, NULL}
145 #endif
146 };
147
148 static GtkTargetEntry mimeview_mime_types[] =
149 {
150         {"text/uri-list", 0, 0}
151 };
152
153 GSList *mimeviewer_factories;
154 GSList *mimeviews;
155
156 MimeView *mimeview_create(MainWindow *mainwin)
157 {
158         MimeView *mimeview;
159
160         GtkWidget *notebook;
161         GtkWidget *vbox;
162         GtkWidget *paned;
163         GtkWidget *scrolledwin;
164         GtkWidget *ctree;
165         GtkWidget *mime_notebook;
166         GtkWidget *popupmenu;
167         GtkWidget *ctree_mainbox;
168         GtkWidget *mime_toggle;
169         GtkWidget *icon_mainbox;
170         GtkWidget *icon_scroll;
171         GtkWidget *icon_vbox;
172         GtkWidget *arrow;
173         GtkWidget *scrollbutton;
174         GtkTooltips *tooltips;
175         GtkItemFactory *popupfactory;
176         gchar *titles[N_MIMEVIEW_COLS];
177         gint n_entries;
178         gint i;
179
180         debug_print("Creating MIME view...\n");
181         mimeview = g_new0(MimeView, 1);
182
183         titles[COL_MIMETYPE] = _("MIME Type");
184         titles[COL_SIZE]     = _("Size");
185         titles[COL_NAME]     = _("Name");
186
187         notebook = gtk_notebook_new();
188         gtk_notebook_set_scrollable(GTK_NOTEBOOK(notebook), TRUE);
189         gtk_notebook_set_show_tabs(GTK_NOTEBOOK(notebook), FALSE);
190         gtk_notebook_set_show_border(GTK_NOTEBOOK(notebook), FALSE);
191
192         vbox = gtk_vbox_new(FALSE, 0);
193         gtk_container_add(GTK_CONTAINER(notebook), vbox);
194         
195         scrolledwin = gtk_scrolled_window_new(NULL, NULL);
196         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
197                                        GTK_POLICY_AUTOMATIC,
198                                        GTK_POLICY_ALWAYS);
199
200         ctree = gtk_sctree_new_with_titles(N_MIMEVIEW_COLS, 0, titles);
201         gtk_clist_set_selection_mode(GTK_CLIST(ctree), GTK_SELECTION_BROWSE);
202         gtk_ctree_set_line_style(GTK_CTREE(ctree), GTK_CTREE_LINES_NONE);
203         gtk_clist_set_column_justification(GTK_CLIST(ctree), COL_SIZE,
204                                            GTK_JUSTIFY_RIGHT);
205         gtk_clist_set_column_width(GTK_CLIST(ctree), COL_MIMETYPE, 200);
206         gtk_clist_set_column_width(GTK_CLIST(ctree), COL_SIZE, 50);
207         gtk_clist_set_column_width(GTK_CLIST(ctree), COL_NAME, 200);
208         for (i = 0; i < N_MIMEVIEW_COLS; i++)
209                 GTK_WIDGET_UNSET_FLAGS(GTK_CLIST(ctree)->column[i].button,
210                                        GTK_CAN_FOCUS);
211         gtk_container_add(GTK_CONTAINER(scrolledwin), ctree);
212
213         gtk_signal_connect(GTK_OBJECT(ctree), "tree_select_row",
214                            GTK_SIGNAL_FUNC(mimeview_selected), mimeview);
215         gtk_signal_connect(GTK_OBJECT(ctree), "button_press_event",
216                            GTK_SIGNAL_FUNC(mimeview_button_pressed), mimeview);
217         gtk_signal_connect(GTK_OBJECT(ctree), "key_press_event",
218                            GTK_SIGNAL_FUNC(mimeview_key_pressed), mimeview);
219         gtk_signal_connect(GTK_OBJECT (ctree),"start_drag",
220                            GTK_SIGNAL_FUNC (mimeview_start_drag), mimeview);
221         gtk_signal_connect(GTK_OBJECT(ctree), "drag_data_get",
222                            GTK_SIGNAL_FUNC(mimeview_drag_data_get), mimeview);
223
224         mime_notebook = gtk_notebook_new();
225         gtk_widget_show(mime_notebook);
226         GTK_WIDGET_UNSET_FLAGS(mime_notebook, GTK_CAN_FOCUS);
227         gtk_notebook_set_show_tabs(GTK_NOTEBOOK(mime_notebook), FALSE);
228         gtk_notebook_set_show_border(GTK_NOTEBOOK(mime_notebook), FALSE);
229         
230         gtk_container_add(GTK_CONTAINER(notebook), mime_notebook);
231         gtk_notebook_set_page(GTK_NOTEBOOK(notebook), 0);
232                                 
233         icon_vbox = gtk_vbox_new(FALSE,0);
234         icon_scroll = gtk_layout_new(NULL, NULL);
235         gtk_layout_put(GTK_LAYOUT(icon_scroll), icon_vbox, 0, 0);
236         scrollbutton = gtk_vscrollbutton_new(gtk_layout_get_vadjustment(GTK_LAYOUT(icon_scroll)));
237
238         mime_toggle = gtk_toggle_button_new();
239         arrow = gtk_arrow_new(GTK_ARROW_LEFT, GTK_SHADOW_NONE);
240         gtk_widget_show(arrow);
241         gtk_container_add(GTK_CONTAINER(mime_toggle), arrow);
242         gtk_signal_connect(GTK_OBJECT(mime_toggle), "toggled", 
243                            GTK_SIGNAL_FUNC(mime_toggle_button_cb), mimeview);
244
245         icon_mainbox = gtk_vbox_new(FALSE, 0);
246         gtk_box_pack_start(GTK_BOX(icon_mainbox), mime_toggle, FALSE, FALSE, 0);
247         gtk_box_pack_start(GTK_BOX(icon_mainbox), icon_scroll, TRUE, TRUE, 3);
248         gtk_box_pack_end(GTK_BOX(icon_mainbox), scrollbutton, FALSE, FALSE, 0);
249         gtk_signal_connect(GTK_OBJECT(icon_mainbox), "size_allocate", 
250                         GTK_SIGNAL_FUNC(icon_scroll_size_allocate_cb), mimeview);
251         gtk_widget_set_usize(icon_mainbox, ICONBOX_DEFAULT_WIDTH, -1);
252         
253         ctree_mainbox = gtk_vbox_new(FALSE, 0); 
254         gtk_box_pack_start(GTK_BOX(ctree_mainbox), scrolledwin, TRUE, TRUE, 0);
255
256         n_entries = sizeof(mimeview_popup_entries) /
257                 sizeof(mimeview_popup_entries[0]);
258         popupmenu = menu_create_items(mimeview_popup_entries, n_entries,
259                                       "<MimeView>", &popupfactory, mimeview);
260         tooltips = gtk_tooltips_new();
261         gtk_tooltips_set_delay(tooltips, 0); 
262
263         paned = gtk_hpaned_new();
264         gtk_paned_set_gutter_size(GTK_PANED(paned), 0);
265         gtk_paned_pack1(GTK_PANED(paned), notebook, TRUE, TRUE);
266         gtk_paned_pack2(GTK_PANED(paned), icon_mainbox, FALSE, FALSE);
267         
268         
269         gtk_widget_ref(icon_mainbox);
270         gtk_widget_ref(ctree_mainbox);
271
272         gtk_widget_show_all(paned);
273         gtk_widget_show_all(icon_mainbox);
274         gtk_widget_show_all(ctree_mainbox);
275
276         mimeview->notebook      = notebook;
277         mimeview->vbox          = vbox;
278         mimeview->paned         = paned;
279         mimeview->scrolledwin   = scrolledwin;
280         mimeview->ctree         = ctree;
281         mimeview->mime_notebook = mime_notebook;
282         mimeview->popupmenu     = popupmenu;
283         mimeview->popupfactory  = popupfactory;
284         mimeview->type          = -1;
285         mimeview->ctree_mainbox = ctree_mainbox;
286         mimeview->icon_scroll   = icon_scroll;
287         mimeview->icon_vbox     = icon_vbox;
288         mimeview->icon_mainbox  = icon_mainbox;
289         mimeview->icon_count    = 0;
290         mimeview->mainwin       = mainwin;
291         mimeview->tooltips      = tooltips;
292         mimeview->oldsize       = 160;
293
294         mimeview->target_list   = gtk_target_list_new(mimeview_mime_types, 1); 
295         
296         mimeviews = g_slist_prepend(mimeviews, mimeview);
297
298         return mimeview;
299 }
300
301 void mimeview_init(MimeView *mimeview)
302 {
303         textview_init(mimeview->textview);
304
305         gtk_container_add(GTK_CONTAINER(mimeview->mime_notebook),
306                 GTK_WIDGET_PTR(mimeview->textview));
307 }
308
309 /* 
310  * Check whether the message is OpenPGP signed
311  */
312 #if USE_GPGME
313 static gboolean mimeview_is_signed(MimeView *mimeview)
314 {
315         MimeInfo *partinfo = NULL;
316
317         debug_print("mimeview_is signed of %p\n", mimeview);
318
319         if (!mimeview) return FALSE;
320         if (!mimeview->opened) return FALSE;
321
322         debug_print("mimeview_is_signed: open\n" );
323
324         if (!mimeview->file) return FALSE;
325
326         debug_print("mimeview_is_signed: file\n" );
327
328         partinfo = gtk_ctree_node_get_row_data
329                 (GTK_CTREE(mimeview->ctree), mimeview->opened);
330         g_return_val_if_fail(partinfo != NULL, FALSE);
331
332         /* walk the tree and see whether there is a signature somewhere */
333         do {
334                 if (rfc2015_has_signature(partinfo))
335                         return TRUE;
336         } while ((partinfo = partinfo->parent) != NULL);
337
338         debug_print("mimeview_is_signed: FALSE\n" );
339
340         return FALSE;
341 }
342
343 static void set_unchecked_signature(MimeInfo *mimeinfo)
344 {
345         MimeInfo *sig_partinfo;
346
347         sig_partinfo = rfc2015_find_signature(mimeinfo);
348         if (sig_partinfo == NULL) return;
349
350         g_free(sig_partinfo->sigstatus);
351         sig_partinfo->sigstatus =
352                 g_strdup(_("Right-click here to verify the signature"));
353
354         g_free(sig_partinfo->sigstatus_full);
355         sig_partinfo->sigstatus_full = NULL;
356 }
357 #endif /* USE_GPGME */
358
359 void mimeview_show_message(MimeView *mimeview, MimeInfo *mimeinfo,
360                            const gchar *file)
361 {
362         GtkCTree *ctree = GTK_CTREE(mimeview->ctree);
363         GtkCTreeNode *node;
364         FILE *fp;
365
366         mimeview_clear(mimeview);
367         textview_clear(mimeview->messageview->textview);
368
369         g_return_if_fail(file != NULL);
370         g_return_if_fail(mimeinfo != NULL);
371
372         mimeview->mimeinfo = mimeinfo;
373
374         mimeview->file = g_strdup(file);
375
376 #if USE_GPGME
377         if (prefs_common.auto_check_signatures && gpg_started) {
378                 if ((fp = fopen(file, "rb")) == NULL) {
379                         FILE_OP_ERROR(file, "fopen");
380                         return;
381                 }
382                 rfc2015_check_signature(mimeinfo, fp);
383                 fclose(fp);
384         } else
385                 set_unchecked_signature(mimeinfo);
386 #endif
387
388         gtk_signal_handler_block_by_func(GTK_OBJECT(ctree), mimeview_selected,
389                                          mimeview);
390
391         mimeview_set_multipart_tree(mimeview, mimeinfo, NULL);
392         icon_list_create(mimeview, mimeinfo);
393
394         gtk_signal_handler_unblock_by_func(GTK_OBJECT(ctree),
395                                            mimeview_selected, mimeview);
396
397         /* search first text part */
398         for (node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
399              node != NULL; node = GTK_CTREE_NODE_NEXT(node)) {
400                 MimeInfo *partinfo;
401
402                 partinfo = gtk_ctree_node_get_row_data(ctree, node);
403                 if (partinfo &&
404                     (partinfo->mime_type == MIME_TEXT ||
405                      partinfo->mime_type == MIME_TEXT_HTML))
406                         break;
407         }
408         textview_show_message(mimeview->messageview->textview, mimeinfo, file);
409
410         if (!node)
411                 node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
412
413         if (node) {
414                 gtk_ctree_select(ctree, node);
415                 gtkut_ctree_set_focus_row(ctree, node);
416                 gtk_widget_grab_focus(mimeview->ctree);
417         }
418 }
419
420 void mimeview_destroy(MimeView *mimeview)
421 {
422         GSList *cur;
423         
424         for (cur = mimeview->viewers; cur != NULL; cur = g_slist_next(cur)) {
425                 MimeViewer *viewer = (MimeViewer *) cur->data;
426                 gtk_container_remove(GTK_CONTAINER(mimeview->mime_notebook),
427                         GTK_WIDGET(viewer->get_widget(viewer)));
428                 viewer->destroy_viewer(viewer);
429         }
430         g_slist_free(mimeview->viewers);
431         gtk_target_list_unref(mimeview->target_list);
432
433         procmime_mimeinfo_free_all(mimeview->mimeinfo);
434         g_free(mimeview->file);
435         g_free(mimeview);
436
437         mimeviews = g_slist_remove(mimeviews, mimeview);
438         
439         gtk_widget_unref(mimeview->icon_mainbox);
440         gtk_widget_unref(mimeview->ctree_mainbox);
441 }
442
443 MimeInfo *mimeview_get_selected_part(MimeView *mimeview)
444 {
445         if (gtk_notebook_get_current_page
446                 (GTK_NOTEBOOK(mimeview->notebook)) == 0)
447                 return NULL;
448
449         return gtk_ctree_node_get_row_data
450                 (GTK_CTREE(mimeview->ctree), mimeview->opened);
451 }
452
453 static void mimeview_set_multipart_tree(MimeView *mimeview,
454                                         MimeInfo *mimeinfo,
455                                         GtkCTreeNode *parent)
456 {
457         GtkCTreeNode *node;
458
459         g_return_if_fail(mimeinfo != NULL);
460
461         if (mimeinfo->children)
462                 mimeinfo = mimeinfo->children;
463
464         while (mimeinfo != NULL) {
465                 node = mimeview_append_part(mimeview, mimeinfo, parent);
466
467                 if (mimeinfo->children)
468                         mimeview_set_multipart_tree(mimeview, mimeinfo, node);
469                 else if (mimeinfo->sub &&
470                          mimeinfo->sub->mime_type != MIME_TEXT &&
471                          mimeinfo->sub->mime_type != MIME_TEXT_HTML)
472                         mimeview_set_multipart_tree(mimeview, mimeinfo->sub,
473                                                     node);
474                 mimeinfo = mimeinfo->next;
475         }
476 }
477
478 static gchar *get_part_name(MimeInfo *partinfo)
479 {
480 #if USE_GPGME
481         if (partinfo->sigstatus)
482                 return partinfo->sigstatus;
483         else
484 #endif
485         if (partinfo->name)
486                 return partinfo->name;
487         else if (partinfo->filename)
488                 return partinfo->filename;
489         else if (partinfo->description)
490                 return partinfo->description;
491         else
492                 return "";
493 }
494
495 static gchar *get_part_description(MimeInfo *partinfo)
496 {
497         if (partinfo->description)
498                 return partinfo->description;
499         else if (partinfo->name)
500                 return partinfo->name;
501         else if (partinfo->filename)
502                 return partinfo->filename;
503         else
504                 return "";
505 }
506
507 static GtkCTreeNode *mimeview_append_part(MimeView *mimeview,
508                                           MimeInfo *partinfo,
509                                           GtkCTreeNode *parent)
510 {
511         GtkCTree *ctree = GTK_CTREE(mimeview->ctree);
512         GtkCTreeNode *node;
513         gchar *str[N_MIMEVIEW_COLS];
514
515         str[COL_MIMETYPE] =
516                 partinfo->content_type ? partinfo->content_type : "";
517         str[COL_SIZE] = to_human_readable(partinfo->size);
518         if (prefs_common.attach_desc)
519                 str[COL_NAME] = get_part_description(partinfo);
520         else
521                 str[COL_NAME] = get_part_name(partinfo);
522
523         node = gtk_ctree_insert_node(ctree, parent, NULL, str, 0,
524                                      NULL, NULL, NULL, NULL,
525                                      FALSE, TRUE);
526         gtk_ctree_node_set_row_data(ctree, node, partinfo);
527
528         return node;
529 }
530
531 static void mimeview_show_message_part(MimeView *mimeview, MimeInfo *partinfo)
532 {
533         FILE *fp;
534         const gchar *fname;
535 #if USE_GPGME
536         MimeInfo *pi;
537 #endif
538
539         if (!partinfo) return;
540
541 #if USE_GPGME
542         for (pi = partinfo; pi && !pi->plaintextfile ; pi = pi->parent)
543                 ;
544         fname = pi ? pi->plaintextfile : mimeview->file;
545 #else
546         fname = mimeview->file;
547 #endif /* USE_GPGME */
548         if (!fname) return;
549
550         if ((fp = fopen(fname, "rb")) == NULL) {
551                 FILE_OP_ERROR(fname, "fopen");
552                 return;
553         }
554
555         if (fseek(fp, partinfo->fpos, SEEK_SET) < 0) {
556                 FILE_OP_ERROR(mimeview->file, "fseek");
557                 fclose(fp);
558                 return;
559         }
560
561         mimeview_change_view_type(mimeview, MIMEVIEW_TEXT);
562         textview_show_part(mimeview->textview, partinfo, fp);
563
564         fclose(fp);
565 }
566
567 static MimeViewer *get_viewer_for_content_type(MimeView *mimeview, const gchar *content_type)
568 {
569         GSList *cur;
570         MimeViewerFactory *factory = NULL;
571         MimeViewer *viewer = NULL;
572
573 /*
574  * FNM_CASEFOLD is a GNU extension
575  * if its not defined copy the string to the stack and
576  * convert the copy to lower case
577  */
578 #ifndef FNM_CASEFOLD
579 #define FNM_CASEFOLD 0
580         Xstrdup_a(content_type, content_type, return NULL);
581         g_strdown((gchar *)content_type);
582 #endif
583         
584         for (cur = mimeviewer_factories; cur != NULL; cur = g_slist_next(cur)) {
585                 MimeViewerFactory *curfactory = cur->data;
586                 gint i = 0;
587
588                 while (curfactory->content_types[i] != NULL) {
589                         debug_print("%s\n", curfactory->content_types[i]);
590                         if(!fnmatch(curfactory->content_types[i], content_type, FNM_CASEFOLD)) {
591                                 factory = curfactory;
592                                 break;
593                         }
594                         i++;
595                 }
596                 if (factory != NULL)
597                         break;
598         }
599         if (factory == NULL)
600                 return NULL;
601
602         for (cur = mimeview->viewers; cur != NULL; cur = g_slist_next(cur)) {
603                 MimeViewer *curviewer = cur->data;
604                 
605                 if (curviewer->factory == factory)
606                         return curviewer;
607         }
608         viewer = factory->create_viewer();
609         gtk_container_add(GTK_CONTAINER(mimeview->mime_notebook),
610                 GTK_WIDGET(viewer->get_widget(viewer)));
611                 
612         mimeview->viewers = g_slist_append(mimeview->viewers, viewer);
613
614         return viewer;
615 }
616
617 static MimeViewer *get_viewer_for_mimeinfo(MimeView *mimeview, MimeInfo *partinfo)
618 {
619         gchar *content_type = NULL;
620         MimeViewer *viewer = NULL;
621
622         if ((partinfo->mime_type == MIME_APPLICATION_OCTET_STREAM) &&
623             (partinfo->name != NULL)) {
624                 content_type = procmime_get_mime_type(partinfo->name);
625         } else {
626                 content_type = g_strdup(partinfo->content_type);
627         }
628
629         if (content_type != NULL) {
630                 viewer = get_viewer_for_content_type(mimeview, content_type);
631                 g_free(content_type);
632         }
633
634         return viewer;
635 }
636
637 static gboolean mimeview_show_part(MimeView *mimeview, MimeInfo *partinfo)
638 {
639         MimeViewer *viewer;
640         
641         viewer = get_viewer_for_mimeinfo(mimeview, partinfo);
642         if (viewer == NULL) {
643                 if (mimeview->mimeviewer != NULL)
644                         mimeview->mimeviewer->clear_viewer(mimeview->mimeviewer);
645                 mimeview->mimeviewer = NULL;
646                 return FALSE;
647         }
648
649         if (mimeview->mimeviewer != viewer) {
650                 if (mimeview->mimeviewer != NULL)
651                         mimeview->mimeviewer->clear_viewer(mimeview->mimeviewer);
652                 mimeview->mimeviewer = viewer;
653                 mimeview_change_view_type(mimeview, MIMEVIEW_VIEWER);
654         }
655         viewer->show_mimepart(viewer, mimeview->file, partinfo);
656
657         return TRUE;
658 }
659
660 static void mimeview_change_view_type(MimeView *mimeview, MimeViewType type)
661 {
662         TextView  *textview  = mimeview->textview;
663
664         if ((mimeview->type != MIMEVIEW_VIEWER) && 
665             (mimeview->type == type)) return;
666
667         switch (type) {
668         case MIMEVIEW_TEXT:
669                 gtk_notebook_set_page(GTK_NOTEBOOK(mimeview->mime_notebook),
670                         gtk_notebook_page_num(GTK_NOTEBOOK(mimeview->mime_notebook), 
671                         GTK_WIDGET_PTR(textview)));
672                 break;
673         case MIMEVIEW_VIEWER:
674                 gtk_notebook_set_page(GTK_NOTEBOOK(mimeview->mime_notebook),
675                         gtk_notebook_page_num(GTK_NOTEBOOK(mimeview->mime_notebook), 
676                         GTK_WIDGET(mimeview->mimeviewer->get_widget(mimeview->mimeviewer))));
677                 break;
678         default:
679                 return;
680         }
681
682         mimeview->type = type;
683 }
684
685 static void mimeview_clear(MimeView *mimeview)
686 {
687         GtkCList *clist = GTK_CLIST(mimeview->ctree);
688
689         procmime_mimeinfo_free_all(mimeview->mimeinfo);
690         mimeview->mimeinfo = NULL;
691
692         gtk_clist_clear(clist);
693         textview_clear(mimeview->textview);
694         if (mimeview->mimeviewer != NULL)
695                 mimeview->mimeviewer->clear_viewer(mimeview->mimeviewer);
696
697         mimeview->opened = NULL;
698
699         g_free(mimeview->file);
700         mimeview->file = NULL;
701         icon_list_clear(mimeview);
702
703         /* gtk_notebook_set_page(GTK_NOTEBOOK(mimeview->notebook), 0); */
704 }
705
706 static void mimeview_selected(GtkCTree *ctree, GtkCTreeNode *node, gint column,
707                               MimeView *mimeview)
708 {
709         MimeInfo *partinfo;
710
711         if (mimeview->opened == node) return;
712         mimeview->opened = node;
713         gtk_ctree_node_moveto(ctree, node, -1, 0.5, 0);
714
715         partinfo = gtk_ctree_node_get_row_data(ctree, node);
716         if (!partinfo) return;
717
718         /* ungrab the mouse event */
719         if (GTK_WIDGET_HAS_GRAB(ctree)) {
720                 gtk_grab_remove(GTK_WIDGET(ctree));
721                 if (gdk_pointer_is_grabbed())
722                         gdk_pointer_ungrab(GDK_CURRENT_TIME);
723         }
724         
725         mimeview->textview->default_text = FALSE;
726         
727         if (!mimeview_show_part(mimeview, partinfo)) {
728                 switch (partinfo->mime_type) {
729                 case MIME_TEXT:
730                 case MIME_TEXT_HTML:
731                 case MIME_TEXT_ENRICHED:
732                 case MIME_MESSAGE_RFC822:
733                 case MIME_MULTIPART:
734                         mimeview_show_message_part(mimeview, partinfo);
735                 
736                         break;
737                 default:
738                         mimeview->textview->default_text = TRUE;        
739                         mimeview_change_view_type(mimeview, MIMEVIEW_TEXT);
740 #if USE_GPGME
741                         if (g_strcasecmp(partinfo->content_type,
742                                          "application/pgp-signature") == 0)
743                                 textview_show_signature_part(mimeview->textview,
744                                                              partinfo);
745                         else
746 #endif
747                                 textview_show_mime_part(mimeview->textview, partinfo);
748                         break;
749                 }
750         }
751 }
752
753 static void mimeview_start_drag(GtkWidget *widget, gint button,
754                                 GdkEvent *event, MimeView *mimeview)
755 {
756         GdkDragContext *context;
757         MimeInfo *partinfo;
758
759         g_return_if_fail(mimeview != NULL);
760
761         partinfo = mimeview_get_selected_part(mimeview);
762         if (partinfo->filename == NULL && partinfo->name == NULL) return;
763
764         context = gtk_drag_begin(widget, mimeview->target_list,
765                                  GDK_ACTION_COPY, button, event);
766         gtk_drag_set_icon_default(context);
767 }
768
769 static gint mimeview_button_pressed(GtkWidget *widget, GdkEventButton *event,
770                                     MimeView *mimeview)
771 {
772         GtkCList *clist = GTK_CLIST(widget);
773         gint row, column;
774
775         if (!event) return FALSE;
776
777         if (event->button == 2 || event->button == 3) {
778                 if (!gtk_clist_get_selection_info(clist, event->x, event->y,
779                                                   &row, &column))
780                         return FALSE;
781                 gtk_clist_unselect_all(clist);
782                 gtk_clist_select_row(clist, row, column);
783                 gtkut_clist_set_focus_row(clist, row);
784         }
785         part_button_pressed(mimeview, event, mimeview_get_selected_part(mimeview));
786
787         return TRUE;
788 }
789
790 static void part_button_pressed(MimeView *mimeview, GdkEventButton *event, 
791                                 MimeInfo *partinfo)
792 {
793         if (event->button == 2 ||
794             (event->button == 1 && event->type == GDK_2BUTTON_PRESS)) {
795                 /* call external program for image, audio or html */
796                 mimeview_launch(mimeview);
797         } else if (event->button == 3) {
798                 if (partinfo && (partinfo->mime_type == MIME_TEXT ||
799                                  partinfo->mime_type == MIME_TEXT_HTML ||
800                                  partinfo->mime_type == MIME_TEXT_ENRICHED ||
801                                  partinfo->mime_type == MIME_MESSAGE_RFC822 ||
802                                  partinfo->mime_type == MIME_IMAGE ||
803                                  partinfo->mime_type == MIME_MULTIPART))
804                         menu_set_sensitive(mimeview->popupfactory,
805                                            "/Display as text", FALSE);
806                 else
807                         menu_set_sensitive(mimeview->popupfactory,
808                                            "/Display as text", TRUE);
809                 if (partinfo &&
810                     partinfo->mime_type == MIME_APPLICATION_OCTET_STREAM)
811                         menu_set_sensitive(mimeview->popupfactory,
812                                            "/Open", FALSE);
813                 else
814                         menu_set_sensitive(mimeview->popupfactory,
815                                            "/Open", TRUE);
816
817 #if USE_GPGME
818                 menu_set_sensitive(mimeview->popupfactory,
819                                    "/Check signature",
820                                    mimeview_is_signed(mimeview));
821 #endif
822
823                 gtk_menu_popup(GTK_MENU(mimeview->popupmenu),
824                                NULL, NULL, NULL, NULL,
825                                event->button, event->time);
826         }
827
828 }
829
830
831 void mimeview_pass_key_press_event(MimeView *mimeview, GdkEventKey *event)
832 {
833         mimeview_key_pressed(mimeview->ctree, event, mimeview);
834 }
835
836 #define BREAK_ON_MODIFIER_KEY() \
837         if ((event->state & (GDK_MOD1_MASK|GDK_CONTROL_MASK)) != 0) break
838
839 #define KEY_PRESS_EVENT_STOP() \
840         if (gtk_signal_n_emissions_by_name \
841                 (GTK_OBJECT(ctree), "key_press_event") > 0) { \
842                 gtk_signal_emit_stop_by_name(GTK_OBJECT(ctree), \
843                                              "key_press_event"); \
844         }
845
846 static gint mimeview_key_pressed(GtkWidget *widget, GdkEventKey *event,
847                                  MimeView *mimeview)
848 {
849         SummaryView *summaryview;
850         GtkCTree *ctree = GTK_CTREE(widget);
851         GtkCTreeNode *node;
852
853         if (!event) return FALSE;
854         if (!mimeview->opened) return FALSE;
855
856         switch (event->keyval) {
857         case GDK_space:
858                 if (textview_scroll_page(mimeview->textview, FALSE))
859                         return TRUE;
860
861                 node = GTK_CTREE_NODE_NEXT(mimeview->opened);
862                 if (node) {
863                         gtk_sctree_unselect_all(GTK_SCTREE(ctree));
864                         gtk_sctree_select(GTK_SCTREE(ctree), node);
865                         return TRUE;
866                 }
867                 break;
868         case GDK_BackSpace:
869                 textview_scroll_page(mimeview->textview, TRUE);
870                 return TRUE;
871         case GDK_Return:
872                 textview_scroll_one_line(mimeview->textview,
873                                          (event->state & GDK_MOD1_MASK) != 0);
874                 return TRUE;
875         case GDK_n:
876         case GDK_N:
877                 BREAK_ON_MODIFIER_KEY();
878                 if (!GTK_CTREE_NODE_NEXT(mimeview->opened)) break;
879                 KEY_PRESS_EVENT_STOP();
880
881                 gtk_signal_emit_by_name(GTK_OBJECT(ctree), "scroll_vertical",
882                                         GTK_SCROLL_STEP_FORWARD, 0.0);
883                 return TRUE;
884         case GDK_p:
885         case GDK_P:
886                 BREAK_ON_MODIFIER_KEY();
887                 if (!GTK_CTREE_NODE_PREV(mimeview->opened)) break;
888                 KEY_PRESS_EVENT_STOP();
889
890                 gtk_signal_emit_by_name(GTK_OBJECT(ctree), "scroll_vertical",
891                                         GTK_SCROLL_STEP_BACKWARD, 0.0);
892                 return TRUE;
893         case GDK_y:
894                 BREAK_ON_MODIFIER_KEY();
895                 KEY_PRESS_EVENT_STOP();
896                 mimeview_save_as(mimeview);
897                 return TRUE;
898         case GDK_t:
899                 BREAK_ON_MODIFIER_KEY();
900                 KEY_PRESS_EVENT_STOP();
901                 mimeview_display_as_text(mimeview);
902                 return TRUE;    
903         case GDK_l:
904                 BREAK_ON_MODIFIER_KEY();
905                 KEY_PRESS_EVENT_STOP();
906                 mimeview_launch(mimeview);
907                 return TRUE;
908         default:
909                 break;
910         }
911
912         if (!mimeview->messageview->mainwin) return FALSE;
913         summaryview = mimeview->messageview->mainwin->summaryview;
914         summary_pass_key_press_event(summaryview, event);
915         return TRUE;
916 }
917
918 static void mimeview_drag_data_get(GtkWidget        *widget,
919                                    GdkDragContext   *drag_context,
920                                    GtkSelectionData *selection_data,
921                                    guint             info,
922                                    guint             time,
923                                    MimeView         *mimeview)
924 {
925         gchar *filename, *uriname;
926         MimeInfo *partinfo;
927
928         if (!mimeview->opened) return;
929         if (!mimeview->file) return;
930
931         partinfo = mimeview_get_selected_part(mimeview);
932         if (!partinfo) return;
933         if (!partinfo->filename && !partinfo->name) return;
934
935         filename = partinfo->filename ? partinfo->filename : partinfo->name;
936         filename = g_basename(filename);
937         if (*filename == '\0') return;
938
939         filename = g_strconcat(get_mime_tmp_dir(), G_DIR_SEPARATOR_S,
940                                filename, NULL);
941
942         if (procmime_get_part(filename, mimeview->file, partinfo) < 0)
943                 alertpanel_error
944                         (_("Can't save the part of multipart message."));
945
946         uriname = g_strconcat("file:/", filename, NULL);
947         gtk_selection_data_set(selection_data, selection_data->target, 8,
948                                uriname, strlen(uriname));
949
950         g_free(uriname);
951         g_free(filename);
952 }
953
954 static void mimeview_save_all(MimeView *mimeview)
955 {
956         gchar *dirname;
957         gchar *defname = NULL;
958         MimeInfo *partinfo;
959         MimeInfo *attachment;
960         gchar buf[1024];
961
962         if (!mimeview->opened) return;
963         if (!mimeview->file) return;
964
965         partinfo = mimeview_get_selected_part(mimeview);
966         g_return_if_fail(partinfo != NULL);
967
968         dirname = filesel_select_file(_("Save as"), defname);
969         if (!dirname) return;
970
971         /* return to first children */
972         if (!partinfo->parent->children) return;  /* multipart container? */
973         attachment = partinfo->parent->children->next;
974         /* for each attachment, extract it in the selected dir. */
975         while (attachment != NULL) {
976                 static guint subst_cnt = 1;
977                 gchar *attachdir;
978                 gchar *attachname = g_strdup(get_part_name(attachment));
979                 AlertValue aval = G_ALERTDEFAULT;
980                 gchar *res;
981
982                 if (!attachname || !strlen(attachname))
983                         attachname = g_strdup_printf("noname.%d",subst_cnt++);
984                 subst_chars(attachname, ":?*&|<>\t\r\n", '_');
985                 g_snprintf(buf, sizeof(buf), "%s%s",
986                            dirname,
987                            (attachname[0] == G_DIR_SEPARATOR)
988                            ? &attachname[1]
989                            : attachname);
990                 subst_chars(buf, "/\\", G_DIR_SEPARATOR);
991                 attachdir = g_dirname(buf);
992                 make_dir_hier(attachdir);
993                 g_free(attachdir);
994
995                 if (is_file_exist(buf)) {
996                         res = g_strdup_printf(_("Overwrite existing file '%s'?"),
997                                               attachname);
998                         aval = alertpanel(_("Overwrite"), res, _("OK"), 
999                                           _("Cancel"), NULL);
1000                         g_free(res);                                      
1001                 }
1002                 g_free(attachname);
1003
1004                 if ((G_ALERTDEFAULT != aval) || (procmime_get_part(buf, mimeview->file, attachment) < 0))
1005                         alertpanel_error(_("Can't save the part of multipart message."));
1006                 attachment = attachment->next;
1007         }
1008 }
1009
1010 static void mimeview_display_as_text(MimeView *mimeview)
1011 {
1012         MimeInfo *partinfo;
1013
1014         if (!mimeview->opened) return;
1015
1016         partinfo = mimeview_get_selected_part(mimeview);
1017         g_return_if_fail(partinfo != NULL);
1018         mimeview_show_message_part(mimeview, partinfo);
1019 }
1020
1021 static void mimeview_save_as(MimeView *mimeview)
1022 {
1023         gchar *filename;
1024         gchar *defname = NULL;
1025         MimeInfo *partinfo;
1026         gchar *res;
1027
1028         if (!mimeview->opened) return;
1029         if (!mimeview->file) return;
1030
1031         partinfo = mimeview_get_selected_part(mimeview);
1032         g_return_if_fail(partinfo != NULL);
1033
1034         if (partinfo->filename)
1035                 defname = partinfo->filename;
1036         else if (partinfo->name) {
1037                 Xstrdup_a(defname, partinfo->name, return);
1038                 subst_for_filename(defname);
1039         }
1040
1041         filename = filesel_select_file(_("Save as"), defname);
1042         if (!filename) return;
1043         if (is_file_exist(filename)) {
1044                 AlertValue aval;
1045                 res = g_strdup_printf(_("Overwrite existing file '%s'?"),
1046                                       filename);
1047                 aval = alertpanel(_("Overwrite"), res, _("OK"), 
1048                                   _("Cancel"), NULL);
1049                 g_free(res);                                      
1050                 if (G_ALERTDEFAULT != aval) return;
1051         }
1052
1053         if (procmime_get_part(filename, mimeview->file, partinfo) < 0)
1054                 alertpanel_error
1055                         (_("Can't save the part of multipart message."));
1056 }
1057
1058 static void mimeview_launch(MimeView *mimeview)
1059 {
1060         MimeInfo *partinfo;
1061         gchar *filename;
1062
1063         if (!mimeview->opened) return;
1064         if (!mimeview->file) return;
1065
1066         partinfo = mimeview_get_selected_part(mimeview);
1067         g_return_if_fail(partinfo != NULL);
1068
1069         filename = procmime_get_tmp_file_name(partinfo);
1070
1071         if (procmime_get_part(filename, mimeview->file, partinfo) < 0)
1072                 alertpanel_error
1073                         (_("Can't save the part of multipart message."));
1074         else
1075                 mimeview_view_file(filename, partinfo, NULL);
1076
1077         g_free(filename);
1078 }
1079
1080 static void mimeview_open_with(MimeView *mimeview)
1081 {
1082         MimeInfo *partinfo;
1083         gchar *filename;
1084         gchar *cmd;
1085
1086         if (!mimeview->opened) return;
1087         if (!mimeview->file) return;
1088
1089         partinfo = mimeview_get_selected_part(mimeview);
1090         g_return_if_fail(partinfo != NULL);
1091
1092         filename = procmime_get_tmp_file_name(partinfo);
1093
1094         if (procmime_get_part(filename, mimeview->file, partinfo) < 0) {
1095                 alertpanel_error
1096                         (_("Can't save the part of multipart message."));
1097                 g_free(filename);
1098                 return;
1099         }
1100
1101         if (!prefs_common.mime_open_cmd_history)
1102                 prefs_common.mime_open_cmd_history =
1103                         add_history(NULL, prefs_common.mime_open_cmd);
1104
1105         cmd = input_dialog_combo
1106                 (_("Open with"),
1107                  _("Enter the command line to open file:\n"
1108                    "(`%s' will be replaced with file name)"),
1109                  prefs_common.mime_open_cmd,
1110                  prefs_common.mime_open_cmd_history,
1111                  TRUE);
1112         if (cmd) {
1113                 mimeview_view_file(filename, partinfo, cmd);
1114                 g_free(prefs_common.mime_open_cmd);
1115                 prefs_common.mime_open_cmd = cmd;
1116                 prefs_common.mime_open_cmd_history =
1117                         add_history(prefs_common.mime_open_cmd_history, cmd);
1118         }
1119
1120         g_free(filename);
1121 }
1122
1123 static void mimeview_view_file(const gchar *filename, MimeInfo *partinfo,
1124                                const gchar *cmdline)
1125 {
1126         static gchar *default_image_cmdline = "display '%s'";
1127         static gchar *default_audio_cmdline = "play '%s'";
1128         static gchar *default_html_cmdline = DEFAULT_BROWSER_CMD;
1129         static gchar *mime_cmdline = "metamail -d -b -x -c %s '%s'";
1130         gchar buf[1024];
1131         gchar m_buf[1024];
1132         const gchar *cmd;
1133         const gchar *def_cmd;
1134         const gchar *p;
1135
1136         if (cmdline) {
1137                 cmd = cmdline;
1138                 def_cmd = NULL;
1139         } else if (MIME_APPLICATION_OCTET_STREAM == partinfo->mime_type) {
1140                 return;
1141         } else if (MIME_IMAGE == partinfo->mime_type) {
1142                 cmd = prefs_common.mime_image_viewer;
1143                 def_cmd = default_image_cmdline;
1144         } else if (MIME_AUDIO == partinfo->mime_type) {
1145                 cmd = prefs_common.mime_audio_player;
1146                 def_cmd = default_audio_cmdline;
1147         } else if (MIME_TEXT_HTML == partinfo->mime_type) {
1148                 cmd = prefs_common.uri_cmd;
1149                 def_cmd = default_html_cmdline;
1150         } else {
1151                 g_snprintf(m_buf, sizeof(m_buf), mime_cmdline,
1152                            partinfo->content_type, "%s");
1153                 cmd = m_buf;
1154                 def_cmd = NULL;
1155         }
1156
1157         if (cmd && (p = strchr(cmd, '%')) && *(p + 1) == 's' &&
1158             !strchr(p + 2, '%'))
1159                 g_snprintf(buf, sizeof(buf), cmd, filename);
1160         else {
1161                 if (cmd)
1162                         g_warning("MIME viewer command line is invalid: `%s'", cmd);
1163                 if (def_cmd)
1164                         g_snprintf(buf, sizeof(buf), def_cmd, filename);
1165                 else
1166                         return;
1167         }
1168
1169         execute_command_line(buf, TRUE);
1170 }
1171
1172 #if USE_GPGME
1173 static void update_node_name(GtkCTree *ctree, GtkCTreeNode *node,
1174                              gpointer data)
1175 {
1176         MimeInfo *partinfo;
1177         gchar *part_name;
1178
1179         partinfo = gtk_ctree_node_get_row_data(ctree, node);
1180         g_return_if_fail(partinfo != NULL);
1181
1182         part_name = get_part_name(partinfo);
1183         gtk_ctree_node_set_text(ctree, node, COL_NAME, part_name);
1184 }
1185
1186 static void mimeview_update_names(MimeView *mimeview)
1187 {
1188         GtkCTree *ctree = GTK_CTREE(mimeview->ctree);
1189
1190         gtk_ctree_pre_recursive(ctree, NULL, update_node_name, NULL);
1191 }
1192
1193 static void mimeview_update_signature_info(MimeView *mimeview)
1194 {
1195         MimeInfo *partinfo;
1196
1197         if (!mimeview) return;
1198         if (!mimeview->opened) return;
1199
1200         partinfo = mimeview_get_selected_part(mimeview);
1201         if (!partinfo) return;
1202
1203         if (g_strcasecmp(partinfo->content_type,
1204                          "application/pgp-signature") == 0) {
1205                 mimeview_change_view_type(mimeview, MIMEVIEW_TEXT);
1206                 textview_show_signature_part(mimeview->textview, partinfo);
1207         }
1208 }
1209
1210 void mimeview_check_signature(MimeView *mimeview)
1211 {
1212         MimeInfo *mimeinfo;
1213         FILE *fp;
1214
1215         g_return_if_fail (mimeview_is_signed(mimeview));
1216         g_return_if_fail (gpg_started);
1217
1218         mimeinfo = gtk_ctree_node_get_row_data
1219                 (GTK_CTREE(mimeview->ctree), mimeview->opened);
1220         g_return_if_fail(mimeinfo != NULL);
1221         g_return_if_fail(mimeview->file != NULL);
1222
1223         while (mimeinfo->parent)
1224                 mimeinfo = mimeinfo->parent;
1225
1226         if ((fp = fopen(mimeview->file, "rb")) == NULL) {
1227                 FILE_OP_ERROR(mimeview->file, "fopen");
1228                 return;
1229         }
1230
1231         rfc2015_check_signature(mimeinfo, fp);
1232         fclose(fp);
1233
1234         mimeview_update_names(mimeview);
1235         mimeview_update_signature_info(mimeview);
1236
1237         textview_show_message(mimeview->messageview->textview, mimeinfo,
1238                               mimeview->file);
1239 }
1240 #endif /* USE_GPGME */
1241
1242 void mimeview_register_viewer_factory(MimeViewerFactory *factory)
1243 {
1244         mimeviewer_factories = g_slist_append(mimeviewer_factories, factory);
1245 }
1246
1247 static gint cmp_viewer_by_factroy(gconstpointer a, gconstpointer b)
1248 {
1249         return ((MimeViewer *) a)->factory == (MimeViewerFactory *) b ? 0 : -1;
1250 }
1251
1252 void mimeview_unregister_viewer_factory(MimeViewerFactory *factory)
1253 {
1254         GSList *mimeview_list, *viewer_list;
1255
1256         for (mimeview_list = mimeviews; mimeview_list != NULL; mimeview_list = g_slist_next(mimeview_list)) {
1257                 MimeView *mimeview = (MimeView *) mimeview_list->data;
1258                 
1259                 if (mimeview->mimeviewer && mimeview->mimeviewer->factory == factory) {
1260                         mimeview_change_view_type(mimeview, MIMEVIEW_TEXT);
1261                         mimeview->mimeviewer = NULL;
1262                 }
1263
1264                 while ((viewer_list = g_slist_find_custom(mimeview->viewers, factory, cmp_viewer_by_factroy)) != NULL) {
1265                         MimeViewer *mimeviewer = (MimeViewer *) viewer_list->data;
1266
1267                         mimeviewer->destroy_viewer(mimeviewer);
1268                         mimeview->viewers = g_slist_remove(mimeview->viewers, mimeviewer);
1269                 }
1270         }
1271
1272         mimeviewer_factories = g_slist_remove(mimeviewer_factories, factory);
1273 }
1274
1275 static gboolean icon_clicked_cb (GtkWidget *button, GdkEventButton *event, MimeView *mimeview)
1276 {
1277         gint num;
1278         MimeInfo *partinfo;
1279         GtkCTreeNode *node;
1280         num = GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(button), "icon_number"));
1281         partinfo = gtk_object_get_data(GTK_OBJECT(button), "partinfo");
1282         if (num == 1 && (partinfo->mime_type == MIME_TEXT ||  
1283                          partinfo->mime_type == MIME_TEXT_HTML))  {
1284                 gtk_notebook_set_page(GTK_NOTEBOOK(mimeview->notebook), 0);
1285                 /* don't set the ctree, as it will unload the plugin, and
1286                  * we want to be able to switch quickly between the text
1287                  * part and the attachment */
1288         } else {
1289                 gtk_notebook_set_page(GTK_NOTEBOOK(mimeview->notebook), 1);
1290                 node = gtk_ctree_find_by_row_data(GTK_CTREE(mimeview->ctree), NULL, partinfo);
1291                 if (node)
1292                         gtk_ctree_select(GTK_CTREE(mimeview->ctree), node);
1293         }
1294         part_button_pressed(mimeview, event, partinfo);
1295         return TRUE;
1296 }
1297
1298 static void icon_list_append_icon (MimeView *mimeview, MimeInfo *mimeinfo) 
1299 {
1300         GtkWidget *pixmap;
1301         GtkWidget *vbox;
1302         GtkWidget *icon_scroll = mimeview->icon_scroll;
1303         GtkWidget *button;
1304         GtkWidget *sep;
1305         GtkTooltips *tooltips = mimeview->tooltips;
1306         gchar *tip;
1307         gchar *desc = NULL;
1308         gint width;
1309         StockPixmap stockp;
1310         
1311         vbox = mimeview->icon_vbox;
1312         mimeview->icon_count++;
1313         if (mimeview->icon_count > 1) {
1314                 sep = gtk_hseparator_new();
1315                 gtk_widget_show(sep);
1316                 gtk_widget_set_usize(sep, ICONBOX_DEFAULT_WIDTH, -1);
1317                 gtk_box_pack_start(GTK_BOX(vbox), sep, FALSE, FALSE, 3);
1318         }
1319         button = gtk_event_box_new();
1320         gtk_object_set_data(GTK_OBJECT(button), "icon_number", 
1321                 GINT_TO_POINTER(mimeview->icon_count));
1322         gtk_object_set_data(GTK_OBJECT(button), "partinfo", 
1323                 mimeinfo);
1324         
1325         switch (mimeinfo->mime_type) {
1326                 
1327         case MIME_TEXT:
1328         case MIME_MESSAGE_RFC822:
1329                 stockp = STOCK_PIXMAP_MIME_TEXT_PLAIN;
1330                 break;
1331         case MIME_TEXT_HTML:
1332                 stockp = STOCK_PIXMAP_MIME_TEXT_HTML;
1333                 break;
1334         case MIME_APPLICATION:
1335 #ifdef USE_GPGME        
1336                 if (mimeinfo->content_type
1337                         &&  g_strcasecmp(mimeinfo->content_type, 
1338                                          "application/pgp-signature")  == 0) {
1339                         if (mimeinfo->sigstatus_full) {
1340                                 desc = mimeinfo->sigstatus;
1341                                 if (mimeinfo->sig_ok)
1342                                         stockp = STOCK_PIXMAP_MIME_GPG_PASSED;
1343                                 else
1344                                         stockp = STOCK_PIXMAP_MIME_GPG_FAILED;
1345                         } else
1346                                 stockp = STOCK_PIXMAP_MIME_GPG_SIGNED;
1347                 } else
1348 #endif  
1349                         stockp = STOCK_PIXMAP_MIME_APPLICATION;
1350                 break;
1351         case MIME_APPLICATION_OCTET_STREAM:
1352                 stockp = STOCK_PIXMAP_MIME_APPLICATION_OCTET_STREAM;
1353                 break;
1354         case MIME_IMAGE:
1355                 stockp = STOCK_PIXMAP_MIME_IMAGE;
1356                 break;
1357         case MIME_AUDIO:
1358                 stockp = STOCK_PIXMAP_MIME_AUDIO;
1359                 break;
1360         case MIME_TEXT_ENRICHED:
1361                 stockp = STOCK_PIXMAP_MIME_TEXT_ENRICHED;
1362                 break;
1363         default:
1364                 stockp = STOCK_PIXMAP_MIME_UNKNOWN;
1365                 break;
1366         }
1367         
1368         pixmap = stock_pixmap_widget(mimeview->mainwin->window, stockp);
1369         gtk_container_add(GTK_CONTAINER(button), pixmap);
1370         
1371         width = MAX(ICONBOX_DEFAULT_WIDTH, pixmap->requisition.width);
1372         if (width > mimeview->icon_mainbox->requisition.width) {
1373                 gtk_widget_set_usize(mimeview->icon_mainbox, 
1374                                      width, -1);
1375         }
1376         if (!desc) {
1377                 if (prefs_common.attach_desc)
1378                         desc = get_part_description(mimeinfo);
1379                 else
1380                         desc = get_part_name(mimeinfo);
1381         }
1382         if (desc && *desc)
1383                 tip = g_strdup_printf("%s\n%s\n%s", desc, mimeinfo->content_type, 
1384                                 to_human_readable(mimeinfo->size), NULL);
1385         else            
1386                 tip = g_strdup_printf("%s\n%s", mimeinfo->content_type, 
1387                                 to_human_readable(mimeinfo->size), NULL);
1388
1389         gtk_tooltips_set_tip(mimeview->tooltips, button, tip, NULL);
1390         g_free(tip);
1391         gtk_widget_show_all(button);
1392         gtk_signal_connect(GTK_OBJECT(button), "button_press_event", 
1393                            GTK_SIGNAL_FUNC(icon_clicked_cb), mimeview);
1394         gtk_box_pack_start(GTK_BOX(vbox), button, FALSE, FALSE, 0);
1395         
1396 }
1397
1398 static void icon_list_clear (MimeView *mimeview)
1399 {
1400         GtkWidget *wid;
1401         GList     *child;
1402         GtkAdjustment *adj;
1403         
1404         child = gtk_container_children(GTK_CONTAINER(mimeview->icon_vbox));
1405         for (; child != NULL; child = g_list_next(child)) {
1406                 gtkut_container_remove(GTK_CONTAINER(mimeview->icon_vbox), 
1407                                        GTK_WIDGET(child->data));
1408         }
1409         mimeview->icon_count = 0;
1410         adj  = gtk_layout_get_vadjustment(GTK_LAYOUT(mimeview->icon_scroll));
1411         adj->value = 0;
1412         gtk_adjustment_value_changed(adj);
1413         gtk_signal_handler_block_by_func(GTK_OBJECT(mimeview->icon_mainbox),
1414                 GTK_SIGNAL_FUNC(icon_scroll_size_allocate_cb), mimeview);
1415         gtk_widget_set_usize(mimeview->icon_mainbox, ICONBOX_DEFAULT_WIDTH, -1);
1416         gtk_signal_handler_unblock_by_func(GTK_OBJECT(mimeview->icon_mainbox),
1417                 GTK_SIGNAL_FUNC(icon_scroll_size_allocate_cb), mimeview);
1418         
1419 }
1420
1421 static void icon_scroll_size_allocate_cb(GtkWidget *widget, 
1422                                         GtkAllocation *size, MimeView *mimeview)
1423 {
1424         GtkAllocation *mainbox_size;
1425         GtkAllocation *vbox_size;
1426         GtkAllocation *layout_size;
1427         GtkAdjustment *adj;
1428         GtkWidget     *wid;
1429         
1430         adj = gtk_layout_get_vadjustment(GTK_LAYOUT(mimeview->icon_scroll));
1431
1432         mainbox_size = &mimeview->icon_mainbox->allocation;
1433         vbox_size = &mimeview->icon_vbox->allocation;
1434         layout_size = &mimeview->icon_scroll->allocation;
1435         
1436         /* centralise the vbox */
1437         gtk_layout_move(GTK_LAYOUT(mimeview->icon_scroll), mimeview->icon_vbox, 
1438                         (mainbox_size->width - vbox_size->width)/2, 0);
1439         
1440         gtk_layout_set_size(GTK_LAYOUT(mimeview->icon_scroll), GTK_LAYOUT(mimeview->icon_scroll)->width, MAX(vbox_size->height, layout_size->height));
1441         adj->step_increment = 5;
1442 }
1443
1444 static void icon_list_create(MimeView *mimeview, MimeInfo *mimeinfo)
1445 {
1446         g_return_if_fail(mimeinfo != NULL);
1447
1448         if (mimeinfo->children)
1449                 mimeinfo = mimeinfo->children;
1450
1451         while (mimeinfo != NULL) {
1452                 if (mimeinfo->children)
1453                         icon_list_create(mimeview, mimeinfo);
1454                 else if (mimeinfo->sub &&
1455                          mimeinfo->sub->mime_type != MIME_TEXT &&
1456                          mimeinfo->sub->mime_type != MIME_TEXT_HTML)
1457                         icon_list_create(mimeview, mimeinfo->sub);
1458                 else 
1459                         icon_list_append_icon(mimeview, mimeinfo);
1460                 mimeinfo = mimeinfo->next;
1461         }
1462 }
1463
1464 static void mime_toggle_button_cb (GtkWidget *button, MimeView *mimeview) 
1465 {
1466         GtkWidget *paned = mimeview->paned;
1467         gtk_widget_ref(button); 
1468
1469         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button))) {
1470                 gtk_arrow_set(GTK_ARROW(GTK_BIN(button)->child), GTK_ARROW_RIGHT, 
1471                                         GTK_SHADOW_NONE);
1472                 gtkut_container_remove(GTK_CONTAINER(paned), 
1473                                        mimeview->icon_mainbox);
1474                 gtk_paned_pack2(GTK_PANED(paned), 
1475                                 mimeview->ctree_mainbox, 
1476                                 FALSE, TRUE);
1477                 gtk_paned_set_gutter_size(GTK_PANED(paned), -1);
1478                 gtk_widget_set_usize(mimeview->ctree_mainbox, mimeview->oldsize, -1);
1479                 gtk_paned_set_position(GTK_PANED(paned),  paned->allocation.width 
1480                                        - mimeview->oldsize
1481                                        - GTK_PANED (paned)->gutter_size);
1482                 
1483                 gtkut_container_remove(GTK_CONTAINER(mimeview->icon_mainbox), 
1484                                         button);
1485                 gtk_box_pack_start(GTK_BOX(mimeview->ctree_mainbox), 
1486                                    button, FALSE, FALSE, 0);
1487                 gtk_notebook_set_page(GTK_NOTEBOOK(mimeview->notebook), 1); 
1488         } else {
1489                 gtk_arrow_set(GTK_ARROW(GTK_BIN(button)->child), GTK_ARROW_LEFT, 
1490                               GTK_SHADOW_NONE);
1491                 mimeview->oldsize = mimeview->ctree_mainbox->allocation.width;
1492                 gtkut_container_remove(GTK_CONTAINER(paned), 
1493                                        mimeview->ctree_mainbox);
1494                 gtk_paned_pack2(GTK_PANED(paned), mimeview->icon_mainbox, 
1495                                 FALSE, FALSE);
1496                 gtk_paned_set_gutter_size(GTK_PANED(paned), 0);
1497                 gtk_paned_set_position(GTK_PANED(paned), -1);
1498
1499                 gtkut_container_remove(GTK_CONTAINER(mimeview->ctree_mainbox), 
1500                                         button);
1501                 gtk_box_pack_start(GTK_BOX(mimeview->icon_mainbox), 
1502                                    button, FALSE, FALSE, 0);
1503         }
1504                 
1505         gtk_widget_unref(button);
1506         gtk_box_reorder_child(GTK_BOX(button->parent), button, 0);
1507
1508 }
1509
1510 void mimeview_update (MimeView *mimeview) {
1511         if (mimeview && mimeview->mimeinfo) {
1512                 icon_list_clear(mimeview);
1513                 icon_list_create(mimeview, mimeview->mimeinfo);
1514         }
1515 }