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