Add a plugin method to allow updating stored passwords on master password change.
[claws.git] / src / plugins / pdf_viewer / poppler_viewer.c
1 /*
2  * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright(C) 1999-2007 the Claws Mail Team
4  * This file Copyright (C) 2007 Salvatore De Paolis 
5  * <iwkse@claws-mail.org> 
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 3 of the License, or
10  *(at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #  include "config.h"
24 #include "claws-features.h"
25 #endif
26
27 #include <glib.h>
28 #include <glib/gi18n.h>
29
30 #include "poppler_viewer.h"
31 #include "printing.h"
32 #include "prefs_common.h"
33 #include "gtk/gtkutils.h"
34 #include "gtk/inputdialog.h"
35 #include "mimeview.h"
36 #ifndef POPPLER_WITH_GDK
37 #include "stdbool.h"
38 #endif
39
40 static FileType pdf_viewer_mimepart_get_type(MimeInfo *partinfo);
41 static MimeViewerFactory pdf_viewer_factory;
42
43 static void pdf_viewer_show_mimepart(MimeViewer *_viewer, const gchar *infile,
44                                 MimeInfo *partinfo);
45
46 static MimeViewer *pdf_viewer_create(void);
47 static void pdf_viewer_clear(MimeViewer *_viewer);
48 static void pdf_viewer_destroy(MimeViewer *_viewer);
49 static void pdf_viewer_update(MimeViewer *_viewer, gboolean reload_file, int page_num);
50
51 static GtkWidget *pdf_viewer_get_widget(MimeViewer *_viewer);
52
53 static void pdf_viewer_hide_index_pane(PdfViewer *viewer);
54 static void pdf_viewer_set_index_button_sensitive(PdfViewer *viewer);
55 static void pdf_viewer_scroll_to(PdfViewer *viewer, gfloat x, gfloat y);
56
57 static void search_matches_free(PdfViewer *viewer);
58 static gboolean pdf_viewer_text_search(MimeViewer *_viewer, gboolean backward,
59                                      const gchar *str, gboolean case_sens);
60 static void pdf_viewer_render_selection(PdfViewer *viewer, PopplerRectangle *rect, PageResult *page_results);
61 static void pdf_viewer_render_page(PopplerPage *page, GtkWidget *view, double width, double height, double zoom, gint rotate);
62
63 static char * pdf_viewer_get_document_format_data(GTime utime);
64 static void pdf_viewer_get_document_index(PdfViewer *viewer, PopplerIndexIter *index_iter, GtkTreeIter *parentiter);
65 static void pdf_viewer_index_row_activated(GtkTreeView          *tree_view,
66                                         GtkTreePath             *path,
67                                         GtkTreeViewColumn       *column,
68                                         gpointer                 data);
69
70 static GtkTable * pdf_viewer_fill_info_table(PdfViewer *viewer);
71
72 /* Callbacks */
73 static void pdf_viewer_move_events_cb(GtkWidget *widget, GdkEventMotion *event, PdfViewer *viewer); 
74 static void pdf_viewer_button_press_events_cb(GtkWidget *widget, GdkEventButton *event, PdfViewer *viewer); 
75 static void pdf_viewer_mouse_scroll_destroy_cb(GtkWidget *widget, GdkEventButton *event, PdfViewer *viewer); 
76 static void pdf_viewer_button_first_page_cb(GtkButton *button, PdfViewer *viewer);
77 static void pdf_viewer_button_last_page_cb(GtkButton *button, PdfViewer *viewer);
78 static void pdf_viewer_button_zoom_in_cb(GtkButton *button, PdfViewer *viewer);
79 static void pdf_viewer_button_zoom_out_cb(GtkButton *button, PdfViewer *viewer);
80 static void pdf_viewer_button_zoom_fit_cb(GtkButton *button, PdfViewer *viewer);
81 static void pdf_viewer_button_zoom_width_cb(GtkButton *button, PdfViewer *viewer);
82 static void pdf_viewer_button_rotate_right_cb(GtkButton *button, PdfViewer *viewer);
83 static void pdf_viewer_button_rotate_left_cb(GtkButton *button, PdfViewer *viewer);
84 static void pdf_viewer_spin_change_page_cb(GtkSpinButton *button, PdfViewer *viewer);
85 static void pdf_viewer_spin_zoom_scroll_cb(GtkSpinButton *button, PdfViewer *viewer);
86 /* Show/Hide the index pane */
87 static void pdf_viewer_show_document_index_cb(GtkButton *button, PdfViewer *viewer);
88 static void pdf_viewer_button_document_info_cb(GtkButton *button, PdfViewer *viewer);
89
90 static void pdf_viewer_show_controls(PdfViewer *viewer, gboolean show);
91 static gboolean pdf_viewer_scroll_page(MimeViewer *_viewer, gboolean up);
92 static void pdf_viewer_scroll_one_line(MimeViewer *_viewer, gboolean up);
93
94 /** Claws-Mail Plugin functions*/
95 gint plugin_init(gchar **error);
96 const gchar *plugin_name(void);
97 const gchar *plugin_desc(void);
98 const gchar *plugin_type(void);
99 const gchar *plugin_licence(void);
100 const gchar *plugin_version(void);
101 struct PluginFeature *plugin_provides(void);
102
103 #ifndef POPPLER_WITH_GDK
104 static void
105 copy_cairo_surface_to_pixbuf (cairo_surface_t *surface,
106                               GdkPixbuf       *pixbuf)
107 {
108         int cairo_width, cairo_height, cairo_rowstride;
109         unsigned char *pixbuf_data, *dst, *cairo_data;
110         int pixbuf_rowstride, pixbuf_n_channels;
111         unsigned int *src;
112         int x, y;
113
114         cairo_width = cairo_image_surface_get_width (surface);
115         cairo_height = cairo_image_surface_get_height (surface);
116         cairo_rowstride = cairo_image_surface_get_stride (surface);
117         cairo_data = cairo_image_surface_get_data (surface);
118
119         pixbuf_data = gdk_pixbuf_get_pixels (pixbuf);
120         pixbuf_rowstride = gdk_pixbuf_get_rowstride (pixbuf);
121         pixbuf_n_channels = gdk_pixbuf_get_n_channels (pixbuf);
122
123         if (cairo_width > gdk_pixbuf_get_width (pixbuf))
124                 cairo_width = gdk_pixbuf_get_width (pixbuf);
125         if (cairo_height > gdk_pixbuf_get_height (pixbuf))
126                 cairo_height = gdk_pixbuf_get_height (pixbuf);
127         for (y = 0; y < cairo_height; y++) {
128                 src = (unsigned int *) (cairo_data + y * cairo_rowstride);
129                 dst = pixbuf_data + y * pixbuf_rowstride;
130                 for (x = 0; x < cairo_width; x++) {
131                         dst[0] = (*src >> 16) & 0xff;
132                         dst[1] = (*src >> 8) & 0xff; 
133                         dst[2] = (*src >> 0) & 0xff;
134                         if (pixbuf_n_channels == 4)
135                                 dst[3] = (*src >> 24) & 0xff;
136                         dst += pixbuf_n_channels;
137                         src++;
138                 }
139         }
140 }
141 static void
142 _poppler_page_render_to_pixbuf (PopplerPage *page,
143                                 int src_x, int src_y,
144                                 int src_width, int src_height,
145                                 double scale,
146                                 int rotation,
147                                 gboolean printing,
148                                 GdkPixbuf *pixbuf)
149 {
150         cairo_t *cr;
151         cairo_surface_t *surface;
152
153         surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
154                                         src_width, src_height);
155         cr = cairo_create (surface);
156         cairo_save (cr);
157         switch (rotation) {
158         case 90:
159                 cairo_translate (cr, src_x + src_width, -src_y);
160                 break;
161         case 180:
162                 cairo_translate (cr, src_x + src_width, src_y + src_height);
163                 break;
164         case 270:
165                 cairo_translate (cr, -src_x, src_y + src_height);
166                 break;
167         default:
168                 cairo_translate (cr, -src_x, -src_y);
169         }
170
171         if (scale != 1.0)
172                 cairo_scale (cr, scale, scale);
173
174         if (rotation != 0)
175                 cairo_rotate (cr, rotation * G_PI / 180.0);
176
177         if (printing)
178                 poppler_page_render_for_printing (page, cr);
179         else
180                 poppler_page_render (page, cr);
181         cairo_restore (cr);
182
183         cairo_set_operator (cr, CAIRO_OPERATOR_DEST_OVER);
184         cairo_set_source_rgb (cr, 1., 1., 1.);
185         cairo_paint (cr);
186
187         cairo_destroy (cr);
188
189         copy_cairo_surface_to_pixbuf (surface, pixbuf);
190         cairo_surface_destroy (surface);
191 }
192
193 /**
194  * poppler_page_render_to_pixbuf:
195  * @page: the page to render from
196  * @src_x: x coordinate of upper left corner  
197  * @src_y: y coordinate of upper left corner  
198  * @src_width: width of rectangle to render  
199  * @src_height: height of rectangle to render
200  * @scale: scale specified as pixels per point
201  * @rotation: rotate the document by the specified degree
202  * @pixbuf: pixbuf to render into
203  *
204  * First scale the document to match the specified pixels per point,
205  * then render the rectangle given by the upper left corner at
206  * (src_x, src_y) and src_width and src_height.
207  * This function is for rendering a page that will be displayed.
208  * If you want to render a page that will be printed use
209  * poppler_page_render_to_pixbuf_for_printing() instead
210  *
211  * Deprecated: 0.16
212  **/
213 static void
214 poppler_page_render_to_pixbuf (PopplerPage *page,
215                                int src_x, int src_y,
216                                int src_width, int src_height,
217                                double scale,
218                                int rotation,
219                                GdkPixbuf *pixbuf)
220 {
221         g_return_if_fail (POPPLER_IS_PAGE (page));
222         g_return_if_fail (scale > 0.0);
223         g_return_if_fail (pixbuf != NULL);
224
225         _poppler_page_render_to_pixbuf (page, src_x, src_y,
226                                   src_width, src_height,
227                                   scale, rotation,
228                                   FALSE,
229                                   pixbuf);
230 }
231 #endif
232 static GtkWidget *pdf_viewer_get_widget(MimeViewer *_viewer)
233 {
234         PdfViewer *viewer = (PdfViewer *) _viewer;
235         debug_print("pdf_viewer_get_widget: %p\n", viewer->vbox);
236
237         return GTK_WIDGET(viewer->vbox);
238 }
239 /** Hide the index panel */
240 static void pdf_viewer_hide_index_pane(PdfViewer *viewer)
241 {
242         if (viewer->pdf_index) {   
243                 poppler_index_iter_free(viewer->pdf_index);
244                 viewer->pdf_index = NULL;
245                 gtk_widget_hide(GTK_WIDGET(viewer->frame_index));
246         }
247 }
248
249 static void search_matches_free(PdfViewer *viewer)
250 {
251         GList *cur; 
252         for(cur = viewer->text_found; cur; cur = cur->next) {
253                 PageResult *res = (PageResult *)cur->data;
254                 g_list_free(res->results);
255                 g_free(res);
256         }
257         g_list_free(viewer->text_found);
258         viewer->text_found = NULL;
259         g_free(viewer->last_search);
260         viewer->last_search = NULL;
261         if (viewer->last_rect && viewer->last_page_result) {
262                 viewer->last_rect = NULL;
263                 viewer->last_page_result = NULL;
264                 pdf_viewer_update((MimeViewer *)viewer, 
265                         FALSE, 
266                         gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(viewer->cur_page)));
267         }
268 }
269
270 static void pdf_viewer_scroll_to(PdfViewer *viewer, gfloat x, gfloat y)
271 {
272         GtkAdjustment *vadj;
273         GtkAdjustment *hadj;
274         vadj = gtk_scrolled_window_get_vadjustment(
275                 GTK_SCROLLED_WINDOW(viewer->scrollwin));
276
277         if (y < vadj->value) {
278                 vadj->value = y;
279         }
280         else {
281                 while(y > vadj->value + vadj->page_size) {
282                         vadj->value += vadj->page_size;
283                 }
284         }
285
286         hadj = gtk_scrolled_window_get_hadjustment(
287                 GTK_SCROLLED_WINDOW(viewer->scrollwin));
288
289         if (x < hadj->value) {
290                 hadj->value = x;
291         }
292         else {
293                 while(x > hadj->value + hadj->page_size) {
294                         hadj->value += hadj->page_size;
295                 }
296         }
297
298         g_signal_emit_by_name(G_OBJECT(hadj), "value-changed", 0);      
299         g_signal_emit_by_name(G_OBJECT(vadj), "value-changed", 0);      
300 }
301 static void pdf_viewer_render_page(PopplerPage *page, GtkWidget *view, double width, 
302                                    double height, double zoom, gint rotate)
303 {
304         GdkPixbuf *pb;
305
306         debug_print("width: %f\n", width);
307         pb = gdk_pixbuf_new(GDK_COLORSPACE_RGB, 
308                                 FALSE, 8, 
309                                 (int)(width * zoom), 
310                                 (int)(height * zoom));  
311
312                         poppler_page_render_to_pixbuf(page, 0, 0, 
313                                 (int)(width * zoom), 
314                                 (int)(height * zoom), 
315                                 zoom, rotate, pb);
316
317                         gtk_image_set_from_pixbuf(GTK_IMAGE(view), pb);
318                         g_object_unref(G_OBJECT(pb));
319 }
320 static void pdf_viewer_render_selection(PdfViewer *viewer, PopplerRectangle *rect, PageResult *page_results)
321 {
322         gint selw, selh;
323         double width_points, height_points;
324         gint width, height;
325         GdkPixbuf *sel_pb, *page_pb;
326         gfloat x1, x2, y1, y2;  
327
328
329         gint cur_page_num = 
330                 gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(viewer->cur_page));
331
332         viewer->last_match = viewer->res_cnt;
333
334         viewer->last_rect = NULL;
335         viewer->last_page_result = NULL;
336         if (cur_page_num != page_results->page_num) {
337                 /* we changed page. update the view */
338                 gtk_spin_button_set_value(GTK_SPIN_BUTTON(viewer->cur_page), 
339         (gdouble) page_results->page_num);
340         }
341
342         viewer->last_rect = rect;
343         viewer->last_page_result = page_results;
344
345         GTK_EVENTS_FLUSH();
346
347         poppler_page_get_size(POPPLER_PAGE(viewer->pdf_page), &width_points, &height_points);
348         width = (int)((width_points * viewer->zoom) + 0.5);
349         height = (int)((height_points * viewer->zoom) + 0.5);
350
351         if (viewer->rotate == 90) {
352                 x1 = MIN(rect->y1,rect->y2) * viewer->zoom;
353                 x2 = MAX(rect->y1,rect->y2) * viewer->zoom;
354                 y1 = MAX(rect->x1,rect->x2) * viewer->zoom;
355                 y2 = MIN(rect->x1,rect->x2) * viewer->zoom;
356                 selw = (x2 - x1);
357                 selh = (y1 - y2);
358
359         } else if (viewer->rotate == 180) {
360                 x1 = width - rect->x2 * viewer->zoom;
361                 x2 = width - rect->x1 * viewer->zoom;
362                 y1 = height - rect->y2 * viewer->zoom;
363                 y2 = height - rect->y1 * viewer->zoom;
364                 selw = (x2 - x1);
365                 selh = (y2 - y1);
366                 y1 = height - y1;
367                 y2 = height - y2;
368
369         } else if (viewer->rotate == 270) {
370                 x1 = height - MAX(rect->y1,rect->y2) * viewer->zoom;
371                 x2 = height - MIN(rect->y1,rect->y2) * viewer->zoom;
372                 y1 = width - MIN(rect->x1,rect->x2) * viewer->zoom;
373                 y2 = width - MAX(rect->x1,rect->x2) * viewer->zoom;
374                 selw = (x2 - x1);
375                 selh = (y1 - y2);
376         } else {
377                 x1 = rect->x1 * viewer->zoom;
378                 x2 = rect->x2 * viewer->zoom;
379                 y1 = rect->y1 * viewer->zoom;
380                 y2 = rect->y2 * viewer->zoom;
381                 selw = (x2 - x1);
382                 selh = (y2 - y1);
383                 y1 = height - y1;
384                 y2 = height - y2;
385         }
386
387         sel_pb = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8, 
388                                                         selw, selh);
389
390         gdk_pixbuf_fill(sel_pb, SELECTION_COLOR);
391
392         page_pb = gdk_pixbuf_new(GDK_COLORSPACE_RGB, 
393                                         FALSE, 8, 
394                                 (int)(viewer->width * viewer->zoom), 
395                                 (int)(viewer->height * viewer->zoom));  
396
397         poppler_page_render_to_pixbuf(viewer->pdf_page, 
398                                         0, 
399                                         0, 
400                                 (int)(viewer->width * viewer->zoom), 
401                                 (int)(viewer->height * viewer->zoom), 
402                                         viewer->zoom, 
403                                         viewer->rotate, 
404                                         page_pb);
405
406         gdk_pixbuf_composite(sel_pb, page_pb, 
407                                         x1, y2, selw, selh, 0, 0, 
408                                         viewer->zoom, viewer->zoom, 
409                                         GDK_INTERP_BILINEAR, ALPHA_CHANNEL);
410
411         gtk_image_set_from_pixbuf(GTK_IMAGE(viewer->pdf_view), page_pb);
412
413         pdf_viewer_scroll_to(viewer, MIN(x1,x2), MIN(y1,y2));
414
415         g_object_unref(G_OBJECT(sel_pb));
416         g_object_unref(G_OBJECT(page_pb));
417
418 }
419
420 static gboolean pdf_viewer_text_search(MimeViewer *_viewer, gboolean backward,
421                                      const gchar *str, gboolean case_sens)
422 {
423         PdfViewer *viewer = (PdfViewer *)_viewer;
424         GList *all_pages_results, *cur_page_results;
425         viewer->res_cnt = 0;
426
427         debug_print("pdf_viewer_text_search: %s\n", str);
428         main_window_cursor_wait(mainwindow_get_mainwindow());
429
430         if (viewer->last_search && strcmp(str, viewer->last_search)) {
431                 search_matches_free(viewer);
432                 viewer->last_match = -1;
433                 viewer->num_matches = 0;
434         } else if (!viewer->last_search) {
435                 viewer->last_match = -1;
436                 viewer->num_matches = 0;
437         }
438         /* It's a new search, build list of matches 
439          * across all pages */
440         if (viewer->last_match == -1) {
441                 gint i; 
442
443                 for(i = 1; i <= viewer->num_pages; i++) {
444
445                         PopplerPage *pdf_page = poppler_document_get_page(viewer->pdf_doc, i - 1);
446                         viewer->page_results = poppler_page_find_text(pdf_page, str);
447
448                         if (viewer->page_results != NULL) {
449                                 debug_print("page_results %p\n", viewer->page_results);
450                                 /* store results for this page */
451                                 gint num_res = 0;
452                                 PageResult *res = g_new0(PageResult, 1);
453                                 res->results = viewer->page_results;
454                                 res->page_num = i;
455                                 /* found text, prepend this page(faster than append) */
456                                 viewer->text_found = g_list_prepend(viewer->text_found, res);
457                                 num_res = g_list_length(viewer->page_results);
458                                 debug_print("%d results on page %d\n", num_res, i);
459                                 viewer->num_matches += num_res;
460                         }
461                         g_object_unref(G_OBJECT(pdf_page));
462                 }
463
464                 if (viewer->text_found == NULL) {
465                         main_window_cursor_normal(mainwindow_get_mainwindow());
466                         return FALSE;
467                 }
468                 /* put back the list in the correct order */
469                 viewer->text_found = g_list_reverse(viewer->text_found);
470         } 
471
472         if (!viewer->text_found) {
473                 main_window_cursor_normal(mainwindow_get_mainwindow());
474                 return FALSE;
475         } else {
476                 viewer->last_search = g_strdup(str);
477         }
478
479         if (backward) {
480                 /* if backward, we have to initialize stuff to search 
481                  * from the end */
482                 viewer->res_cnt = viewer->num_matches-1;
483                 if (viewer->last_match == -1) {
484                         viewer->last_match = viewer->num_matches+1;
485                 }
486                 all_pages_results = g_list_last(viewer->text_found);
487         } 
488         else {
489                 all_pages_results = viewer->text_found;
490         }
491
492         for(; all_pages_results; all_pages_results = (backward?all_pages_results->prev:all_pages_results->next)) {
493
494                 PageResult * page_results = (PageResult *)all_pages_results->data;
495
496                 if (backward) {
497                         cur_page_results = g_list_last(page_results->results);
498                 }
499                 else {
500                         cur_page_results = page_results->results;
501                 }
502
503                 for(; cur_page_results; cur_page_results = (backward?cur_page_results->prev:cur_page_results->next)) {
504
505                         gboolean valid = FALSE;
506                         /* first valid result is the last+1 if searching
507                          * forward, last-1 if searching backward */
508                         if (backward) {
509                                 valid = (viewer->res_cnt < viewer->last_match);
510                         }
511                         else {
512                                 valid = (viewer->res_cnt > viewer->last_match);
513                         }
514                         if (valid) {
515                                 pdf_viewer_render_selection(viewer, 
516                                         (PopplerRectangle *)cur_page_results->data,
517                                                 page_results);
518                                 main_window_cursor_normal(mainwindow_get_mainwindow());
519                                 return TRUE;
520                         }
521
522                         if (backward) {
523                                 viewer->res_cnt--;
524                         }
525                         else {
526                                 viewer->res_cnt++;
527                         }
528                 }
529         }
530         main_window_cursor_normal(mainwindow_get_mainwindow());
531         search_matches_free(viewer);
532         return FALSE;
533 }
534
535 static void pdf_viewer_get_document_index(PdfViewer *viewer, PopplerIndexIter *index_iter, GtkTreeIter *parentiter)
536 {
537         PopplerAction *action;
538         PopplerIndexIter *child;
539         GtkTreeIter childiter;
540
541         debug_print("get document index\n");
542         do      {
543                 gint page_num = 0;
544
545                 action = poppler_index_iter_get_action(index_iter);
546
547                 if (action->type != POPPLER_ACTION_GOTO_DEST) {
548                         poppler_action_free(action);
549                         continue;
550                 }
551
552                 if (action->goto_dest.dest->type == POPPLER_DEST_XYZ || action->goto_dest.dest->type == POPPLER_DEST_FITH) {
553                         page_num = action->goto_dest.dest->page_num;
554                 }
555 #ifdef HAVE_POPPLER_DEST_NAMED
556                 else if (action->goto_dest.dest->type == POPPLER_DEST_NAMED) {
557                         PopplerDest *dest = poppler_document_find_dest(
558                                         viewer->pdf_doc, action->goto_dest.dest->named_dest);
559                         if (dest->type != POPPLER_DEST_XYZ) {
560                                 g_warning("couldn't figure out link");
561                                 poppler_dest_free(dest);
562                                 continue;
563                         }
564                         page_num = dest->page_num;
565                         poppler_dest_free(dest);
566                 } 
567 #endif
568                 else {
569 #ifdef HAVE_POPPLER_DEST_NAMED
570                         g_warning("unhandled link type %d. please contact developers", action->goto_dest.dest->type);
571 #else
572                         g_warning("unhandled link type %d. please upgrade libpoppler-glib to 0.5.4", action->goto_dest.dest->type);
573 #endif
574                         continue;
575                 }
576                 gtk_tree_store_append(GTK_TREE_STORE(viewer->index_model), &childiter, parentiter);
577                 gtk_tree_store_set(GTK_TREE_STORE(viewer->index_model), &childiter,
578                                                 INDEX_NAME, action->named.title,
579                                                 INDEX_PAGE, page_num,
580                                                 INDEX_TOP, action->goto_dest.dest->top,
581                                                 -1);
582                 poppler_action_free(action);
583                 child = poppler_index_iter_get_child(index_iter);
584                 if (child) {
585                         pdf_viewer_get_document_index(viewer, child, &childiter);
586                         poppler_index_iter_free(child);
587                 }
588         }
589         while(poppler_index_iter_next(index_iter));
590 }
591
592 static void pdf_viewer_index_row_activated(GtkTreeView          *tree_view,
593                                         GtkTreePath             *path,
594                                         GtkTreeViewColumn       *column,
595                                         gpointer                 data)
596 {
597         GtkTreeIter iter;
598         GtkTreeModel *model = gtk_tree_view_get_model(tree_view);
599         PdfViewer *viewer = (PdfViewer *)data;
600         gint page_num = 0;
601
602         debug_print("index_row_activated\n");
603         if (!gtk_tree_model_get_iter(model, &iter, path)) return;
604
605         gtk_tree_model_get(model, &iter, 
606                            INDEX_PAGE, &page_num,
607                            -1);
608
609         if (page_num > 0) {
610                 gtk_spin_button_set_value(GTK_SPIN_BUTTON(viewer->cur_page),(gdouble)page_num);
611                 debug_print("Page num: %d\n", page_num);
612         }
613         GTK_EVENTS_FLUSH();
614 }
615
616 /** Disable the index button if the document doesn't have an index */
617 static void pdf_viewer_set_index_button_sensitive(PdfViewer *viewer)
618 {
619         viewer->pdf_index  = poppler_index_iter_new(viewer->pdf_doc);
620         if (viewer->pdf_index) {        
621                 if (!gtk_widget_is_sensitive(viewer->doc_index)) {
622                         gtk_widget_set_sensitive(viewer->doc_index, TRUE);
623                 }
624         }
625         else {
626                 gtk_widget_set_sensitive(viewer->doc_index, FALSE);
627         }
628
629     poppler_index_iter_free(viewer->pdf_index);
630     viewer->pdf_index = NULL;
631 }
632
633 static char * pdf_viewer_get_document_format_data(GTime utime) 
634 {
635         time_t time = (time_t) utime;
636         struct tm t;
637         char s[256];
638         const char *fmt_hack = "%c";
639         size_t len;
640
641         if (time == 0 || !localtime_r(&time, &t)) return NULL;
642
643         len = strftime(s, sizeof(s), fmt_hack, &t);
644
645         if (len == 0 || s[0] == '\0') return NULL;
646
647         return g_locale_to_utf8(s, -1, NULL, NULL, NULL);
648 }
649
650 #define ADD_TO_TABLE(LABEL, VALUE) \
651         label = gtk_label_new(LABEL); \
652         gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5); \
653         gtk_misc_set_padding(GTK_MISC(label), 4, 0); \
654         gtk_table_attach(viewer->table_doc_info, label, 0, 1, row, row+1, GTK_EXPAND | GTK_FILL, 0, 0, 0); \
655         \
656         label = gtk_label_new(VALUE); \
657         gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); \
658         gtk_misc_set_padding(GTK_MISC(label), 4, 0); \
659         gtk_table_attach(viewer->table_doc_info, label, 1, 2, row, row+1, GTK_EXPAND | GTK_FILL, 0, 0, 0); \
660         row++;
661
662
663 static GtkTable * pdf_viewer_fill_info_table(PdfViewer *viewer)
664 {
665         GtkWidget *label;
666         const gchar *title, *format, *author, *subject, *keywords, *creator, *producer;
667         gboolean linearized;
668         gchar *tmp;
669         gint row = 0;
670
671         GTime creation_date, mod_date;
672
673         PopplerPageLayout layout;
674         PopplerPageMode mode;
675         PopplerPermissions permissions;
676         PopplerViewerPreferences view_prefs;
677
678         title = format = author = subject = keywords = creator = producer = tmp = 0;
679
680         g_object_get(viewer->pdf_doc,
681                                 "title", &title,
682                                 "format", &format,
683                                 "author", &author,
684                                 "subject", &subject,
685                                 "keywords", &keywords,
686                                 "creation-date", &creation_date,
687                                 "permissions", &permissions,
688                                 "mod-date", &mod_date,
689                                 "creator", &creator,
690                                 "producer", &producer,  
691                                 "linearized", &linearized,
692                                 "page-mode", &mode,
693                                 "page-layout", &layout,
694                                 "viewer-preferences", &view_prefs,
695                                 NULL);
696
697         viewer->table_doc_info = GTK_TABLE(gtk_table_new(13, 2, FALSE));
698
699         ADD_TO_TABLE(_("Filename:"), viewer->target_filename)
700         ADD_TO_TABLE(_("Size:"), to_human_readable(viewer->to_load->length))
701         ADD_TO_TABLE(NULL, NULL)
702         ADD_TO_TABLE(_("Title:"), title)
703         ADD_TO_TABLE(_("Subject:"), subject)
704         ADD_TO_TABLE(_("Author:"), author)
705         ADD_TO_TABLE(_("Keywords:"), keywords)
706         ADD_TO_TABLE(_("Creator:"), creator)
707         ADD_TO_TABLE(_("Producer:"), producer)
708
709         tmp = pdf_viewer_get_document_format_data(creation_date);
710         ADD_TO_TABLE(_("Created:"), tmp)
711         g_free(tmp);
712
713         tmp = pdf_viewer_get_document_format_data(mod_date);
714         ADD_TO_TABLE(_("Modified:"), tmp)
715         g_free(tmp);
716
717         ADD_TO_TABLE(_("Format:"), format)
718         if (linearized) {
719                 ADD_TO_TABLE(_("Optimized:"), _("Yes"))
720         }
721         else {
722                 ADD_TO_TABLE(_("Optimized:"), _("No"))
723         }
724         //ADD_TO_TABLE(_("Page Mode:"), pdf_viewer_get_document_info_mode(mode)) 
725         //ADD_TO_TABLE(_("Page Layout:"), pdf_viewer_get_document_info_layout(layout))
726
727         return(GtkTable *) viewer->table_doc_info;
728 }
729 #undef ADD_TO_TABLE
730
731 static FileType pdf_viewer_mimepart_get_type(MimeInfo *partinfo)
732 {
733         gchar *content_type = NULL;
734         FileType type = TYPE_UNKNOWN;
735         debug_print("mimepart_get_type\n");
736         if ((partinfo->type == MIMETYPE_APPLICATION) &&
737         (!g_ascii_strcasecmp(partinfo->subtype, "octet-stream"))) {
738
739                 const gchar *filename;
740
741                 filename = procmime_mimeinfo_get_parameter(partinfo, "filename");
742
743                         if (filename == NULL)
744                                 filename = procmime_mimeinfo_get_parameter(partinfo, "name");
745                         if (filename != NULL)
746                                 content_type = procmime_get_mime_type(filename);
747         } 
748         else {
749                 content_type = procmime_get_content_type_str(partinfo->type, partinfo->subtype);
750         }
751
752         if (content_type == NULL) type = TYPE_UNKNOWN;
753         else if (!strcmp(content_type, "application/pdf")) type = TYPE_PDF;
754         else if (!strcmp(content_type, "application/postscript")) type = TYPE_PS;
755         else type = TYPE_UNKNOWN;
756
757         g_free(content_type);
758         return type;
759 }
760
761 /* Callbacks */
762 static void pdf_viewer_button_first_page_cb(GtkButton *button, PdfViewer *viewer) 
763 {
764
765         gtk_spin_button_spin(GTK_SPIN_BUTTON(viewer->cur_page), GTK_SPIN_HOME, 1);
766 }
767
768 static void pdf_viewer_button_prev_page_cb(GtkButton *button, PdfViewer *viewer) 
769 {
770
771         gtk_spin_button_spin(GTK_SPIN_BUTTON(viewer->cur_page), GTK_SPIN_STEP_BACKWARD, 1);
772 }
773
774 static void pdf_viewer_button_next_page_cb(GtkButton *button, PdfViewer *viewer) 
775 {
776
777         gtk_spin_button_spin(GTK_SPIN_BUTTON(viewer->cur_page), GTK_SPIN_STEP_FORWARD, 1);
778 }
779
780 static void pdf_viewer_button_last_page_cb(GtkButton *button, PdfViewer *viewer) 
781 {
782
783         gtk_spin_button_spin(GTK_SPIN_BUTTON(viewer->cur_page), GTK_SPIN_END, 1);
784 }
785
786 static void pdf_viewer_spin_change_page_cb(GtkSpinButton *button, PdfViewer *viewer)
787 {
788         pdf_viewer_update((MimeViewer *)viewer, 
789                         FALSE, 
790                         gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(viewer->cur_page)));
791 }
792
793 static void pdf_viewer_button_zoom_in_cb(GtkButton *button, PdfViewer *viewer) 
794 {
795
796         gtk_spin_button_spin(GTK_SPIN_BUTTON(viewer->zoom_scroll), GTK_SPIN_STEP_FORWARD, ZOOM_FACTOR);
797 }
798
799 static void pdf_viewer_spin_zoom_scroll_cb(GtkSpinButton *button, PdfViewer *viewer)
800 {
801         viewer->zoom = gtk_spin_button_get_value(GTK_SPIN_BUTTON(viewer->zoom_scroll));
802         pdf_viewer_update((MimeViewer *)viewer,
803                         FALSE,
804                         gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(viewer->cur_page)));
805 }
806
807 static void pdf_viewer_button_zoom_out_cb(GtkButton *button, PdfViewer *viewer) 
808 {
809
810         gtk_spin_button_spin(GTK_SPIN_BUTTON(viewer->zoom_scroll), GTK_SPIN_STEP_BACKWARD, ZOOM_FACTOR);
811
812 }
813
814 static void pdf_viewer_button_press_events_cb(GtkWidget *widget, GdkEventButton *event, PdfViewer *viewer) 
815 {
816         gchar *uri;
817         #ifdef HAVE_POPPLER_DEST_NAMED
818         PopplerDest *dest;
819         #endif
820         static GdkCursor *hand_cur = NULL;
821
822         if (!hand_cur) hand_cur = gdk_cursor_new(GDK_FLEUR);
823
824         /* Execute Poppler Links */
825         if (event->button == 1 && viewer->in_link) {
826                 switch (viewer->link_action->type) {
827                 case POPPLER_ACTION_UNKNOWN:
828                         debug_print("action unknown\n");
829                         break;
830                 case POPPLER_ACTION_GOTO_DEST:
831                         if (viewer->link_action->goto_dest.dest->type == POPPLER_DEST_XYZ || 
832                                         viewer->link_action->goto_dest.dest->type == POPPLER_DEST_FITH) {
833                                 gtk_spin_button_set_value(GTK_SPIN_BUTTON(viewer->cur_page), 
834                                                                           (gdouble)viewer->link_action->goto_dest.dest->page_num);
835                         }
836                 #ifdef HAVE_POPPLER_DEST_NAMED
837                         else if (viewer->link_action->goto_dest.dest->type == POPPLER_DEST_NAMED) {
838                                 dest = poppler_document_find_dest(
839                                         viewer->pdf_doc, viewer->link_action->goto_dest.dest->named_dest);
840                         if (dest->type != POPPLER_DEST_XYZ) {
841                                 g_warning("couldn't figure out link");
842                                 poppler_dest_free(dest);
843                                 break;
844                         }
845                         gtk_spin_button_set_value(GTK_SPIN_BUTTON(viewer->cur_page), 
846                                                                           (gdouble)dest->page_num);
847                         poppler_dest_free(dest);
848                 } 
849                 #endif
850                         break;
851                 case POPPLER_ACTION_GOTO_REMOTE:
852                         #ifdef HAVE_POPPLER_DEST_NAMED
853                         dest = poppler_document_find_dest(
854                                         viewer->pdf_doc, viewer->link_action->goto_remote.dest->named_dest);
855                         if (dest->type != POPPLER_DEST_XYZ) {
856                                 g_warning ("couldn't figure out link");
857                                 poppler_dest_free(dest);
858                                 break;
859                         }
860                         gtk_spin_button_set_value(GTK_SPIN_BUTTON(viewer->cur_page),
861                                                                           (gdouble)dest->page_num);
862                         poppler_dest_free(dest);
863                         #endif
864                         break;
865                 case POPPLER_ACTION_LAUNCH:
866                         debug_print("action launch not yet implemented\n");
867                         break;
868                 case POPPLER_ACTION_URI:
869                         uri = g_strdup(viewer->link_action->uri.uri);
870                         if (!g_ascii_strncasecmp(uri, "mailto:", 7)) 
871                                 compose_new(NULL, uri + 7, NULL);
872                         else 
873                                 open_uri(uri, prefs_common_get_uri_cmd());
874                         g_free(uri);
875                         break;
876                 case POPPLER_ACTION_NAMED:
877                         debug_print("action named not yet implemented\n");
878                         break;
879                 case POPPLER_ACTION_NONE:
880                         debug_print("action none does nothing, surprise!\n");
881                         break;
882 #if POPPLER_CHECK_VERSION(0,14,0)
883                 case POPPLER_ACTION_MOVIE:
884                         debug_print("yoyoyo ;-) a movie?\n");
885                         break;
886                 case POPPLER_ACTION_RENDITION:
887                         debug_print("yoyoyo ;-) multimedia?\n");
888                         break;
889                 case POPPLER_ACTION_OCG_STATE:
890                         debug_print("yoyoyo ;-) layer state?\n");
891                         break;
892 #if POPPLER_CHECK_VERSION(0,18,0)
893                 case POPPLER_ACTION_JAVASCRIPT:
894                         debug_print("yoyoyo ;-) javascript?\n");
895                         break;
896 #endif /* 0.18 */
897 #endif /* 0.14 */
898                 }
899                 if (((MimeViewer *)viewer)->mimeview && 
900                         ((MimeViewer *)viewer)->mimeview->messageview && 
901                         ((MimeViewer *)viewer)->mimeview->messageview->window && 
902                         ((MimeViewer *)viewer)->mimeview->messageview->window->window) 
903                         gdk_window_set_cursor (((MimeViewer *)viewer)->mimeview->messageview->window->window, NULL);
904                 else
905                         gdk_window_set_cursor (mainwindow_get_mainwindow()->window->window, NULL);
906         }
907
908         /* Init document to be scrolled with left mouse click */
909         if (event->button == 1 && !viewer->in_link) {
910                 viewer->pdf_view_scroll = TRUE;
911                 if (((MimeViewer *)viewer)->mimeview && 
912                         ((MimeViewer *)viewer)->mimeview->messageview && 
913                         ((MimeViewer *)viewer)->mimeview->messageview->window && 
914                         ((MimeViewer *)viewer)->mimeview->messageview->window->window) 
915                         gdk_window_set_cursor (((MimeViewer *)viewer)->mimeview->messageview->window->window, hand_cur);
916                 else
917                         gdk_window_set_cursor (mainwindow_get_mainwindow()->window->window, hand_cur);
918
919                 viewer->last_x = event->x;
920                 viewer->last_y = event->y;
921                 viewer->last_dir_x = 0;
922                 viewer->last_dir_y = 0;
923         }
924 }
925 /* Set the normal cursor*/
926 static void pdf_viewer_mouse_scroll_destroy_cb(GtkWidget *widget, GdkEventButton *event, PdfViewer *viewer) 
927 {
928
929         if (event->button == 1) {
930                 viewer->pdf_view_scroll = FALSE;
931                 if (((MimeViewer *)viewer)->mimeview && 
932                         ((MimeViewer *)viewer)->mimeview->messageview && 
933                         ((MimeViewer *)viewer)->mimeview->messageview->window && 
934                         ((MimeViewer *)viewer)->mimeview->messageview->window->window) 
935                         gdk_window_set_cursor (((MimeViewer *)viewer)->mimeview->messageview->window->window, NULL);
936                 else
937                         gdk_window_set_cursor (mainwindow_get_mainwindow()->window->window, NULL);
938         }
939 }
940
941 static void pdf_viewer_move_events_cb(GtkWidget *widget, GdkEventMotion *event, PdfViewer *viewer) 
942 {
943         /* Grab the document and scroll it with mouse */ 
944         if (viewer->pdf_view_scroll) {
945
946                 viewer->pdf_view_vadj = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(viewer->scrollwin));
947                 viewer->pdf_view_hadj = gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(viewer->scrollwin));
948
949                         if (event->x < viewer->last_x
950                                         && viewer->pdf_view_hadj->value < (viewer->pdf_view_hadj->upper - viewer->pdf_view_hadj->page_size)) {
951                                 if (viewer->last_dir_x == -1) {
952                                         viewer->pdf_view_hadj->value += viewer->last_x - event->x; 
953                                         g_signal_emit_by_name(G_OBJECT(viewer->pdf_view_hadj),
954                                                                 "value_changed", 0);
955                                 }
956                                 viewer->last_dir_x = -1;
957                         }
958                         else if (event->x > viewer->last_x
959                                         && viewer->pdf_view_hadj->value > 0.0)  {
960                                 if (viewer->last_dir_x == +1) {
961                                         viewer->pdf_view_hadj->value += viewer->last_x - event->x; 
962                                         g_signal_emit_by_name(G_OBJECT(viewer->pdf_view_hadj),
963                                                                 "value_changed", 0);
964                                 }
965                                 viewer->last_dir_x = +1;
966                         }
967
968                         if (event->y < viewer->last_y
969                                         && viewer->pdf_view_vadj->value < (viewer->pdf_view_vadj->upper - viewer->pdf_view_vadj->page_size)) {
970                                 if (viewer->last_dir_y == -1) {
971                                         viewer->pdf_view_vadj->value += viewer->last_y - event->y; 
972                                         g_signal_emit_by_name(G_OBJECT(viewer->pdf_view_vadj),
973                                                                 "value_changed", 0);
974                                 }
975                                 viewer->last_dir_y = -1;
976                         }
977                         else if (event->y > viewer->last_y
978                                         && viewer->pdf_view_vadj->value > 0.0)  {
979                                 if (viewer->last_dir_y == +1) {
980                                         viewer->pdf_view_vadj->value += viewer->last_y - event->y; 
981                                         g_signal_emit_by_name(G_OBJECT(viewer->pdf_view_vadj),
982                                                                 "value_changed", 0);
983                                 }
984                                 viewer->last_dir_y = +1;
985                         }
986                         viewer->last_x = event->x;
987                         viewer->last_y = event->y;
988                         GTK_EVENTS_FLUSH();
989                 } 
990         else {  
991         /* Link Mapping */
992         static GList *l;
993         static GdkCursor *link_cur = NULL;
994         static GtkRequisition size;
995         static gdouble x,y, x1, y1, x2, y2;
996         gboolean ccur;
997
998         viewer->pdf_view_vadj = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(viewer->scrollwin));
999         viewer->pdf_view_hadj = gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(viewer->scrollwin));
1000
1001         if (!link_cur) link_cur = gdk_cursor_new(GDK_HAND2);
1002
1003         ccur = FALSE;
1004         viewer->in_link = FALSE;        
1005         for (l = viewer->link_map; l; l = g_list_next (l)) {
1006                 PopplerLinkMapping *lmapping;
1007                 lmapping = (PopplerLinkMapping *)l->data;
1008
1009                 x1 = lmapping->area.x1;
1010                 y1 = lmapping->area.y1;
1011                 x2 = lmapping->area.x2;
1012                 y2 = lmapping->area.y2;
1013                 gtk_widget_size_request(viewer->pdf_view, &size);
1014
1015                 switch (viewer->rotate) {
1016                 case 0:
1017                 case 360:
1018                                 if (size.width != viewer->pdf_view_hadj->upper)
1019                                         x = (event->x - (viewer->pdf_view_hadj->upper - size.width) / 2) / viewer->zoom;
1020                                 else
1021                                         x = event->x / viewer->zoom;
1022
1023                                 y = (viewer->pdf_view_vadj->upper - event->y) / viewer->zoom;
1024                         break;
1025                 case 90:
1026                                 if (size.width != viewer->pdf_view_hadj->upper)
1027                                         y = (event->x - (viewer->pdf_view_hadj->upper - size.width) / 2) / viewer->zoom;
1028                                 else
1029                                         y = event->x / viewer->zoom;
1030
1031                                 x = (event->y) / viewer->zoom;
1032                         break;
1033                 case 180:
1034                                 if (size.width != viewer->pdf_view_hadj->upper)
1035                                         x = ((viewer->pdf_view_hadj->upper -  event->x) - ((viewer->pdf_view_hadj->upper - size.width) / 2)) / viewer->zoom;
1036                                 else
1037                                         x =  ((viewer->pdf_view_hadj->upper -  event->x) - (viewer->pdf_view_hadj->upper - size.width)) / viewer->zoom;
1038
1039                                 y = (event->y) / viewer->zoom;
1040                         break;
1041                 case 270:
1042                                 if (size.width != viewer->pdf_view_hadj->upper)
1043                                         y = ((viewer->pdf_view_hadj->upper -  event->x) - ((viewer->pdf_view_hadj->upper - size.width) / 2)) / viewer->zoom;
1044                                 else
1045                                         y =  ((viewer->pdf_view_hadj->upper -  event->x) - (viewer->pdf_view_hadj->upper - size.width)) / viewer->zoom;
1046
1047                                 x = (viewer->pdf_view_vadj->upper - event->y) / viewer->zoom;
1048                         break;
1049                 }
1050
1051                 if ( (x > x1 && x < x2) && (y > y1 && y < y2) ) {
1052                                 viewer->in_link = TRUE;
1053                         if (((MimeViewer *)viewer)->mimeview && 
1054                                 ((MimeViewer *)viewer)->mimeview->messageview && 
1055                                 ((MimeViewer *)viewer)->mimeview->messageview->window && 
1056                                 ((MimeViewer *)viewer)->mimeview->messageview->window->window) 
1057                                         gdk_window_set_cursor (((MimeViewer *)viewer)->mimeview->messageview->window->window, link_cur);
1058                                 else
1059                                         gdk_window_set_cursor (mainwindow_get_mainwindow()->window->window, link_cur);
1060
1061                                 viewer->link_action = lmapping->action; 
1062                                 ccur = TRUE;
1063                 }
1064                 if (!ccur) {
1065                         if (((MimeViewer *)viewer)->mimeview && 
1066                                 ((MimeViewer *)viewer)->mimeview->messageview && 
1067                                 ((MimeViewer *)viewer)->mimeview->messageview->window && 
1068                                 ((MimeViewer *)viewer)->mimeview->messageview->window->window) 
1069                                 gdk_window_set_cursor (((MimeViewer *)viewer)->mimeview->messageview->window->window, NULL);
1070                         else
1071                                 gdk_window_set_cursor (mainwindow_get_mainwindow()->window->window, NULL);
1072                 }
1073         }
1074         g_free(l);
1075         }
1076 }
1077 static gboolean pdf_viewer_scroll_cb(GtkWidget *widget, GdkEventScroll *event,
1078                                     PdfViewer *viewer)
1079 {
1080         GtkAdjustment *adj = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(viewer->scrollwin));
1081         static gboolean in_scroll_cb = FALSE;
1082         gboolean handled = FALSE;
1083         gint cur_p = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(viewer->cur_page));               
1084
1085         if (in_scroll_cb)
1086                 return FALSE;
1087
1088         in_scroll_cb = TRUE;
1089
1090         if (event->direction == GDK_SCROLL_UP &&
1091             adj->value == adj->lower && 
1092             cur_p > 1) {
1093                 gtk_spin_button_spin(GTK_SPIN_BUTTON(viewer->cur_page), GTK_SPIN_STEP_BACKWARD, 1);
1094                 adj->value = adj->upper - adj->page_size;
1095                 handled = TRUE;
1096         } else if (event->direction == GDK_SCROLL_DOWN &&
1097             adj->value + adj->page_size == adj->upper &&
1098             cur_p < viewer->num_pages) {
1099                 gtk_spin_button_spin(GTK_SPIN_BUTTON(viewer->cur_page), GTK_SPIN_STEP_FORWARD, 1);
1100                 adj->value = 0.0;
1101                 handled = TRUE;
1102         }
1103         in_scroll_cb = FALSE;
1104         return handled;
1105 }
1106
1107 static void pdf_viewer_button_zoom_fit_cb(GtkButton *button, PdfViewer *viewer)
1108 {
1109         GtkAllocation *allocation;
1110         double xratio, yratio;
1111         allocation = &(viewer->scrollwin->allocation);
1112         debug_print("width: %d\n", allocation->width);
1113         debug_print("height: %d\n", allocation->height);
1114         xratio = allocation->width / viewer->width;
1115         yratio = allocation->height / viewer->height;
1116
1117         if (xratio >= yratio) {
1118                 viewer->zoom = yratio;
1119                 gtk_spin_button_set_value(GTK_SPIN_BUTTON(viewer->zoom_scroll),viewer->zoom);
1120         }
1121         else {
1122                 viewer->zoom = xratio;
1123                 gtk_spin_button_set_value(GTK_SPIN_BUTTON(viewer->zoom_scroll),viewer->zoom);
1124         }
1125 }
1126
1127 static void pdf_viewer_button_zoom_width_cb(GtkButton *button, PdfViewer *viewer)
1128 {
1129         GtkAllocation *allocation;
1130         double xratio;
1131         allocation = &(viewer->scrollwin->allocation);
1132         debug_print("width: %d\n", allocation->width);
1133         xratio = allocation->width / viewer->width;
1134         gtk_spin_button_set_value(GTK_SPIN_BUTTON(viewer->zoom_scroll), xratio);
1135 }
1136
1137 static void pdf_viewer_button_rotate_right_cb(GtkButton *button, PdfViewer *viewer)
1138 {
1139         if (viewer->rotate == 360) {
1140                 viewer->rotate = 0;
1141         }
1142
1143         viewer->rotate += (gint) ROTATION;
1144         pdf_viewer_update((MimeViewer *)viewer, FALSE,
1145                 gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(viewer->cur_page)));
1146 }
1147
1148 static void pdf_viewer_button_rotate_left_cb(GtkButton *button, PdfViewer *viewer)
1149 {
1150         if (viewer->rotate == 0) {
1151                 viewer->rotate = 360;
1152         }
1153
1154         viewer->rotate = abs(viewer->rotate -(gint) ROTATION);
1155         pdf_viewer_update((MimeViewer *)viewer, FALSE,
1156                 gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(viewer->cur_page)));
1157 }
1158
1159 /* Show/Hide the index pane */
1160 static void pdf_viewer_show_document_index_cb(GtkButton *button, PdfViewer *viewer)
1161 {
1162         if (!viewer->pdf_index) {
1163                 viewer->pdf_index = poppler_index_iter_new(viewer->pdf_doc);
1164         }
1165
1166         gtk_tree_store_clear(GTK_TREE_STORE(viewer->index_model));
1167
1168         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(viewer->doc_index))) {
1169                 pdf_viewer_get_document_index(viewer,(PopplerIndexIter *) viewer->pdf_index, NULL);
1170                 gtk_widget_show(GTK_WIDGET(viewer->frame_index));
1171         }
1172         else {
1173                 pdf_viewer_hide_index_pane(viewer);
1174         }
1175
1176 }
1177
1178 static void pdf_viewer_button_document_info_cb(GtkButton *button, PdfViewer *viewer)
1179 {
1180         alertpanel_full(_("PDF properties"), NULL, GTK_STOCK_CLOSE, NULL, NULL,
1181                         FALSE, (GtkWidget *) pdf_viewer_fill_info_table(viewer), 
1182                         ALERT_NOTICE, G_ALERTDEFAULT);
1183 }
1184
1185 /*
1186 static const char * poppler_get_document_info_mode(PopplerPageMode mode)
1187 {
1188         GEnumValue *enum_value;
1189
1190         enum_value = g_enum_get_value((GEnumClass *) g_type_class_peek(POPPLER_TYPE_PAGE_MODE), mode);
1191         return(gchar *) enum_value->value_name;
1192 }
1193 static const char * poppler_get_document_info_layout(PopplerPageLayout layout)
1194 {
1195
1196         GEnumValue *enum_value;
1197
1198         enum_value = g_enum_get_value((GEnumClass *) g_type_class_peek(POPPLER_TYPE_PAGE_LAYOUT), layout);
1199         return(gchar *) enum_value->value_name;
1200 }
1201 */
1202 static void pdf_viewer_show_controls(PdfViewer *viewer, gboolean show)
1203 {
1204         if (show) {
1205                 gtk_widget_show(viewer->first_page);
1206                 gtk_widget_show(viewer->cur_page);
1207                 gtk_widget_show(viewer->prev_page);
1208                 gtk_widget_show(viewer->next_page);
1209                 gtk_widget_show(viewer->last_page);
1210                 gtk_widget_show(viewer->zoom_in);
1211                 gtk_widget_show(viewer->zoom_out);
1212                 gtk_widget_show(viewer->zoom_fit);
1213                 gtk_widget_show(viewer->zoom_width);
1214                 gtk_widget_show(viewer->zoom_scroll);
1215                 gtk_widget_show(viewer->widgets_table);
1216                 gtk_widget_show(viewer->rotate_right);
1217                 gtk_widget_show(viewer->rotate_left);
1218                 gtk_widget_show(viewer->doc_info);
1219                 gtk_widget_show(viewer->doc_index);
1220         } else {
1221                 gtk_widget_hide(viewer->first_page);
1222                 gtk_widget_hide(viewer->cur_page);
1223                 gtk_widget_hide(viewer->prev_page);
1224                 gtk_widget_hide(viewer->next_page);
1225                 gtk_widget_hide(viewer->last_page);
1226                 gtk_widget_hide(viewer->zoom_in);
1227                 gtk_widget_hide(viewer->zoom_out);
1228                 gtk_widget_hide(viewer->zoom_fit);
1229                 gtk_widget_hide(viewer->zoom_width);
1230                 gtk_widget_hide(viewer->widgets_table);
1231                 gtk_widget_hide(viewer->rotate_right);
1232                 gtk_widget_hide(viewer->rotate_left);
1233                 gtk_widget_hide(viewer->doc_info);
1234                 gtk_widget_hide(viewer->doc_index);
1235                 gtk_widget_hide(viewer->zoom_scroll);
1236         }
1237 }
1238 /** Render the current page, page_num on the viewer */
1239 static void pdf_viewer_update(MimeViewer *_viewer, gboolean reload_file, int page_num) 
1240 {
1241
1242         PdfViewer *viewer = (PdfViewer *) _viewer;
1243         GError *error = NULL;
1244         gchar *tmpfile = NULL;
1245         gchar *tmp;
1246         gchar *password = NULL;
1247
1248         debug_print("pdf_viewer_update\n");
1249
1250         if (reload_file) {
1251                 if (viewer->pdf_doc) {
1252                         g_object_unref(G_OBJECT(viewer->pdf_doc));
1253                         viewer->pdf_doc = NULL;
1254                 }
1255
1256                 if (pdf_viewer_mimepart_get_type(viewer->to_load) == TYPE_PS) {
1257                         stock_pixbuf_gdk(viewer->hbox, 
1258                                         STOCK_PIXMAP_MIME_PS, 
1259                                         &viewer->icon_pixbuf);
1260                         gtk_image_set_from_pixbuf(GTK_IMAGE(viewer->icon_type),
1261                                                                         viewer->icon_pixbuf);
1262                 } 
1263                 else if (pdf_viewer_mimepart_get_type(viewer->to_load) == TYPE_PDF) {
1264                         stock_pixbuf_gdk(viewer->hbox, 
1265                         STOCK_PIXMAP_MIME_PDF, 
1266                         &viewer->icon_pixbuf);
1267                         gtk_image_set_from_pixbuf(GTK_IMAGE(viewer->icon_type), 
1268                                                                         viewer->icon_pixbuf);
1269                 } 
1270                 else {
1271                         stock_pixbuf_gdk(viewer->hbox, 
1272                         STOCK_PIXMAP_MIME_APPLICATION, 
1273                         &viewer->icon_pixbuf);
1274                         gtk_image_set_from_pixbuf(GTK_IMAGE(viewer->icon_type), 
1275                                                                         viewer->icon_pixbuf);
1276                 }
1277
1278                 gtk_label_set_text(GTK_LABEL(viewer->doc_label), _("Loading..."));      
1279                 pdf_viewer_show_controls(viewer, FALSE);
1280                 main_window_cursor_wait(mainwindow_get_mainwindow());
1281
1282                 GTK_EVENTS_FLUSH();
1283
1284                 if (pdf_viewer_mimepart_get_type(viewer->to_load) == TYPE_PS) {
1285                         gchar *cmdline = NULL, *tmp = NULL, *gspath = NULL;
1286                         gint result = 0;
1287
1288                         if ((gspath = g_find_program_in_path("gs")) != NULL) {
1289                                 g_free(gspath);
1290                                 /* convert postscript to pdf */
1291                                 tmpfile = get_tmp_file();
1292                                 cmdline = g_strdup_printf(
1293                                         "gs -dSAFER -dCompatibilityLevel=1.2 -q -dNOPAUSE -dBATCH "
1294                                           "-sDEVICE=pdfwrite -sOutputFile=%s -c .setpdfwrite -f \"%s\"",
1295                                         tmpfile, viewer->filename);
1296                                 result = execute_command_line(cmdline, FALSE);
1297                                 if (result == 0) {
1298                                         tmp = g_filename_to_uri(tmpfile, NULL, NULL);
1299                                         viewer->pdf_doc = poppler_document_new_from_file( tmp, NULL, &error);
1300                                         g_free(tmp);
1301                                 } 
1302                                 else {
1303                                         g_warning("gs conversion failed: %s returned %d", cmdline, result);
1304                                         tmp = g_strdup_printf("gs: err %d", result);
1305                                         alertpanel_warning("%s", tmp);
1306                                         g_free(tmp);
1307                                 }
1308
1309                                 g_free(cmdline);
1310                                 claws_unlink(tmpfile);
1311                                 g_free(tmpfile);
1312                         }
1313                         else {
1314                                 g_warning("gs conversion disabled: gs binary was not found");
1315                                 alertpanel_warning("PostScript view disabled: required gs program not found");
1316                                 result = 1;
1317
1318                         }
1319                         if (result != 0) {
1320                                 main_window_cursor_normal(mainwindow_get_mainwindow());
1321                                 return;
1322                         }
1323                 }   
1324                 else {
1325                         viewer->pdf_doc = poppler_document_new_from_file( viewer->fsname, NULL, &error);
1326                 }
1327                 if (error && g_error_matches(error, POPPLER_ERROR, POPPLER_ERROR_ENCRYPTED)) {
1328                         g_clear_error(&error);
1329                         password = input_dialog_with_invisible(_("Enter password"),
1330                                         _("This document is locked and requires a password before it can be opened."),
1331                                         "");
1332                         viewer->pdf_doc = poppler_document_new_from_file(viewer->fsname, password, &error);
1333                         g_free(password);
1334                 }
1335
1336                 viewer->num_pages = poppler_document_get_n_pages(viewer->pdf_doc);
1337
1338                 g_signal_handlers_block_by_func(G_OBJECT(viewer->cur_page), pdf_viewer_spin_change_page_cb,(gpointer *)viewer);
1339                 gtk_spin_button_set_range(GTK_SPIN_BUTTON(viewer->cur_page), 
1340                                                                         1, 
1341                                                                 (gdouble)viewer->num_pages );
1342
1343                 g_signal_handlers_unblock_by_func(G_OBJECT(viewer->cur_page), pdf_viewer_spin_change_page_cb,(gpointer *)viewer);
1344                 gtk_spin_button_spin(GTK_SPIN_BUTTON(viewer->cur_page), GTK_SPIN_HOME, 1);
1345                 tmp = g_strdup_printf(_("%s Document"),pdf_viewer_mimepart_get_type(viewer->to_load) == TYPE_PDF ? "PDF":"Postscript");
1346                 CLAWS_SET_TIP(
1347                                 GTK_WIDGET(viewer->icon_type_ebox),
1348                                 tmp);
1349                 g_free(tmp);
1350
1351                 tmp = g_strdup_printf(_("of %d"), viewer->num_pages);
1352                 gtk_label_set_text(GTK_LABEL(viewer->doc_label), tmp);
1353                 g_free(tmp);
1354
1355                 pdf_viewer_show_controls(viewer, TRUE);
1356                 main_window_cursor_normal(mainwindow_get_mainwindow());
1357         } 
1358         if (viewer->pdf_doc == NULL) {
1359                 stock_pixbuf_gdk(viewer->hbox, 
1360                                 STOCK_PIXMAP_MIME_APPLICATION, 
1361                                 &viewer->icon_pixbuf);
1362
1363                 gtk_image_set_from_pixbuf(GTK_IMAGE(viewer->icon_type), viewer->icon_pixbuf);
1364                 if (error) {
1365                         strretchomp(error->message);
1366                         alertpanel_error("%s", error->message);
1367                 } else {
1368                         alertpanel_error(_("PDF rendering failed for an unknown reason."));
1369                 }
1370                 pdf_viewer_show_controls(viewer, FALSE);
1371                 g_error_free(error);
1372                 return;
1373         }
1374
1375         if (page_num == 1) { 
1376                 gtk_widget_set_sensitive(viewer->first_page, FALSE);
1377                 gtk_widget_set_sensitive(viewer->prev_page, FALSE);
1378         }
1379         else {
1380                 gtk_widget_set_sensitive(viewer->first_page, TRUE);
1381                 gtk_widget_set_sensitive(viewer->prev_page, TRUE);
1382         }
1383
1384         if (page_num == viewer->num_pages) { 
1385                 gtk_widget_set_sensitive(viewer->last_page, FALSE);
1386                 gtk_widget_set_sensitive(viewer->next_page, FALSE);
1387         }
1388         else {
1389                 gtk_widget_set_sensitive(viewer->last_page, TRUE);
1390                 gtk_widget_set_sensitive(viewer->next_page, TRUE);
1391         }
1392
1393         /* check for the index if exists */
1394         pdf_viewer_set_index_button_sensitive((PdfViewer *) viewer);
1395
1396         if (page_num > 0 && page_num <= viewer->num_pages) {
1397
1398                 GTK_EVENTS_FLUSH();
1399
1400                 if (viewer->pdf_page) {
1401                         g_object_unref(G_OBJECT(viewer->pdf_page));
1402                 }
1403
1404                 viewer->pdf_page = poppler_document_get_page(viewer->pdf_doc, page_num - 1);
1405
1406                 if (viewer->pdf_page == NULL) {
1407                         g_warning("Page not found");
1408                         return;
1409                 }   
1410
1411                 if (viewer->rotate == 90 || viewer->rotate == 270) {
1412                         poppler_page_get_size(viewer->pdf_page, &viewer->height, &viewer->width);
1413                 } 
1414                 else {
1415                         poppler_page_get_size(viewer->pdf_page, &viewer->width, &viewer->height);
1416                 }
1417
1418                 if (viewer->last_rect && viewer->last_page_result &&
1419                     viewer->last_page_result->page_num == page_num) {
1420                         pdf_viewer_render_selection(viewer, viewer->last_rect, viewer->last_page_result);
1421                 }
1422                 else {
1423                         pdf_viewer_render_page(viewer->pdf_page, viewer->pdf_view, viewer->width, 
1424                                                                         viewer->height, viewer->zoom, viewer->rotate);
1425
1426                 }
1427
1428         /* Get Links Mapping */
1429         if (viewer->link_map) {
1430                 poppler_page_free_link_mapping(viewer->link_map);
1431         }
1432         viewer->link_map = poppler_page_get_link_mapping(viewer->pdf_page);
1433
1434         }
1435 }
1436
1437
1438 static void pdf_viewer_show_mimepart(MimeViewer *_viewer, const gchar *infile,
1439                                 MimeInfo *partinfo)
1440 {
1441         PdfViewer *viewer = (PdfViewer *) _viewer;
1442         gchar buf[4096];
1443         const gchar *charset = NULL;
1444         MessageView *messageview = ((MimeViewer *)viewer)->mimeview 
1445                                         ?((MimeViewer *)viewer)->mimeview->messageview 
1446                                         : NULL;
1447
1448         viewer->rotate = 0;
1449         viewer->to_load = partinfo;
1450
1451         if (messageview)
1452                 messageview->updating = TRUE;
1453
1454         memset(buf, 0, sizeof(buf));
1455         debug_print("pdf_viewer_show_mimepart\n");
1456
1457         if (viewer->filename != NULL) {
1458                 claws_unlink(viewer->filename);
1459                 g_free(viewer->filename);
1460                 viewer->filename = NULL;
1461         }
1462
1463         viewer->mimeinfo = NULL;
1464
1465         if (partinfo) {
1466                 viewer->target_filename = procmime_get_part_file_name(partinfo);
1467                 viewer->filename = procmime_get_tmp_file_name(partinfo);
1468                 viewer->fsname = g_filename_to_uri(viewer->filename, NULL, NULL);
1469         }
1470
1471         if (partinfo && !(procmime_get_part(viewer->filename, partinfo) < 0)) {
1472
1473                 if (messageview && messageview->forced_charset)
1474                         charset = _viewer->mimeview->messageview->forced_charset;
1475                 else
1476                         charset = procmime_mimeinfo_get_parameter(partinfo, "charset");
1477
1478                 if (charset == NULL) {
1479                         charset = conv_get_locale_charset_str();
1480                 }
1481
1482                 debug_print("using charset %s\n", charset);
1483
1484                 viewer->mimeinfo = partinfo;
1485         }
1486
1487         pdf_viewer_update((MimeViewer *)viewer, TRUE, 1);
1488
1489         if (messageview)
1490                 messageview->updating = FALSE;
1491 }
1492
1493 static void pdf_viewer_clear(MimeViewer *_viewer)
1494 {
1495         PdfViewer *viewer = (PdfViewer *) _viewer;
1496         GtkAdjustment *vadj;
1497
1498         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(viewer->doc_index), FALSE);
1499         gtk_widget_hide(viewer->frame_index);
1500
1501         debug_print("pdf_viewer_clear\n");
1502         viewer->to_load = NULL;
1503
1504         if (viewer->pdf_doc) {
1505                 g_object_unref(G_OBJECT(viewer->pdf_doc));
1506                 viewer->pdf_doc = NULL;
1507         }
1508
1509         vadj = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(viewer->scrollwin));
1510         vadj->value = 0.0;
1511         g_signal_emit_by_name(G_OBJECT(vadj), "value-changed", 0);
1512         vadj = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(viewer->scrollwin_index));
1513         vadj->value = 0.0;
1514         g_signal_emit_by_name(G_OBJECT(vadj), "value-changed", 0);
1515         gtk_tree_store_clear(GTK_TREE_STORE(viewer->index_model));
1516         gtk_image_set_from_pixbuf(GTK_IMAGE(viewer->pdf_view), NULL);
1517 }
1518
1519 static void pdf_viewer_destroy(MimeViewer *_viewer)
1520 {
1521         PdfViewer *viewer = (PdfViewer *) _viewer;
1522
1523         debug_print("pdf_viewer_destroy\n");
1524
1525         if (viewer->pdf_index) poppler_index_iter_free(viewer->pdf_index);
1526
1527         poppler_page_free_link_mapping (viewer->link_map);
1528         g_object_unref(GTK_WIDGET(viewer->vbox));
1529         g_object_unref(GTK_WIDGET(viewer->pdf_view));
1530         g_object_unref(GTK_WIDGET(viewer->doc_index_pane));
1531         g_object_unref(GTK_WIDGET(viewer->scrollwin));
1532         g_object_unref(GTK_WIDGET(viewer->scrollwin_index));
1533         claws_unlink(viewer->filename);
1534         g_free(viewer->filename);
1535         g_free(viewer);
1536 }
1537
1538 static gboolean pdf_viewer_scroll_page(MimeViewer *_viewer, gboolean up)
1539 {
1540         PdfViewer *viewer = (PdfViewer *)_viewer;
1541         GtkAdjustment *vadj = gtk_scrolled_window_get_vadjustment(
1542                                 GTK_SCROLLED_WINDOW(viewer->scrollwin));
1543
1544         gint cur_p = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(viewer->cur_page));
1545
1546         if (viewer->pdf_view == NULL) return FALSE;
1547
1548         if (!gtkutils_scroll_page(GTK_WIDGET(viewer->pdf_view), vadj, up)) {
1549                 if (!up && cur_p != viewer->num_pages) {
1550                         gtk_spin_button_spin(GTK_SPIN_BUTTON(viewer->cur_page), GTK_SPIN_STEP_FORWARD, 1);
1551                         vadj = gtk_scrolled_window_get_vadjustment(
1552                                         GTK_SCROLLED_WINDOW(viewer->scrollwin));
1553                         vadj->value = 0.0;
1554                         g_signal_emit_by_name(G_OBJECT(vadj), "value-changed", 0);
1555                         return TRUE;
1556                 } 
1557                 else if (up && cur_p != 1) {
1558                         gtk_spin_button_spin(GTK_SPIN_BUTTON(viewer->cur_page), GTK_SPIN_STEP_BACKWARD, 1);
1559                         vadj = gtk_scrolled_window_get_vadjustment(
1560                                         GTK_SCROLLED_WINDOW(viewer->scrollwin));
1561                         vadj->value = vadj->upper - vadj->page_size;
1562                         g_signal_emit_by_name(G_OBJECT(vadj), "value-changed", 0);
1563                         return TRUE;
1564                 } 
1565                 return FALSE;
1566         } 
1567         else return TRUE;
1568 }
1569
1570 static void pdf_viewer_scroll_one_line(MimeViewer *_viewer, gboolean up)
1571 {
1572         PdfViewer *viewer = (PdfViewer *)_viewer;
1573         GtkAdjustment *vadj = gtk_scrolled_window_get_vadjustment(
1574                                 GTK_SCROLLED_WINDOW(viewer->scrollwin));
1575         gint cur_p;
1576         cur_p = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(viewer->cur_page));
1577
1578         if (viewer->pdf_view == NULL) return; 
1579                 debug_print("up: %d\n", up);    
1580                 if (vadj->value <(vadj->upper - vadj->page_size))  {
1581                         gtkutils_scroll_one_line(GTK_WIDGET(viewer->pdf_view), vadj, up);
1582                 }
1583                 else {
1584                         if (cur_p != viewer->num_pages) {
1585                                 pdf_viewer_scroll_page((MimeViewer *)viewer, up);
1586                         }
1587                 }
1588
1589 }
1590
1591 #define BUTTON_H_PADDING 3
1592 #define ADD_BUTTON_TO_TABLE(widget, stock_image) \
1593         widget = gtk_button_new(); \
1594         img = gtk_image_new_from_stock(stock_image, GTK_ICON_SIZE_MENU); \
1595         gtk_button_set_image(GTK_BUTTON(widget), img); \
1596         gtk_table_attach(GTK_TABLE(viewer->widgets_table), GTK_WIDGET(widget), \
1597                                 col, col+1, 0, 1, 0, 0, BUTTON_H_PADDING, 0); \
1598         col++;
1599
1600 #define ADD_SEP_TO_TABLE \
1601         sep = gtk_label_new(""); \
1602         gtk_table_attach(GTK_TABLE(viewer->widgets_table), GTK_WIDGET(sep), \
1603                                         col, col+1, 0, 1, 0, 0, 0, 0); \
1604         gtk_table_set_col_spacing(GTK_TABLE(viewer->widgets_table), col, 3*BUTTON_H_PADDING); \
1605         col++;
1606
1607 #if POPPLER_HAS_CAIRO
1608 static PangoContext *pdf_viewer_get_pango_context(gpointer data)
1609 {
1610         return NULL;
1611 }
1612
1613 static gpointer pdf_viewer_get_data_to_print(gpointer data, gint sel_start, gint sel_end)
1614 {
1615         return NULL; /* we don't need it */
1616 }
1617
1618 static void pdf_viewer_cb_begin_print(GtkPrintOperation *op, GtkPrintContext *context,
1619                            gpointer user_data)
1620 {
1621   PrintData *print_data;
1622   PopplerDocument *pdf_doc;
1623   gint n_pages = 0;
1624   print_data = (PrintData*) user_data;
1625   pdf_doc = (PopplerDocument *)printing_get_renderer_data(print_data);
1626
1627   debug_print("Preparing print job...\n");
1628
1629   n_pages = poppler_document_get_n_pages(pdf_doc);
1630   printing_set_n_pages(print_data, n_pages);
1631   gtk_print_operation_set_n_pages(op, n_pages);
1632
1633   debug_print("Starting print job...\n");
1634 }
1635
1636 static void pdf_viewer_cb_draw_page(GtkPrintOperation *op, GtkPrintContext *context,
1637                          int page_nr, gpointer user_data)
1638 {
1639   cairo_t *cr;
1640   PrintData *print_data;
1641   PopplerDocument *pdf_doc;
1642   PopplerPage *pdf_page;
1643   
1644   print_data = (PrintData*) user_data;
1645   pdf_doc = (PopplerDocument *)printing_get_renderer_data(print_data);
1646   pdf_page = poppler_document_get_page(pdf_doc, page_nr);
1647
1648   cr = gtk_print_context_get_cairo_context(context);
1649   cairo_scale(cr, printing_get_zoom(print_data), printing_get_zoom(print_data));
1650   cairo_set_source_rgb(cr, 0., 0., 0.);
1651
1652   poppler_page_render_for_printing(pdf_page, cr);
1653
1654   g_object_unref(G_OBJECT(pdf_page));
1655
1656   debug_print("Sent page %d to printer\n", page_nr+1);
1657 }
1658
1659 static void pdf_viewer_print(MimeViewer *mviewer)
1660 {
1661         PdfViewer *viewer = (PdfViewer *)mviewer;
1662         PrintRenderer *pdf_renderer = g_new0(PrintRenderer, 1);
1663         MainWindow *mainwin = mainwindow_get_mainwindow();
1664
1665         pdf_renderer->get_pango_context = pdf_viewer_get_pango_context;
1666         pdf_renderer->get_data_to_print = pdf_viewer_get_data_to_print;
1667         pdf_renderer->cb_begin_print    = pdf_viewer_cb_begin_print;
1668         pdf_renderer->cb_draw_page      = pdf_viewer_cb_draw_page;
1669
1670         printing_print_full(mainwin ? GTK_WINDOW(mainwin->window):NULL,
1671                         pdf_renderer, viewer->pdf_doc, -1, -1, NULL);
1672
1673         g_free(pdf_renderer);
1674 }
1675 #endif
1676
1677 static MimeViewer *pdf_viewer_create(void)
1678 {
1679         PdfViewer *viewer;
1680         GtkTreeViewColumn *column;
1681         GtkCellRenderer *renderer;
1682         GtkTreeStore *tree_store;
1683         GtkWidget *sep;
1684         GtkWidget *img;
1685         gint col = 0;
1686
1687         viewer = g_new0(PdfViewer, 1);
1688         debug_print("pdf_viewer_create\n");
1689     
1690         viewer->last_x = 0;
1691         viewer->last_y = 0;
1692         viewer->mimeviewer.factory = &pdf_viewer_factory;
1693         viewer->mimeviewer.get_widget = pdf_viewer_get_widget;
1694         viewer->mimeviewer.show_mimepart = pdf_viewer_show_mimepart;
1695         viewer->mimeviewer.clear_viewer = pdf_viewer_clear;
1696         viewer->mimeviewer.destroy_viewer = pdf_viewer_destroy;
1697         viewer->mimeviewer.text_search = pdf_viewer_text_search;
1698         viewer->mimeviewer.scroll_page = pdf_viewer_scroll_page;
1699         viewer->mimeviewer.scroll_one_line = pdf_viewer_scroll_one_line;
1700 #if POPPLER_HAS_CAIRO
1701         viewer->mimeviewer.print = pdf_viewer_print;
1702 #endif
1703         viewer->scrollwin = gtk_scrolled_window_new(NULL, NULL);
1704         viewer->scrollwin_index = gtk_scrolled_window_new(NULL, NULL);
1705         viewer->pdf_view_ebox = gtk_event_box_new();
1706         gtk_event_box_set_visible_window(GTK_EVENT_BOX(viewer->pdf_view_ebox), FALSE);
1707
1708         viewer->mimeinfo  = NULL;
1709
1710         viewer->pdf_view = gtk_image_new();
1711         gtk_widget_set_events(viewer->pdf_view_ebox,
1712                                                 GDK_BUTTON_RELEASE_MASK
1713                                                 | GDK_POINTER_MOTION_MASK
1714                                                 | GDK_BUTTON_PRESS_MASK
1715                                                 | GDK_BUTTON_MOTION_MASK
1716                                             );
1717         gtk_container_add (GTK_CONTAINER(viewer->pdf_view_ebox), viewer->pdf_view);
1718         viewer->icon_type = gtk_image_new();
1719         viewer->icon_type_ebox = gtk_event_box_new();
1720
1721         gtk_container_add(GTK_CONTAINER(viewer->icon_type_ebox), viewer->icon_type);
1722
1723         viewer->doc_label = gtk_label_new("");
1724
1725         viewer->widgets_table = gtk_table_new(1, 1, FALSE);
1726
1727         viewer->doc_index_pane = gtk_hpaned_new();
1728
1729         viewer->frame_index = gtk_frame_new(NULL);
1730         gtk_frame_set_shadow_type(GTK_FRAME(viewer->frame_index), GTK_SHADOW_IN);
1731         gtk_widget_set_size_request(viewer->frame_index, 18, -1);
1732         gtk_frame_set_label(GTK_FRAME(viewer->frame_index), _("Document Index"));
1733
1734         ADD_SEP_TO_TABLE
1735         ADD_BUTTON_TO_TABLE(viewer->first_page, GTK_STOCK_GOTO_FIRST)
1736         ADD_BUTTON_TO_TABLE(viewer->prev_page, GTK_STOCK_GO_BACK)
1737         viewer->cur_page = gtk_spin_button_new_with_range(0.0, 0.0, 1.0);
1738         viewer->zoom_scroll = gtk_spin_button_new_with_range(0.20, 8.0, 0.20);
1739         gtk_spin_button_set_value(GTK_SPIN_BUTTON(viewer->zoom_scroll), 1.0);
1740         viewer->zoom = 1.0;
1741         gtk_spin_button_set_numeric(GTK_SPIN_BUTTON(viewer->cur_page), TRUE);
1742         gtk_spin_button_set_numeric(GTK_SPIN_BUTTON(viewer->zoom_scroll), TRUE);
1743         gtk_table_attach(GTK_TABLE(viewer->widgets_table), GTK_WIDGET(viewer->cur_page),
1744                                         col, col+1, 
1745                                         0, 1, 0, 0, 
1746                                         BUTTON_H_PADDING, 
1747                                         0);
1748         col++;
1749         gtk_table_attach(GTK_TABLE(viewer->widgets_table), GTK_WIDGET(viewer->doc_label),
1750                                         col, col+1, 
1751                                         0, 1, 0, 0, 
1752                                         BUTTON_H_PADDING, 
1753                                         0);
1754         col++;
1755
1756         ADD_BUTTON_TO_TABLE(viewer->next_page, GTK_STOCK_GO_FORWARD)
1757         ADD_BUTTON_TO_TABLE(viewer->last_page, GTK_STOCK_GOTO_LAST)
1758         ADD_SEP_TO_TABLE
1759         ADD_BUTTON_TO_TABLE(viewer->zoom_fit, GTK_STOCK_ZOOM_FIT)
1760         ADD_BUTTON_TO_TABLE(viewer->zoom_in, GTK_STOCK_ZOOM_IN)
1761         gtk_table_attach(GTK_TABLE(viewer->widgets_table), GTK_WIDGET(viewer->zoom_scroll),
1762                                         col, col+1, 
1763                                         0, 1, 0, 0, 
1764                                         BUTTON_H_PADDING, 
1765                                         0);
1766         col++;
1767         ADD_BUTTON_TO_TABLE(viewer->zoom_out, GTK_STOCK_ZOOM_OUT)
1768         ADD_BUTTON_TO_TABLE(viewer->zoom_width, GTK_STOCK_FULLSCREEN)
1769         ADD_SEP_TO_TABLE
1770         ADD_BUTTON_TO_TABLE(viewer->rotate_left, GTK_STOCK_UNDO)
1771         ADD_BUTTON_TO_TABLE(viewer->rotate_right, GTK_STOCK_REDO)
1772         ADD_SEP_TO_TABLE
1773         ADD_BUTTON_TO_TABLE(viewer->doc_info, GTK_STOCK_INFO)
1774
1775         viewer->doc_index = GTK_WIDGET(gtk_toggle_tool_button_new_from_stock(GTK_STOCK_INDEX));
1776         gtk_widget_set_size_request(GTK_WIDGET(viewer->doc_index), 26, 26);
1777         gtk_table_attach(GTK_TABLE(viewer->widgets_table), GTK_WIDGET(viewer->doc_index),
1778                                         col, col+1, 
1779                                         0, 1, 0, 0, 
1780                                         BUTTON_H_PADDING, 
1781                                         0);
1782         col++;
1783
1784         gtk_scrolled_window_set_policy(
1785                         GTK_SCROLLED_WINDOW(viewer->scrollwin), 
1786                         GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1787
1788         gtk_scrolled_window_set_shadow_type(
1789                         GTK_SCROLLED_WINDOW(viewer->scrollwin),
1790                         GTK_SHADOW_IN);
1791
1792         gtk_scrolled_window_add_with_viewport(
1793                         GTK_SCROLLED_WINDOW(viewer->scrollwin),
1794                         viewer->pdf_view_ebox);
1795
1796         viewer->vbox = gtk_vbox_new(FALSE, 4);
1797         viewer->hbox = gtk_hbox_new(FALSE, 4);
1798
1799     /* treeview */
1800         tree_store = gtk_tree_store_new(N_INDEX_COLUMNS,
1801                                         G_TYPE_STRING,
1802                                         G_TYPE_INT,
1803                                         G_TYPE_DOUBLE);
1804
1805         viewer->index_list = gtk_tree_view_new_with_model(GTK_TREE_MODEL(tree_store));
1806         renderer = gtk_cell_renderer_text_new();
1807         column = gtk_tree_view_column_new_with_attributes(_("Name"),  renderer, "text", 0,  NULL);
1808         gtk_tree_view_append_column(GTK_TREE_VIEW(viewer->index_list), column);         
1809         gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(viewer->index_list), FALSE);
1810
1811         viewer->index_model = GTK_TREE_MODEL(tree_store);
1812
1813         gtk_tree_selection_set_mode(gtk_tree_view_get_selection(GTK_TREE_VIEW(viewer->index_list)), 
1814                                                                 GTK_SELECTION_SINGLE);
1815
1816         g_signal_connect(G_OBJECT(viewer->index_list), "row_activated",
1817                          G_CALLBACK(pdf_viewer_index_row_activated),
1818                                          viewer);
1819
1820         gtk_scrolled_window_set_policy(
1821                         GTK_SCROLLED_WINDOW(viewer->scrollwin_index), 
1822                         GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1823
1824         gtk_scrolled_window_set_shadow_type(
1825                         GTK_SCROLLED_WINDOW(viewer->scrollwin_index),
1826                         GTK_SHADOW_IN);
1827
1828         gtk_scrolled_window_add_with_viewport(
1829                         GTK_SCROLLED_WINDOW(viewer->scrollwin_index),
1830                         viewer->index_list);
1831
1832         /* end treeview */
1833
1834         stock_pixbuf_gdk(viewer->hbox, 
1835                         STOCK_PIXMAP_MIME_TEXT_PLAIN, 
1836                         &viewer->icon_pixbuf);
1837
1838         gtk_image_set_from_pixbuf(GTK_IMAGE(viewer->icon_type), 
1839                                 viewer->icon_pixbuf);
1840
1841         /* pack widgets*/
1842         gtk_box_pack_start(GTK_BOX(viewer->hbox), viewer->icon_type_ebox, FALSE, FALSE, 0);
1843         gtk_box_pack_start(GTK_BOX(viewer->hbox), viewer->widgets_table, FALSE, FALSE, 0);
1844
1845         gtk_container_add(GTK_CONTAINER(viewer->frame_index), viewer->scrollwin_index);
1846
1847         gtk_paned_pack1(GTK_PANED(viewer->doc_index_pane), viewer->frame_index, FALSE, FALSE);
1848         gtk_paned_pack2(GTK_PANED(viewer->doc_index_pane), viewer->scrollwin, FALSE, FALSE);
1849
1850         gtk_box_pack_start(GTK_BOX(viewer->vbox), viewer->hbox, FALSE, FALSE, 0);
1851         gtk_box_pack_start(GTK_BOX(viewer->vbox), viewer->doc_index_pane, TRUE, TRUE, 0);
1852         /* show widgets */
1853         gtk_widget_show(GTK_WIDGET(viewer->doc_index_pane));
1854         g_object_ref(GTK_WIDGET(viewer->doc_index_pane));
1855         gtk_widget_show(GTK_WIDGET(viewer->scrollwin));
1856         g_object_ref(GTK_WIDGET(viewer->scrollwin));
1857         gtk_widget_show(GTK_WIDGET(viewer->icon_type_ebox));
1858         g_object_ref(GTK_WIDGET(viewer->icon_type_ebox));
1859         gtk_widget_show(GTK_WIDGET(viewer->pdf_view_ebox));
1860         g_object_ref(GTK_WIDGET(viewer->pdf_view_ebox));
1861         gtk_widget_show(GTK_WIDGET(viewer->scrollwin_index));
1862         g_object_ref(GTK_WIDGET(viewer->scrollwin_index));
1863         gtk_widget_show(GTK_WIDGET(viewer->hbox));
1864         g_object_ref(GTK_WIDGET(viewer->hbox)); 
1865         gtk_widget_show(GTK_WIDGET(viewer->vbox));
1866         g_object_ref(GTK_WIDGET(viewer->vbox));
1867
1868         gtk_widget_show(GTK_WIDGET(viewer->widgets_table));
1869         g_object_ref(GTK_WIDGET(viewer->widgets_table));
1870
1871         gtk_widget_show(GTK_WIDGET(viewer->cur_page));
1872         g_object_ref(GTK_WIDGET(viewer->cur_page));
1873
1874         gtk_widget_show(GTK_WIDGET(viewer->first_page));
1875         g_object_ref(GTK_WIDGET(viewer->first_page));
1876
1877         gtk_widget_show(GTK_WIDGET(viewer->last_page));
1878         g_object_ref(GTK_WIDGET(viewer->last_page));
1879
1880         gtk_widget_show(GTK_WIDGET(viewer->prev_page));
1881         g_object_ref(GTK_WIDGET(viewer->prev_page));
1882
1883         gtk_widget_show(GTK_WIDGET(viewer->next_page));
1884         g_object_ref(GTK_WIDGET(viewer->next_page));
1885
1886         gtk_widget_show(GTK_WIDGET(viewer->zoom_in));
1887         g_object_ref(GTK_WIDGET(viewer->zoom_in));
1888         gtk_widget_show(GTK_WIDGET(viewer->zoom_out));
1889         g_object_ref(GTK_WIDGET(viewer->zoom_out));
1890         gtk_widget_show(GTK_WIDGET(viewer->zoom_fit));
1891         g_object_ref(GTK_WIDGET(viewer->zoom_fit));
1892         gtk_widget_show(GTK_WIDGET(viewer->zoom_width));
1893         g_object_ref(GTK_WIDGET(viewer->zoom_width));
1894
1895         gtk_widget_show(GTK_WIDGET(viewer->rotate_right));
1896         g_object_ref(GTK_WIDGET(viewer->rotate_right));
1897         gtk_widget_show(GTK_WIDGET(viewer->rotate_left));
1898         g_object_ref(GTK_WIDGET(viewer->rotate_left));
1899         gtk_widget_show(GTK_WIDGET(viewer->doc_info));
1900         g_object_ref(GTK_WIDGET(viewer->doc_info));
1901         gtk_widget_show(GTK_WIDGET(viewer->doc_index));
1902         g_object_ref(GTK_WIDGET(viewer->doc_index));
1903
1904         gtk_widget_show(GTK_WIDGET(viewer->doc_label));
1905         g_object_ref(GTK_WIDGET(viewer->doc_label));
1906         gtk_widget_show(GTK_WIDGET(viewer->icon_type));
1907         g_object_ref(GTK_WIDGET(viewer->icon_type));    
1908         gtk_widget_show(GTK_WIDGET(viewer->pdf_view));
1909         g_object_ref(GTK_WIDGET(viewer->pdf_view));
1910         gtk_widget_show(GTK_WIDGET(viewer->zoom_scroll));
1911         g_object_ref(GTK_WIDGET(viewer->zoom_scroll));
1912
1913         gtk_widget_show(GTK_WIDGET(viewer->index_list));
1914         g_object_ref(GTK_WIDGET(viewer->index_list));
1915
1916         /* Set Tooltips*/
1917         CLAWS_SET_TIP(viewer->first_page,
1918                                 _("First Page"));
1919
1920         CLAWS_SET_TIP(viewer->prev_page,
1921                                 _("Previous Page"));
1922
1923         CLAWS_SET_TIP(viewer->next_page,
1924                                 _("Next Page"));
1925
1926         CLAWS_SET_TIP(viewer->last_page,
1927                                 _("Last Page"));
1928
1929         CLAWS_SET_TIP(viewer->zoom_in,
1930                                 _("Zoom In"));
1931         CLAWS_SET_TIP(viewer->zoom_out,
1932                                 _("Zoom Out"));
1933
1934         CLAWS_SET_TIP(viewer->zoom_fit,
1935                                 _("Fit Page"));
1936
1937         CLAWS_SET_TIP(viewer->zoom_width,
1938                                 _("Fit Page Width"));
1939
1940         CLAWS_SET_TIP(viewer->rotate_left,
1941                                 _("Rotate Left"));
1942
1943         CLAWS_SET_TIP(viewer->rotate_right,
1944                                 _("Rotate Right"));
1945
1946         CLAWS_SET_TIP(viewer->doc_info,
1947                                 _("Document Info"));
1948
1949         CLAWS_SET_TIP(viewer->doc_index,
1950                                 _("Document Index"));
1951         CLAWS_SET_TIP(viewer->cur_page,
1952                                 _("Page Number"));
1953         CLAWS_SET_TIP(viewer->zoom_scroll,
1954                                 _("Zoom Factor"));
1955         /* Connect Signals */
1956         g_signal_connect(G_OBJECT(viewer->cur_page), 
1957                                     "value-changed", 
1958                                     G_CALLBACK(pdf_viewer_spin_change_page_cb), 
1959                                    (gpointer) viewer);
1960
1961         g_signal_connect(G_OBJECT(viewer->first_page), 
1962                                     "clicked", 
1963                                     G_CALLBACK(pdf_viewer_button_first_page_cb), 
1964                                    (gpointer) viewer);
1965         g_signal_connect(G_OBJECT(viewer->prev_page), 
1966                                     "clicked", 
1967                                     G_CALLBACK(pdf_viewer_button_prev_page_cb), 
1968                                    (gpointer) viewer);
1969         g_signal_connect(G_OBJECT(viewer->next_page), 
1970                                     "clicked", 
1971                                     G_CALLBACK(pdf_viewer_button_next_page_cb), 
1972                                    (gpointer) viewer);
1973         g_signal_connect(G_OBJECT(viewer->last_page), 
1974                                     "clicked", 
1975                                     G_CALLBACK(pdf_viewer_button_last_page_cb), 
1976                                    (gpointer) viewer);
1977         g_signal_connect(G_OBJECT(viewer->zoom_in), 
1978                                     "clicked", 
1979                                     G_CALLBACK(pdf_viewer_button_zoom_in_cb), 
1980                                    (gpointer) viewer);
1981         g_signal_connect(G_OBJECT(viewer->zoom_out), 
1982                                     "clicked", 
1983                                     G_CALLBACK(pdf_viewer_button_zoom_out_cb), 
1984                                    (gpointer) viewer);
1985         g_signal_connect(G_OBJECT(viewer->zoom_scroll), 
1986                                     "value-changed", 
1987                                     G_CALLBACK(pdf_viewer_spin_zoom_scroll_cb), 
1988                                    (gpointer) viewer);
1989
1990         g_signal_connect(G_OBJECT(viewer->zoom_fit), 
1991                                    "clicked", 
1992                                     G_CALLBACK(pdf_viewer_button_zoom_fit_cb), 
1993                                    (gpointer) viewer);
1994
1995         g_signal_connect(G_OBJECT(viewer->zoom_width), 
1996                                     "clicked", 
1997                                     G_CALLBACK(pdf_viewer_button_zoom_width_cb), 
1998                                    (gpointer) viewer);
1999
2000         g_signal_connect(G_OBJECT(viewer->rotate_right), 
2001                                     "clicked", 
2002                                     G_CALLBACK(pdf_viewer_button_rotate_right_cb), 
2003                                    (gpointer) viewer);
2004
2005         g_signal_connect(G_OBJECT(viewer->rotate_left), 
2006                                     "clicked", 
2007                                     G_CALLBACK(pdf_viewer_button_rotate_left_cb), 
2008                                    (gpointer) viewer);
2009
2010         g_signal_connect(G_OBJECT(viewer->doc_info), 
2011                                     "clicked", 
2012                                     G_CALLBACK(pdf_viewer_button_document_info_cb), 
2013                                    (gpointer) viewer);  
2014
2015         g_signal_connect(G_OBJECT(viewer->doc_index), 
2016                                     "clicked", 
2017                                     G_CALLBACK(pdf_viewer_show_document_index_cb), 
2018                                    (gpointer) viewer);
2019         g_signal_connect(G_OBJECT(viewer->scrollwin), 
2020                                     "scroll-event", 
2021                                     G_CALLBACK(pdf_viewer_scroll_cb), 
2022                                    (gpointer) viewer);
2023         g_signal_connect(G_OBJECT(viewer->pdf_view_ebox), 
2024                                     "button_press_event", 
2025                                     G_CALLBACK(pdf_viewer_button_press_events_cb), 
2026                                    (gpointer) viewer);
2027         g_signal_connect(G_OBJECT(viewer->pdf_view_ebox), 
2028                                     "button_release_event", 
2029                                     G_CALLBACK(pdf_viewer_mouse_scroll_destroy_cb), 
2030                                    (gpointer) viewer);
2031         g_signal_connect(G_OBJECT(viewer->pdf_view_ebox), 
2032                                     "motion_notify_event", 
2033                                     G_CALLBACK(pdf_viewer_move_events_cb), 
2034                                    (gpointer) viewer);
2035
2036         viewer->target_filename = NULL;
2037         viewer->filename = NULL;
2038         viewer->fsname = NULL;
2039
2040         return(MimeViewer *) viewer;
2041 }
2042
2043 #undef ADD_BUTTON_TO_TABLE
2044 #undef ADD_SEP_TO_TABLE
2045 #undef BUTTON_H_PADDING
2046 #undef SEP_H_PADDING
2047
2048 static MimeViewerFactory pdf_viewer_factory =
2049 {
2050         content_types,
2051         0,
2052         pdf_viewer_create,
2053 };
2054
2055 gint plugin_init(gchar **error)
2056 {
2057         gchar *gspath = NULL;
2058
2059         msg = g_strdup_printf(_("This plugin enables the viewing of PDF and PostScript "
2060                                 "attachments using the Poppler %s Lib and the gs tool.\n\n"
2061                                 "Any feedback is welcome: iwkse@claws-mail.org"
2062                                 ), poppler_get_version());
2063
2064         if (!check_plugin_version(MAKE_NUMERIC_VERSION(3,8,1,46),
2065                     VERSION_NUMERIC, _("PDF Viewer"), error)) return -1;
2066
2067         if ((gspath = g_find_program_in_path("gs")) == NULL) {
2068                 gchar *pmsg = msg;
2069                 msg = g_strdup_printf(_("Warning: could not find ghostscript binary (gs) required "
2070                                         "for %s plugin to process PostScript attachments, only PDF "
2071                                         "attachments will be displayed. To enable PostScript "
2072                                         "support please install gs program.\n\n%s"
2073                                         ), _("PDF Viewer"), pmsg);
2074                 g_free(pmsg);
2075         }
2076         else {
2077                 g_free(gspath);
2078         }
2079
2080         mimeview_register_viewer_factory(&pdf_viewer_factory);
2081         return 0;
2082 }
2083
2084 gboolean plugin_done(void)
2085 {
2086         g_free(msg);    
2087         mimeview_unregister_viewer_factory(&pdf_viewer_factory);
2088         return TRUE;
2089 }
2090
2091 const gchar *plugin_name(void)
2092 {
2093         return _("PDF Viewer");
2094 }
2095
2096 const gchar *plugin_desc(void)
2097 {
2098         return msg;
2099 }
2100
2101 const gchar *plugin_type(void)
2102 {
2103         return "GTK2";
2104 }
2105
2106 const gchar *plugin_licence(void)
2107 {
2108         return "GPL3+";
2109 }
2110
2111 const gchar *plugin_version(void)
2112 {
2113         return VERSION;
2114 }
2115
2116 struct PluginFeature *plugin_provides(void)
2117 {
2118         static struct PluginFeature features[] = 
2119                 { {PLUGIN_MIMEVIEWER, "application/pdf"},
2120                   {PLUGIN_MIMEVIEWER, "application/postscript"},
2121                   {PLUGIN_NOTHING, NULL} };
2122         return features;
2123 }
2124