support image/x-eps format via pdf_viewer
[claws.git] / src / plugins / pdf_viewer / poppler_viewer.c
blob5acd6e2bd00a29dc95073f18e560d6b4f997e117
1 /*
2 * Claws Mail -- a GTK based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2025 the Claws Mail Team and Salvatore De Paolis
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.
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.
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/>.
19 #ifdef HAVE_CONFIG_H
20 # include "config.h"
21 #include "claws-features.h"
22 #endif
24 #include <glib.h>
25 #include <glib/gi18n.h>
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 #include "summaryview.h"
34 #include "file-utils.h"
35 #ifndef POPPLER_WITH_GDK
36 #include "stdbool.h"
37 #endif
39 static FileType pdf_viewer_mimepart_get_type(MimeInfo *partinfo);
40 static MimeViewerFactory pdf_viewer_factory;
42 static void pdf_viewer_show_mimepart(MimeViewer *_viewer, const gchar *infile,
43 MimeInfo *partinfo);
45 static MimeViewer *pdf_viewer_create(void);
46 static void pdf_viewer_clear(MimeViewer *_viewer);
47 static void pdf_viewer_destroy(MimeViewer *_viewer);
48 static void pdf_viewer_update(MimeViewer *_viewer, gboolean reload_file, int page_num);
50 static GtkWidget *pdf_viewer_get_widget(MimeViewer *_viewer);
52 static void pdf_viewer_hide_index_pane(PdfViewer *viewer);
53 static void pdf_viewer_set_index_button_sensitive(PdfViewer *viewer);
54 static void pdf_viewer_scroll_to(PdfViewer *viewer, gfloat x, gfloat y);
56 static void search_matches_free(PdfViewer *viewer);
57 static gboolean pdf_viewer_text_search(MimeViewer *_viewer, gboolean backward,
58 const gchar *str, gboolean case_sens);
59 static void pdf_viewer_render_selection(PdfViewer *viewer, PopplerRectangle *rect, PageResult *page_results);
60 static void pdf_viewer_render_page(PopplerPage *page, GtkWidget *view, double width, double height, double zoom, gint rotate);
62 static char * pdf_viewer_get_document_format_data(GDateTime *utime);
63 static void pdf_viewer_get_document_index(PdfViewer *viewer, PopplerIndexIter *index_iter, GtkTreeIter *parentiter);
64 static void pdf_viewer_index_row_activated(GtkTreeView *tree_view,
65 GtkTreePath *path,
66 GtkTreeViewColumn *column,
67 gpointer data);
69 static GtkGrid * pdf_viewer_fill_info_table(PdfViewer *viewer);
71 /* Callbacks */
72 static void pdf_viewer_move_events_cb(GtkWidget *widget, GdkEventMotion *event, PdfViewer *viewer);
73 static void pdf_viewer_button_press_events_cb(GtkWidget *widget, GdkEventButton *event, PdfViewer *viewer);
74 static void pdf_viewer_mouse_scroll_destroy_cb(GtkWidget *widget, GdkEventButton *event, PdfViewer *viewer);
75 static void pdf_viewer_button_first_page_cb(GtkButton *button, PdfViewer *viewer);
76 static void pdf_viewer_button_last_page_cb(GtkButton *button, PdfViewer *viewer);
77 static void pdf_viewer_button_zoom_in_cb(GtkButton *button, PdfViewer *viewer);
78 static void pdf_viewer_button_zoom_out_cb(GtkButton *button, PdfViewer *viewer);
79 static void pdf_viewer_button_zoom_fit_cb(GtkButton *button, PdfViewer *viewer);
80 static void pdf_viewer_button_zoom_width_cb(GtkButton *button, PdfViewer *viewer);
81 static void pdf_viewer_button_rotate_right_cb(GtkButton *button, PdfViewer *viewer);
82 static void pdf_viewer_button_rotate_left_cb(GtkButton *button, PdfViewer *viewer);
83 static void pdf_viewer_spin_change_page_cb(GtkSpinButton *button, PdfViewer *viewer);
84 static void pdf_viewer_spin_zoom_scroll_cb(GtkSpinButton *button, PdfViewer *viewer);
85 /* Show/Hide the index pane */
86 static void pdf_viewer_show_document_index_cb(GtkToggleButton *button, PdfViewer *viewer);
87 static void pdf_viewer_button_print_cb(GtkButton *button, PdfViewer *viewer);
88 static void pdf_viewer_button_document_info_cb(GtkButton *button, PdfViewer *viewer);
90 static void pdf_viewer_show_controls(PdfViewer *viewer, gboolean show);
91 static gboolean pdf_viewer_scroll_page(MimeViewer *_viewer, gboolean up);
92 static void pdf_viewer_scroll_one_line(MimeViewer *_viewer, gboolean up);
94 /** Claws Mail Plugin functions */
95 gint plugin_init(gchar **error);
96 const gchar *plugin_name(void);
97 const gchar *plugin_desc(void);
98 const gchar *plugin_type(void);
99 const gchar *plugin_licence(void);
100 const gchar *plugin_version(void);
101 struct PluginFeature *plugin_provides(void);
103 #ifndef POPPLER_WITH_GDK
104 static void
105 copy_cairo_surface_to_pixbuf (cairo_surface_t *surface,
106 GdkPixbuf *pixbuf)
108 int cairo_width, cairo_height, cairo_rowstride;
109 unsigned char *pixbuf_data, *dst, *cairo_data;
110 int pixbuf_rowstride, pixbuf_n_channels;
111 unsigned int *src;
112 int x, y;
114 cairo_width = cairo_image_surface_get_width (surface);
115 cairo_height = cairo_image_surface_get_height (surface);
116 cairo_rowstride = cairo_image_surface_get_stride (surface);
117 cairo_data = cairo_image_surface_get_data (surface);
119 pixbuf_data = gdk_pixbuf_get_pixels (pixbuf);
120 pixbuf_rowstride = gdk_pixbuf_get_rowstride (pixbuf);
121 pixbuf_n_channels = gdk_pixbuf_get_n_channels (pixbuf);
123 if (cairo_width > gdk_pixbuf_get_width (pixbuf))
124 cairo_width = gdk_pixbuf_get_width (pixbuf);
125 if (cairo_height > gdk_pixbuf_get_height (pixbuf))
126 cairo_height = gdk_pixbuf_get_height (pixbuf);
127 for (y = 0; y < cairo_height; y++) {
128 src = (unsigned int *) (cairo_data + y * cairo_rowstride);
129 dst = pixbuf_data + y * pixbuf_rowstride;
130 for (x = 0; x < cairo_width; x++) {
131 dst[0] = (*src >> 16) & 0xff;
132 dst[1] = (*src >> 8) & 0xff;
133 dst[2] = (*src >> 0) & 0xff;
134 if (pixbuf_n_channels == 4)
135 dst[3] = (*src >> 24) & 0xff;
136 dst += pixbuf_n_channels;
137 src++;
141 static void
142 _poppler_page_render_to_pixbuf (PopplerPage *page,
143 int src_x, int src_y,
144 int src_width, int src_height,
145 double scale,
146 int rotation,
147 gboolean printing,
148 GdkPixbuf *pixbuf)
150 cairo_t *cr;
151 cairo_surface_t *surface;
153 surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
154 src_width, src_height);
155 cr = cairo_create (surface);
156 cairo_save (cr);
157 switch (rotation) {
158 case 90:
159 cairo_translate (cr, src_x + src_width, -src_y);
160 break;
161 case 180:
162 cairo_translate (cr, src_x + src_width, src_y + src_height);
163 break;
164 case 270:
165 cairo_translate (cr, -src_x, src_y + src_height);
166 break;
167 default:
168 cairo_translate (cr, -src_x, -src_y);
171 if (scale != 1.0)
172 cairo_scale (cr, scale, scale);
174 if (rotation != 0)
175 cairo_rotate (cr, rotation * G_PI / 180.0);
177 if (printing)
178 poppler_page_render_for_printing (page, cr);
179 else
180 poppler_page_render (page, cr);
181 cairo_restore (cr);
183 cairo_set_operator (cr, CAIRO_OPERATOR_DEST_OVER);
184 cairo_set_source_rgb (cr, 1., 1., 1.);
185 cairo_paint (cr);
187 cairo_destroy (cr);
189 copy_cairo_surface_to_pixbuf (surface, pixbuf);
190 cairo_surface_destroy (surface);
194 * poppler_page_render_to_pixbuf:
195 * @page: the page to render from
196 * @src_x: x coordinate of upper left corner
197 * @src_y: y coordinate of upper left corner
198 * @src_width: width of rectangle to render
199 * @src_height: height of rectangle to render
200 * @scale: scale specified as pixels per point
201 * @rotation: rotate the document by the specified degree
202 * @pixbuf: pixbuf to render into
204 * First scale the document to match the specified pixels per point,
205 * then render the rectangle given by the upper left corner at
206 * (src_x, src_y) and src_width and src_height.
207 * This function is for rendering a page that will be displayed.
208 * If you want to render a page that will be printed use
209 * poppler_page_render_to_pixbuf_for_printing() instead
211 * Deprecated: 0.16
213 static void
214 poppler_page_render_to_pixbuf (PopplerPage *page,
215 int src_x, int src_y,
216 int src_width, int src_height,
217 double scale,
218 int rotation,
219 GdkPixbuf *pixbuf)
221 g_return_if_fail (POPPLER_IS_PAGE (page));
222 g_return_if_fail (scale > 0.0);
223 g_return_if_fail (pixbuf != NULL);
225 _poppler_page_render_to_pixbuf (page, src_x, src_y,
226 src_width, src_height,
227 scale, rotation,
228 FALSE,
229 pixbuf);
231 #endif
232 static GtkWidget *pdf_viewer_get_widget(MimeViewer *_viewer)
234 PdfViewer *viewer = (PdfViewer *) _viewer;
235 debug_print("pdf_viewer_get_widget: %p\n", viewer->vbox);
237 return GTK_WIDGET(viewer->vbox);
239 /** Hide the index panel */
240 static void pdf_viewer_hide_index_pane(PdfViewer *viewer)
242 if (viewer->pdf_index) {
243 poppler_index_iter_free(viewer->pdf_index);
244 viewer->pdf_index = NULL;
245 gtk_widget_hide(GTK_WIDGET(viewer->frame_index));
249 static void search_matches_free(PdfViewer *viewer)
251 GList *cur;
252 for(cur = viewer->text_found; cur; cur = cur->next) {
253 PageResult *res = (PageResult *)cur->data;
254 g_list_free(res->results);
255 g_free(res);
257 g_list_free(viewer->text_found);
258 viewer->text_found = NULL;
259 g_free(viewer->last_search);
260 viewer->last_search = NULL;
261 if (viewer->last_rect && viewer->last_page_result) {
262 viewer->last_rect = NULL;
263 viewer->last_page_result = NULL;
264 pdf_viewer_update((MimeViewer *)viewer,
265 FALSE,
266 gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(viewer->cur_page)));
270 static void pdf_viewer_scroll_to(PdfViewer *viewer, gfloat x, gfloat y)
272 GtkAdjustment *vadj;
273 GtkAdjustment *hadj;
274 vadj = gtk_scrolled_window_get_vadjustment(
275 GTK_SCROLLED_WINDOW(viewer->scrollwin));
277 if (y < gtk_adjustment_get_value(vadj)) {
278 gtk_adjustment_set_value(vadj, y);
280 else {
281 while(y > gtk_adjustment_get_value(vadj) + gtk_adjustment_get_page_size(vadj)) {
282 gtk_adjustment_set_value(vadj,
283 gtk_adjustment_get_value(vadj) + gtk_adjustment_get_page_size(vadj));
287 hadj = gtk_scrolled_window_get_hadjustment(
288 GTK_SCROLLED_WINDOW(viewer->scrollwin));
290 if (x < gtk_adjustment_get_value(hadj)) {
291 gtk_adjustment_set_value(hadj, x);
293 else {
294 while(x > gtk_adjustment_get_value(hadj) + gtk_adjustment_get_page_size(hadj)) {
295 gtk_adjustment_set_value(hadj,
296 gtk_adjustment_get_value(hadj) + gtk_adjustment_get_page_size(hadj));
300 g_signal_emit_by_name(G_OBJECT(hadj), "value-changed", 0);
301 g_signal_emit_by_name(G_OBJECT(vadj), "value-changed", 0);
303 static void pdf_viewer_render_page(PopplerPage *page, GtkWidget *view, double width,
304 double height, double zoom, gint rotate)
306 GdkPixbuf *pb;
308 debug_print("width: %f\n", width);
309 pb = gdk_pixbuf_new(GDK_COLORSPACE_RGB,
310 FALSE, 8,
311 (int)(width * zoom),
312 (int)(height * zoom));
314 poppler_page_render_to_pixbuf(page, 0, 0,
315 (int)(width * zoom),
316 (int)(height * zoom),
317 zoom, rotate, pb);
319 gtk_image_set_from_pixbuf(GTK_IMAGE(view), pb);
320 g_object_unref(G_OBJECT(pb));
322 static void pdf_viewer_render_selection(PdfViewer *viewer, PopplerRectangle *rect, PageResult *page_results)
324 gint selw, selh;
325 double width_points, height_points;
326 gint width, height;
327 GdkPixbuf *sel_pb, *page_pb;
328 gfloat x1, x2, y1, y2;
331 gint cur_page_num =
332 gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(viewer->cur_page));
334 viewer->last_match = viewer->res_cnt;
336 viewer->last_rect = NULL;
337 viewer->last_page_result = NULL;
338 if (cur_page_num != page_results->page_num) {
339 /* we changed page. update the view */
340 gtk_spin_button_set_value(GTK_SPIN_BUTTON(viewer->cur_page),
341 (gdouble) page_results->page_num);
344 viewer->last_rect = rect;
345 viewer->last_page_result = page_results;
347 GTK_EVENTS_FLUSH();
349 poppler_page_get_size(POPPLER_PAGE(viewer->pdf_page), &width_points, &height_points);
350 width = (int)((width_points * viewer->zoom) + 0.5);
351 height = (int)((height_points * viewer->zoom) + 0.5);
353 if (viewer->rotate == 90) {
354 x1 = MIN(rect->y1,rect->y2) * viewer->zoom;
355 x2 = MAX(rect->y1,rect->y2) * viewer->zoom;
356 y1 = MAX(rect->x1,rect->x2) * viewer->zoom;
357 y2 = MIN(rect->x1,rect->x2) * viewer->zoom;
358 selw = (x2 - x1);
359 selh = (y1 - y2);
361 } else if (viewer->rotate == 180) {
362 x1 = width - rect->x2 * viewer->zoom;
363 x2 = width - rect->x1 * viewer->zoom;
364 y1 = height - rect->y2 * viewer->zoom;
365 y2 = height - rect->y1 * viewer->zoom;
366 selw = (x2 - x1);
367 selh = (y2 - y1);
368 y1 = height - y1;
369 y2 = height - y2;
371 } else if (viewer->rotate == 270) {
372 x1 = height - MAX(rect->y1,rect->y2) * viewer->zoom;
373 x2 = height - MIN(rect->y1,rect->y2) * viewer->zoom;
374 y1 = width - MIN(rect->x1,rect->x2) * viewer->zoom;
375 y2 = width - MAX(rect->x1,rect->x2) * viewer->zoom;
376 selw = (x2 - x1);
377 selh = (y1 - y2);
378 } else {
379 x1 = rect->x1 * viewer->zoom;
380 x2 = rect->x2 * viewer->zoom;
381 y1 = rect->y1 * viewer->zoom;
382 y2 = rect->y2 * viewer->zoom;
383 selw = (x2 - x1);
384 selh = (y2 - y1);
385 y1 = height - y1;
386 y2 = height - y2;
389 sel_pb = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8,
390 selw, selh);
392 gdk_pixbuf_fill(sel_pb, SELECTION_COLOR);
394 page_pb = gdk_pixbuf_new(GDK_COLORSPACE_RGB,
395 FALSE, 8,
396 (int)(viewer->width * viewer->zoom),
397 (int)(viewer->height * viewer->zoom));
399 poppler_page_render_to_pixbuf(viewer->pdf_page,
402 (int)(viewer->width * viewer->zoom),
403 (int)(viewer->height * viewer->zoom),
404 viewer->zoom,
405 viewer->rotate,
406 page_pb);
408 gdk_pixbuf_composite(sel_pb, page_pb,
409 x1, y2, selw, selh, 0, 0,
410 viewer->zoom, viewer->zoom,
411 GDK_INTERP_BILINEAR, ALPHA_CHANNEL);
413 gtk_image_set_from_pixbuf(GTK_IMAGE(viewer->pdf_view), page_pb);
415 pdf_viewer_scroll_to(viewer, MIN(x1,x2), MIN(y1,y2));
417 g_object_unref(G_OBJECT(sel_pb));
418 g_object_unref(G_OBJECT(page_pb));
422 static gboolean pdf_viewer_text_search(MimeViewer *_viewer, gboolean backward,
423 const gchar *str, gboolean case_sens)
425 PdfViewer *viewer = (PdfViewer *)_viewer;
426 GList *all_pages_results, *cur_page_results;
427 viewer->res_cnt = 0;
429 debug_print("pdf_viewer_text_search: %s\n", str);
430 main_window_cursor_wait(mainwindow_get_mainwindow());
432 if (viewer->last_search && strcmp(str, viewer->last_search)) {
433 search_matches_free(viewer);
434 viewer->last_match = -1;
435 viewer->num_matches = 0;
436 } else if (!viewer->last_search) {
437 viewer->last_match = -1;
438 viewer->num_matches = 0;
440 /* It's a new search, build list of matches
441 * across all pages */
442 if (viewer->last_match == -1) {
443 gint i;
445 for(i = 1; i <= viewer->num_pages; i++) {
447 PopplerPage *pdf_page = poppler_document_get_page(viewer->pdf_doc, i - 1);
448 viewer->page_results = poppler_page_find_text(pdf_page, str);
450 if (viewer->page_results != NULL) {
451 debug_print("page_results %p\n", viewer->page_results);
452 /* store results for this page */
453 guint num_res = 0;
454 PageResult *res = g_new0(PageResult, 1);
455 res->results = viewer->page_results;
456 res->page_num = i;
457 /* found text, prepend this page(faster than append) */
458 viewer->text_found = g_list_prepend(viewer->text_found, res);
459 num_res = g_list_length(viewer->page_results);
460 debug_print("%d results on page %d\n", num_res, i);
461 viewer->num_matches += num_res;
463 g_object_unref(G_OBJECT(pdf_page));
466 if (viewer->text_found == NULL) {
467 main_window_cursor_normal(mainwindow_get_mainwindow());
468 return FALSE;
470 /* put back the list in the correct order */
471 viewer->text_found = g_list_reverse(viewer->text_found);
474 if (!viewer->text_found) {
475 main_window_cursor_normal(mainwindow_get_mainwindow());
476 return FALSE;
477 } else {
478 viewer->last_search = g_strdup(str);
481 if (backward) {
482 /* if backward, we have to initialize stuff to search
483 * from the end */
484 viewer->res_cnt = viewer->num_matches-1;
485 if (viewer->last_match == -1) {
486 viewer->last_match = viewer->num_matches+1;
488 all_pages_results = g_list_last(viewer->text_found);
490 else {
491 all_pages_results = viewer->text_found;
494 for(; all_pages_results; all_pages_results = (backward?all_pages_results->prev:all_pages_results->next)) {
496 PageResult * page_results = (PageResult *)all_pages_results->data;
498 if (backward) {
499 cur_page_results = g_list_last(page_results->results);
501 else {
502 cur_page_results = page_results->results;
505 for(; cur_page_results; cur_page_results = (backward?cur_page_results->prev:cur_page_results->next)) {
507 gboolean valid = FALSE;
508 /* first valid result is the last+1 if searching
509 * forward, last-1 if searching backward */
510 if (backward) {
511 valid = (viewer->res_cnt < viewer->last_match);
513 else {
514 valid = (viewer->res_cnt > viewer->last_match);
516 if (valid) {
517 pdf_viewer_render_selection(viewer,
518 (PopplerRectangle *)cur_page_results->data,
519 page_results);
520 main_window_cursor_normal(mainwindow_get_mainwindow());
521 return TRUE;
524 if (backward) {
525 viewer->res_cnt--;
527 else {
528 viewer->res_cnt++;
532 main_window_cursor_normal(mainwindow_get_mainwindow());
533 search_matches_free(viewer);
534 return FALSE;
537 static void pdf_viewer_get_document_index(PdfViewer *viewer, PopplerIndexIter *index_iter, GtkTreeIter *parentiter)
539 PopplerAction *action;
540 PopplerIndexIter *child;
541 GtkTreeIter childiter;
543 debug_print("get document index\n");
544 do {
545 gint page_num = 0;
547 action = poppler_index_iter_get_action(index_iter);
549 if (action->type != POPPLER_ACTION_GOTO_DEST) {
550 poppler_action_free(action);
551 continue;
554 if (action->goto_dest.dest->type == POPPLER_DEST_XYZ || action->goto_dest.dest->type == POPPLER_DEST_FITH) {
555 page_num = action->goto_dest.dest->page_num;
557 #ifdef HAVE_POPPLER_DEST_NAMED
558 else if (action->goto_dest.dest->type == POPPLER_DEST_NAMED) {
559 PopplerDest *dest = poppler_document_find_dest(
560 viewer->pdf_doc, action->goto_dest.dest->named_dest);
561 if (dest->type != POPPLER_DEST_XYZ) {
562 g_warning("couldn't figure out link");
563 poppler_dest_free(dest);
564 continue;
566 page_num = dest->page_num;
567 poppler_dest_free(dest);
569 #endif
570 else {
571 #ifdef HAVE_POPPLER_DEST_NAMED
572 g_warning("unhandled link type %d. please contact developers", action->goto_dest.dest->type);
573 #else
574 g_warning("unhandled link type %d. please upgrade libpoppler-glib to 0.5.4", action->goto_dest.dest->type);
575 #endif
576 continue;
578 gtk_tree_store_append(GTK_TREE_STORE(viewer->index_model), &childiter, parentiter);
579 gtk_tree_store_set(GTK_TREE_STORE(viewer->index_model), &childiter,
580 INDEX_NAME, action->named.title,
581 INDEX_PAGE, page_num,
582 INDEX_TOP, action->goto_dest.dest->top,
583 -1);
584 poppler_action_free(action);
585 child = poppler_index_iter_get_child(index_iter);
586 if (child) {
587 pdf_viewer_get_document_index(viewer, child, &childiter);
588 poppler_index_iter_free(child);
591 while(poppler_index_iter_next(index_iter));
594 static void pdf_viewer_index_row_activated(GtkTreeView *tree_view,
595 GtkTreePath *path,
596 GtkTreeViewColumn *column,
597 gpointer data)
599 GtkTreeIter iter;
600 GtkTreeModel *model = gtk_tree_view_get_model(tree_view);
601 PdfViewer *viewer = (PdfViewer *)data;
602 gint page_num = 0;
604 debug_print("index_row_activated\n");
605 if (!gtk_tree_model_get_iter(model, &iter, path)) return;
607 gtk_tree_model_get(model, &iter,
608 INDEX_PAGE, &page_num,
609 -1);
611 if (page_num > 0) {
612 gtk_spin_button_set_value(GTK_SPIN_BUTTON(viewer->cur_page),(gdouble)page_num);
613 debug_print("Page num: %d\n", page_num);
615 GTK_EVENTS_FLUSH();
618 /** Disable the index button if the document doesn't have an index */
619 static void pdf_viewer_set_index_button_sensitive(PdfViewer *viewer)
621 viewer->pdf_index = poppler_index_iter_new(viewer->pdf_doc);
622 if (viewer->pdf_index) {
623 if (!gtk_widget_is_sensitive(viewer->doc_index)) {
624 gtk_widget_set_sensitive(viewer->doc_index, TRUE);
627 else {
628 gtk_widget_set_sensitive(viewer->doc_index, FALSE);
631 poppler_index_iter_free(viewer->pdf_index);
632 viewer->pdf_index = NULL;
635 static char * pdf_viewer_get_document_format_data(GDateTime *utime)
637 time_t time = (time_t) utime;
638 struct tm t;
639 char s[256];
640 const char *fmt_hack = "%c";
641 size_t len;
643 if (time == 0 || !localtime_r(&time, &t)) return NULL;
645 len = strftime(s, sizeof(s), fmt_hack, &t);
647 if (len == 0 || s[0] == '\0') return NULL;
649 return g_locale_to_utf8(s, -1, NULL, NULL, NULL);
652 #define ADD_TO_GRID(LABEL, VALUE) \
653 label = gtk_label_new(LABEL); \
654 gtk_label_set_xalign(GTK_LABEL(label), 1.0); \
655 gtk_widget_set_margin_start(GTK_WIDGET(label), 4); \
656 gtk_widget_set_margin_end(GTK_WIDGET(label), 0); \
657 gtk_grid_attach(GTK_GRID(viewer->table_doc_info), label, 0, row, 1, 1); \
659 label = gtk_label_new(VALUE); \
660 gtk_label_set_xalign(GTK_LABEL(label), 0.0); \
661 gtk_widget_set_margin_start(GTK_WIDGET(label), 4); \
662 gtk_widget_set_margin_end(GTK_WIDGET(label), 0); \
663 gtk_grid_attach(GTK_GRID(viewer->table_doc_info), label, 1, row, 1, 1); \
664 gtk_widget_set_hexpand(label, TRUE); \
665 gtk_widget_set_halign(label, GTK_ALIGN_FILL); \
666 row++;
669 static GtkGrid * pdf_viewer_fill_info_table(PdfViewer *viewer)
671 GtkWidget *label;
672 const gchar *title, *format, *author, *subject, *keywords, *creator, *producer;
673 gboolean linearized;
674 gchar *tmp;
675 gint row = 0;
677 GDateTime *creation_date, *mod_date;
679 PopplerPageLayout layout;
680 PopplerPageMode mode;
681 PopplerPermissions permissions;
682 PopplerViewerPreferences view_prefs;
684 title = format = author = subject = keywords = creator = producer = tmp = 0;
686 g_object_get(viewer->pdf_doc,
687 "title", &title,
688 "format", &format,
689 "author", &author,
690 "subject", &subject,
691 "keywords", &keywords,
692 "creation-date", &creation_date,
693 "permissions", &permissions,
694 "mod-date", &mod_date,
695 "creator", &creator,
696 "producer", &producer,
697 "linearized", &linearized,
698 "page-mode", &mode,
699 "page-layout", &layout,
700 "viewer-preferences", &view_prefs,
701 NULL);
703 viewer->table_doc_info = gtk_grid_new();
705 ADD_TO_GRID(_("Filename:"), viewer->target_filename)
706 ADD_TO_GRID(_("Size:"), to_human_readable(viewer->to_load->length))
707 ADD_TO_GRID(NULL, NULL)
708 ADD_TO_GRID(_("Title:"), title)
709 ADD_TO_GRID(_("Subject:"), subject)
710 ADD_TO_GRID(_("Author:"), author)
711 ADD_TO_GRID(_("Keywords:"), keywords)
712 ADD_TO_GRID(_("Creator:"), creator)
713 ADD_TO_GRID(_("Producer:"), producer)
715 tmp = pdf_viewer_get_document_format_data(creation_date);
716 ADD_TO_GRID(_("Created:"), tmp)
717 g_free(tmp);
719 tmp = pdf_viewer_get_document_format_data(mod_date);
720 ADD_TO_GRID(_("Modified:"), tmp)
721 g_free(tmp);
723 ADD_TO_GRID(_("Format:"), format)
724 if (linearized) {
725 ADD_TO_GRID(_("Optimized:"), _("Yes"))
727 else {
728 ADD_TO_GRID(_("Optimized:"), _("No"))
730 //ADD_TO_GRID(_("Page Mode:"), pdf_viewer_get_document_info_mode(mode))
731 //ADD_TO_GRID(_("Page Layout:"), pdf_viewer_get_document_info_layout(layout))
733 return(GtkGrid *) viewer->table_doc_info;
735 #undef ADD_TO_GRID
737 static FileType pdf_viewer_mimepart_get_type(MimeInfo *partinfo)
739 gchar *content_type = NULL;
740 FileType type = TYPE_UNKNOWN;
741 debug_print("mimepart_get_type\n");
742 if ((partinfo->type == MIMETYPE_APPLICATION) &&
743 (!g_ascii_strcasecmp(partinfo->subtype, "octet-stream"))) {
745 const gchar *filename;
747 filename = procmime_mimeinfo_get_parameter(partinfo, "filename");
749 if (filename == NULL)
750 filename = procmime_mimeinfo_get_parameter(partinfo, "name");
751 if (filename != NULL)
752 content_type = procmime_get_mime_type(filename);
754 else {
755 content_type = procmime_get_content_type_str(partinfo->type, partinfo->subtype);
758 if (content_type == NULL) type = TYPE_UNKNOWN;
759 else if (!strcmp(content_type, "application/pdf")) type = TYPE_PDF;
760 else if (!strcmp(content_type, "application/postscript")) type = TYPE_PS;
761 else if (!strcmp(content_type, "image/x-eps")) type = TYPE_EPS;
762 else type = TYPE_UNKNOWN;
764 g_free(content_type);
765 return type;
768 /* Callbacks */
769 static void pdf_viewer_button_first_page_cb(GtkButton *button, PdfViewer *viewer)
772 gtk_spin_button_spin(GTK_SPIN_BUTTON(viewer->cur_page), GTK_SPIN_HOME, 1);
775 static void pdf_viewer_button_prev_page_cb(GtkButton *button, PdfViewer *viewer)
778 gtk_spin_button_spin(GTK_SPIN_BUTTON(viewer->cur_page), GTK_SPIN_STEP_BACKWARD, 1);
781 static void pdf_viewer_button_next_page_cb(GtkButton *button, PdfViewer *viewer)
784 gtk_spin_button_spin(GTK_SPIN_BUTTON(viewer->cur_page), GTK_SPIN_STEP_FORWARD, 1);
787 static void pdf_viewer_button_last_page_cb(GtkButton *button, PdfViewer *viewer)
790 gtk_spin_button_spin(GTK_SPIN_BUTTON(viewer->cur_page), GTK_SPIN_END, 1);
793 static void pdf_viewer_spin_change_page_cb(GtkSpinButton *button, PdfViewer *viewer)
795 pdf_viewer_update((MimeViewer *)viewer,
796 FALSE,
797 gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(viewer->cur_page)));
800 static void pdf_viewer_button_zoom_in_cb(GtkButton *button, PdfViewer *viewer)
803 gtk_spin_button_spin(GTK_SPIN_BUTTON(viewer->zoom_scroll), GTK_SPIN_STEP_FORWARD, ZOOM_FACTOR);
806 static void pdf_viewer_spin_zoom_scroll_cb(GtkSpinButton *button, PdfViewer *viewer)
808 viewer->zoom = gtk_spin_button_get_value(GTK_SPIN_BUTTON(viewer->zoom_scroll));
809 pdf_viewer_update((MimeViewer *)viewer,
810 FALSE,
811 gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(viewer->cur_page)));
814 static void pdf_viewer_button_zoom_out_cb(GtkButton *button, PdfViewer *viewer)
817 gtk_spin_button_spin(GTK_SPIN_BUTTON(viewer->zoom_scroll), GTK_SPIN_STEP_BACKWARD, ZOOM_FACTOR);
821 static void pdf_viewer_button_press_events_cb(GtkWidget *widget, GdkEventButton *event, PdfViewer *viewer)
823 gchar *uri;
824 GdkWindow *gdkwin;
825 #ifdef HAVE_POPPLER_DEST_NAMED
826 PopplerDest *dest;
827 #endif
828 static GdkCursor *hand_cur = NULL;
830 if (!hand_cur) hand_cur = gdk_cursor_new_for_display(
831 gtk_widget_get_display(widget), GDK_FLEUR);
833 /* Execute Poppler Links */
834 if (event->button == 1 && viewer->in_link) {
835 switch (viewer->link_action->type) {
836 case POPPLER_ACTION_UNKNOWN:
837 debug_print("action unknown\n");
838 break;
839 case POPPLER_ACTION_GOTO_DEST:
840 if (viewer->link_action->goto_dest.dest->type == POPPLER_DEST_XYZ ||
841 viewer->link_action->goto_dest.dest->type == POPPLER_DEST_FITH) {
842 gtk_spin_button_set_value(GTK_SPIN_BUTTON(viewer->cur_page),
843 (gdouble)viewer->link_action->goto_dest.dest->page_num);
845 #ifdef HAVE_POPPLER_DEST_NAMED
846 else if (viewer->link_action->goto_dest.dest->type == POPPLER_DEST_NAMED) {
847 dest = poppler_document_find_dest(
848 viewer->pdf_doc, viewer->link_action->goto_dest.dest->named_dest);
849 if (dest->type != POPPLER_DEST_XYZ) {
850 g_warning("couldn't figure out link");
851 poppler_dest_free(dest);
852 break;
854 gtk_spin_button_set_value(GTK_SPIN_BUTTON(viewer->cur_page),
855 (gdouble)dest->page_num);
856 poppler_dest_free(dest);
858 #endif
859 break;
860 case POPPLER_ACTION_GOTO_REMOTE:
861 #ifdef HAVE_POPPLER_DEST_NAMED
862 dest = poppler_document_find_dest(
863 viewer->pdf_doc, viewer->link_action->goto_remote.dest->named_dest);
864 if (dest->type != POPPLER_DEST_XYZ) {
865 g_warning("couldn't figure out link");
866 poppler_dest_free(dest);
867 break;
869 gtk_spin_button_set_value(GTK_SPIN_BUTTON(viewer->cur_page),
870 (gdouble)dest->page_num);
871 poppler_dest_free(dest);
872 #endif
873 break;
874 case POPPLER_ACTION_LAUNCH:
875 debug_print("action launch not yet implemented\n");
876 break;
877 case POPPLER_ACTION_URI:
878 uri = g_strdup(viewer->link_action->uri.uri);
879 if (!g_ascii_strncasecmp(uri, "mailto:", 7))
880 compose_new(NULL, uri + 7, NULL);
881 else
882 open_uri(uri, prefs_common_get_uri_cmd());
883 g_free(uri);
884 break;
885 case POPPLER_ACTION_NAMED:
886 debug_print("action named not yet implemented\n");
887 break;
888 case POPPLER_ACTION_NONE:
889 debug_print("action none does nothing, surprise!\n");
890 break;
891 case POPPLER_ACTION_MOVIE:
892 debug_print("yoyoyo ;-) a movie?\n");
893 break;
894 #if POPPLER_CHECK_VERSION(0,14,0)
895 case POPPLER_ACTION_RENDITION:
896 debug_print("yoyoyo ;-) multimedia?\n");
897 break;
898 case POPPLER_ACTION_OCG_STATE:
899 debug_print("yoyoyo ;-) layer state?\n");
900 break;
901 #if POPPLER_CHECK_VERSION(0,18,0)
902 case POPPLER_ACTION_JAVASCRIPT:
903 debug_print("yoyoyo ;-) javascript?\n");
904 break;
905 #if POPPLER_CHECK_VERSION(0,90,0)
906 case POPPLER_ACTION_RESET_FORM:
907 debug_print("yoyoyo ;-) reset form?\n");
908 break;
909 #endif /* 0.90 */
910 #endif /* 0.18 */
911 #endif /* 0.14 */
913 if (((MimeViewer *)viewer)->mimeview &&
914 ((MimeViewer *)viewer)->mimeview->messageview &&
915 ((MimeViewer *)viewer)->mimeview->messageview->window &&
916 (gdkwin = gtk_widget_get_window(((MimeViewer *)viewer)->mimeview->messageview->window)) != NULL)
917 gdk_window_set_cursor (gdkwin, NULL);
918 else
919 gdk_window_set_cursor (gtk_widget_get_window(mainwindow_get_mainwindow()->window), NULL);
922 /* Init document to be scrolled with left mouse click */
923 if (event->button == 1 && !viewer->in_link) {
924 viewer->pdf_view_scroll = TRUE;
925 if (((MimeViewer *)viewer)->mimeview &&
926 ((MimeViewer *)viewer)->mimeview->messageview &&
927 ((MimeViewer *)viewer)->mimeview->messageview->window &&
928 (gdkwin = gtk_widget_get_window(((MimeViewer *)viewer)->mimeview->messageview->window)) != NULL)
929 gdk_window_set_cursor (gdkwin, hand_cur);
930 else
931 gdk_window_set_cursor (gtk_widget_get_window(mainwindow_get_mainwindow()->window), hand_cur);
933 viewer->last_x = event->x;
934 viewer->last_y = event->y;
935 viewer->last_dir_x = 0;
936 viewer->last_dir_y = 0;
939 /* Set the normal cursor*/
940 static void pdf_viewer_mouse_scroll_destroy_cb(GtkWidget *widget, GdkEventButton *event, PdfViewer *viewer)
942 GdkWindow *gdkwin;
944 if (event->button == 1) {
945 viewer->pdf_view_scroll = FALSE;
946 if (((MimeViewer *)viewer)->mimeview &&
947 ((MimeViewer *)viewer)->mimeview->messageview &&
948 ((MimeViewer *)viewer)->mimeview->messageview->window &&
949 (gdkwin = gtk_widget_get_window(((MimeViewer *)viewer)->mimeview->messageview->window)) != NULL)
950 gdk_window_set_cursor (gdkwin, NULL);
951 else
952 gdk_window_set_cursor (gtk_widget_get_window(mainwindow_get_mainwindow()->window), NULL);
956 static void pdf_viewer_move_events_cb(GtkWidget *widget, GdkEventMotion *event, PdfViewer *viewer)
958 GdkWindow *gdkwin;
960 /* Grab the document and scroll it with mouse */
961 if (viewer->pdf_view_scroll) {
963 viewer->pdf_view_vadj = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(viewer->scrollwin));
964 viewer->pdf_view_hadj = gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(viewer->scrollwin));
966 if (event->x < viewer->last_x
967 && gtk_adjustment_get_value(viewer->pdf_view_hadj) < (gtk_adjustment_get_upper(viewer->pdf_view_hadj) - gtk_adjustment_get_page_size(viewer->pdf_view_hadj))) {
968 if (viewer->last_dir_x == -1) {
969 gtk_adjustment_set_value(viewer->pdf_view_hadj,
970 gtk_adjustment_get_value(viewer->pdf_view_hadj)
971 + viewer->last_x
972 - event->x);
973 g_signal_emit_by_name(G_OBJECT(viewer->pdf_view_hadj),
974 "value_changed", 0);
976 viewer->last_dir_x = -1;
978 else if (event->x > viewer->last_x
979 && gtk_adjustment_get_value(viewer->pdf_view_hadj) > 0.0) {
980 if (viewer->last_dir_x == +1) {
981 gtk_adjustment_set_value(viewer->pdf_view_hadj,
982 gtk_adjustment_get_value(viewer->pdf_view_hadj)
983 + viewer->last_x
984 - event->x);
985 g_signal_emit_by_name(G_OBJECT(viewer->pdf_view_hadj),
986 "value_changed", 0);
988 viewer->last_dir_x = +1;
991 if (event->y < viewer->last_y
992 && gtk_adjustment_get_value(viewer->pdf_view_vadj) < (gtk_adjustment_get_upper(viewer->pdf_view_vadj) - gtk_adjustment_get_page_size(viewer->pdf_view_vadj))) {
993 if (viewer->last_dir_y == -1) {
994 gtk_adjustment_set_value(viewer->pdf_view_vadj,
995 gtk_adjustment_get_value(viewer->pdf_view_vadj)
996 + viewer->last_y
997 - event->y);
998 g_signal_emit_by_name(G_OBJECT(viewer->pdf_view_vadj),
999 "value_changed", 0);
1001 viewer->last_dir_y = -1;
1003 else if (event->y > viewer->last_y
1004 && gtk_adjustment_get_value(viewer->pdf_view_vadj) > 0.0) {
1005 if (viewer->last_dir_y == +1) {
1006 gtk_adjustment_set_value(viewer->pdf_view_vadj,
1007 gtk_adjustment_get_value(viewer->pdf_view_vadj)
1008 + viewer->last_y
1009 - event->y);
1010 g_signal_emit_by_name(G_OBJECT(viewer->pdf_view_vadj),
1011 "value_changed", 0);
1013 viewer->last_dir_y = +1;
1015 viewer->last_x = event->x;
1016 viewer->last_y = event->y;
1017 GTK_EVENTS_FLUSH();
1019 else {
1020 /* Link Mapping */
1021 static GList *l;
1022 static GdkCursor *link_cur = NULL;
1023 static GtkRequisition size;
1024 static gdouble x,y, x1, y1, x2, y2;
1025 gboolean ccur;
1027 viewer->pdf_view_vadj = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(viewer->scrollwin));
1028 viewer->pdf_view_hadj = gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(viewer->scrollwin));
1030 if (!link_cur) link_cur = gdk_cursor_new_for_display(
1031 gtk_widget_get_display(viewer->scrollwin), GDK_HAND2);
1033 ccur = FALSE;
1034 viewer->in_link = FALSE;
1035 for (l = viewer->link_map; l; l = g_list_next (l)) {
1036 gint upper;
1037 PopplerLinkMapping *lmapping;
1038 lmapping = (PopplerLinkMapping *)l->data;
1040 x1 = lmapping->area.x1;
1041 y1 = lmapping->area.y1;
1042 x2 = lmapping->area.x2;
1043 y2 = lmapping->area.y2;
1044 gtk_widget_get_preferred_size(viewer->pdf_view, &size, NULL);
1046 upper = gtk_adjustment_get_upper(viewer->pdf_view_hadj);
1047 switch (viewer->rotate) {
1048 case 0:
1049 case 360:
1050 if (size.width != upper)
1051 x = (event->x - (upper - size.width) / 2) / viewer->zoom;
1052 else
1053 x = event->x / viewer->zoom;
1055 y = (upper - event->y) / viewer->zoom;
1056 break;
1057 case 90:
1058 if (size.width != upper)
1059 y = (event->x - (upper - size.width) / 2) / viewer->zoom;
1060 else
1061 y = event->x / viewer->zoom;
1063 x = (event->y) / viewer->zoom;
1064 break;
1065 case 180:
1066 if (size.width != upper)
1067 x = ((upper - event->x) - ((upper - size.width) / 2)) / viewer->zoom;
1068 else
1069 x = ((upper - event->x) - (upper - size.width)) / viewer->zoom;
1071 y = (event->y) / viewer->zoom;
1072 break;
1073 case 270:
1074 if (size.width != upper)
1075 y = ((upper - event->x) - ((upper - size.width) / 2)) / viewer->zoom;
1076 else
1077 y = ((upper - event->x) - (upper - size.width)) / viewer->zoom;
1079 x = (upper - event->y) / viewer->zoom;
1080 break;
1083 if ( (x > x1 && x < x2) && (y > y1 && y < y2) ) {
1084 viewer->in_link = TRUE;
1085 if (((MimeViewer *)viewer)->mimeview &&
1086 ((MimeViewer *)viewer)->mimeview->messageview &&
1087 ((MimeViewer *)viewer)->mimeview->messageview->window &&
1088 (gdkwin = gtk_widget_get_window(((MimeViewer *)viewer)->mimeview->messageview->window)) != NULL)
1089 gdk_window_set_cursor (gdkwin, link_cur);
1090 else
1091 gdk_window_set_cursor (gtk_widget_get_window(mainwindow_get_mainwindow()->window), link_cur);
1093 viewer->link_action = lmapping->action;
1094 ccur = TRUE;
1096 if (!ccur) {
1097 if (((MimeViewer *)viewer)->mimeview &&
1098 ((MimeViewer *)viewer)->mimeview->messageview &&
1099 ((MimeViewer *)viewer)->mimeview->messageview->window &&
1100 (gdkwin = gtk_widget_get_window(((MimeViewer *)viewer)->mimeview->messageview->window)) != NULL)
1101 gdk_window_set_cursor (gdkwin, NULL);
1102 else
1103 gdk_window_set_cursor (gtk_widget_get_window(mainwindow_get_mainwindow()->window), NULL);
1106 g_free(l);
1109 static gboolean pdf_viewer_scroll_cb(GtkWidget *widget, GdkEventScroll *event,
1110 PdfViewer *viewer)
1112 GtkAdjustment *adj = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(viewer->scrollwin));
1113 static gboolean in_scroll_cb = FALSE;
1114 gboolean handled = FALSE;
1115 gdouble delta_x = 0.0, delta_y = 0.0;
1116 gint cur_p = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(viewer->cur_page));
1118 if (in_scroll_cb)
1119 return FALSE;
1121 in_scroll_cb = TRUE;
1123 if ((event->state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK) {
1124 if (event->direction == GDK_SCROLL_UP) {
1125 pdf_viewer_button_zoom_in_cb(NULL, viewer);
1126 } else {
1127 pdf_viewer_button_zoom_out_cb(NULL, viewer);
1129 in_scroll_cb = FALSE;
1130 return TRUE;
1133 if (event->direction == GDK_SCROLL_SMOOTH) {
1134 gdk_event_get_scroll_deltas((GdkEvent *)event, &delta_x, &delta_y);
1137 if ((delta_y < 0.0 || event->direction == GDK_SCROLL_UP) &&
1138 gtk_adjustment_get_value(adj) == gtk_adjustment_get_lower(adj) &&
1139 cur_p > 1) {
1140 gtk_spin_button_spin(GTK_SPIN_BUTTON(viewer->cur_page), GTK_SPIN_STEP_BACKWARD, 1);
1141 gtk_adjustment_set_value(adj,
1142 gtk_adjustment_get_upper(adj) - gtk_adjustment_get_page_size(adj));
1143 handled = TRUE;
1144 } else if ((delta_y > 0.0 || event->direction == GDK_SCROLL_DOWN) &&
1145 gtk_adjustment_get_value(adj) + gtk_adjustment_get_page_size(adj) == gtk_adjustment_get_upper(adj) &&
1146 cur_p < viewer->num_pages) {
1147 gtk_spin_button_spin(GTK_SPIN_BUTTON(viewer->cur_page), GTK_SPIN_STEP_FORWARD, 1);
1148 gtk_adjustment_set_value(adj, 0.0);
1149 handled = TRUE;
1151 in_scroll_cb = FALSE;
1152 return handled;
1155 static void pdf_viewer_button_zoom_fit_cb(GtkButton *button, PdfViewer *viewer)
1157 GtkAllocation allocation;
1158 double xratio, yratio;
1159 gtk_widget_get_allocation(viewer->scrollwin, &allocation);
1160 debug_print("width: %d\n", allocation.width);
1161 debug_print("height: %d\n", allocation.height);
1162 xratio = allocation.width / viewer->width;
1163 yratio = allocation.height / viewer->height;
1165 if (xratio >= yratio) {
1166 viewer->zoom = yratio;
1167 gtk_spin_button_set_value(GTK_SPIN_BUTTON(viewer->zoom_scroll),viewer->zoom);
1169 else {
1170 viewer->zoom = xratio;
1171 gtk_spin_button_set_value(GTK_SPIN_BUTTON(viewer->zoom_scroll),viewer->zoom);
1175 static void pdf_viewer_button_zoom_width_cb(GtkButton *button, PdfViewer *viewer)
1177 GtkAllocation allocation;
1178 double xratio;
1179 gtk_widget_get_allocation(viewer->scrollwin, &allocation);
1180 debug_print("width: %d\n", allocation.width);
1181 xratio = allocation.width / viewer->width;
1182 gtk_spin_button_set_value(GTK_SPIN_BUTTON(viewer->zoom_scroll), xratio);
1185 static void pdf_viewer_button_rotate_right_cb(GtkButton *button, PdfViewer *viewer)
1187 if (viewer->rotate == 360) {
1188 viewer->rotate = 0;
1191 viewer->rotate += (gint) ROTATION;
1192 pdf_viewer_update((MimeViewer *)viewer, FALSE,
1193 gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(viewer->cur_page)));
1196 static void pdf_viewer_button_rotate_left_cb(GtkButton *button, PdfViewer *viewer)
1198 if (viewer->rotate == 0) {
1199 viewer->rotate = 360;
1202 viewer->rotate = abs(viewer->rotate -(gint) ROTATION);
1203 pdf_viewer_update((MimeViewer *)viewer, FALSE,
1204 gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(viewer->cur_page)));
1207 /* Show/Hide the index pane */
1208 static void pdf_viewer_show_document_index_cb(GtkToggleButton *button, PdfViewer *viewer)
1210 if (!viewer->pdf_index) {
1211 viewer->pdf_index = poppler_index_iter_new(viewer->pdf_doc);
1214 gtk_tree_store_clear(GTK_TREE_STORE(viewer->index_model));
1216 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(viewer->doc_index))) {
1217 pdf_viewer_get_document_index(viewer,(PopplerIndexIter *) viewer->pdf_index, NULL);
1218 gtk_widget_show(GTK_WIDGET(viewer->frame_index));
1220 else {
1221 pdf_viewer_hide_index_pane(viewer);
1226 static void pdf_viewer_button_print_cb(GtkButton *button, PdfViewer *viewer)
1228 MainWindow *mainwin = mainwindow_get_mainwindow();
1229 summary_print(mainwin->summaryview);
1232 static void pdf_viewer_button_document_info_cb(GtkButton *button, PdfViewer *viewer)
1234 alertpanel_full(_("PDF properties"), NULL, "window-close", _("_Close"), NULL, NULL,
1235 NULL, NULL, ALERTFOCUS_FIRST, FALSE,
1236 GTK_WIDGET(pdf_viewer_fill_info_table(viewer)),
1237 ALERT_NOTICE);
1241 static const char * poppler_get_document_info_mode(PopplerPageMode mode)
1243 GEnumValue *enum_value;
1245 enum_value = g_enum_get_value((GEnumClass *) g_type_class_peek(POPPLER_TYPE_PAGE_MODE), mode);
1246 return(gchar *) enum_value->value_name;
1248 static const char * poppler_get_document_info_layout(PopplerPageLayout layout)
1251 GEnumValue *enum_value;
1253 enum_value = g_enum_get_value((GEnumClass *) g_type_class_peek(POPPLER_TYPE_PAGE_LAYOUT), layout);
1254 return(gchar *) enum_value->value_name;
1257 static void pdf_viewer_show_controls(PdfViewer *viewer, gboolean show)
1259 if (show) {
1260 gtk_widget_show(viewer->first_page);
1261 gtk_widget_show(viewer->cur_page);
1262 gtk_widget_show(viewer->prev_page);
1263 gtk_widget_show(viewer->next_page);
1264 gtk_widget_show(viewer->last_page);
1265 gtk_widget_show(viewer->zoom_in);
1266 gtk_widget_show(viewer->zoom_out);
1267 gtk_widget_show(viewer->zoom_fit);
1268 gtk_widget_show(viewer->zoom_width);
1269 gtk_widget_show(viewer->zoom_scroll);
1270 gtk_widget_show(viewer->widgets_table);
1271 gtk_widget_show(viewer->rotate_right);
1272 gtk_widget_show(viewer->rotate_left);
1273 gtk_widget_show(viewer->print);
1274 gtk_widget_show(viewer->doc_info);
1275 gtk_widget_show(viewer->doc_index);
1276 } else {
1277 gtk_widget_hide(viewer->first_page);
1278 gtk_widget_hide(viewer->cur_page);
1279 gtk_widget_hide(viewer->prev_page);
1280 gtk_widget_hide(viewer->next_page);
1281 gtk_widget_hide(viewer->last_page);
1282 gtk_widget_hide(viewer->zoom_in);
1283 gtk_widget_hide(viewer->zoom_out);
1284 gtk_widget_hide(viewer->zoom_fit);
1285 gtk_widget_hide(viewer->zoom_width);
1286 gtk_widget_hide(viewer->zoom_scroll);
1287 gtk_widget_hide(viewer->widgets_table);
1288 gtk_widget_hide(viewer->rotate_right);
1289 gtk_widget_hide(viewer->rotate_left);
1290 gtk_widget_hide(viewer->print);
1291 gtk_widget_hide(viewer->doc_info);
1292 gtk_widget_hide(viewer->doc_index);
1295 /** Render the current page, page_num on the viewer */
1296 static void pdf_viewer_update(MimeViewer *_viewer, gboolean reload_file, int page_num)
1299 PdfViewer *viewer = (PdfViewer *) _viewer;
1300 GError *error = NULL;
1301 gchar *tmpfile = NULL;
1302 gchar *tmp;
1303 gchar *password = NULL;
1305 debug_print("pdf_viewer_update\n");
1307 if (reload_file) {
1308 if (viewer->pdf_doc) {
1309 g_object_unref(G_OBJECT(viewer->pdf_doc));
1310 viewer->pdf_doc = NULL;
1313 if (pdf_viewer_mimepart_get_type(viewer->to_load) == TYPE_PS) {
1314 stock_pixbuf_gdk(STOCK_PIXMAP_MIME_PS,
1315 &viewer->icon_pixbuf);
1316 gtk_image_set_from_pixbuf(GTK_IMAGE(viewer->icon_type),
1317 viewer->icon_pixbuf);
1319 else if (pdf_viewer_mimepart_get_type(viewer->to_load) == TYPE_EPS) {
1320 stock_pixbuf_gdk(STOCK_PIXMAP_MIME_IMAGE,
1321 &viewer->icon_pixbuf);
1322 gtk_image_set_from_pixbuf(GTK_IMAGE(viewer->icon_type),
1323 viewer->icon_pixbuf);
1325 else if (pdf_viewer_mimepart_get_type(viewer->to_load) == TYPE_PDF) {
1326 stock_pixbuf_gdk(STOCK_PIXMAP_MIME_PDF,
1327 &viewer->icon_pixbuf);
1328 gtk_image_set_from_pixbuf(GTK_IMAGE(viewer->icon_type),
1329 viewer->icon_pixbuf);
1331 else {
1332 stock_pixbuf_gdk(STOCK_PIXMAP_MIME_APPLICATION,
1333 &viewer->icon_pixbuf);
1334 gtk_image_set_from_pixbuf(GTK_IMAGE(viewer->icon_type),
1335 viewer->icon_pixbuf);
1338 gtk_label_set_text(GTK_LABEL(viewer->doc_label), _("Loading..."));
1339 pdf_viewer_show_controls(viewer, FALSE);
1340 main_window_cursor_wait(mainwindow_get_mainwindow());
1342 GTK_EVENTS_FLUSH();
1344 if (pdf_viewer_mimepart_get_type(viewer->to_load) == TYPE_PS ||
1345 pdf_viewer_mimepart_get_type(viewer->to_load) == TYPE_EPS) {
1346 gchar *cmdline = NULL, *tmp = NULL, *gspath = NULL;
1347 gint result = 0;
1349 if ((gspath = g_find_program_in_path("gs")) != NULL) {
1350 g_free(gspath);
1351 /* convert postscript to pdf */
1352 tmpfile = get_tmp_file();
1353 cmdline = g_strdup_printf(
1354 "gs -dSAFER -dCompatibilityLevel=1.2 -q -dNOPAUSE -dBATCH "
1355 "-sDEVICE=pdfwrite -sOutputFile=%s -f \"%s\"",
1356 tmpfile, viewer->filename);
1357 result = execute_command_line(cmdline, FALSE, NULL);
1358 if (result == 0) {
1359 tmp = g_filename_to_uri(tmpfile, NULL, NULL);
1360 viewer->pdf_doc = poppler_document_new_from_file( tmp, NULL, &error);
1361 g_free(tmp);
1363 else {
1364 g_warning("gs conversion failed: %s returned %d", cmdline, result);
1365 tmp = g_strdup_printf("gs: err %d", result);
1366 alertpanel_warning("%s", tmp);
1367 g_free(tmp);
1370 g_free(cmdline);
1371 claws_unlink(tmpfile);
1372 g_free(tmpfile);
1374 else {
1375 g_warning("gs conversion disabled: gs binary was not found");
1376 alertpanel_warning("PostScript view disabled: required gs program not found");
1377 result = 1;
1380 if (result != 0) {
1381 main_window_cursor_normal(mainwindow_get_mainwindow());
1382 return;
1385 else {
1386 viewer->pdf_doc = poppler_document_new_from_file( viewer->fsname, NULL, &error);
1388 if (error && g_error_matches(error, POPPLER_ERROR, POPPLER_ERROR_ENCRYPTED)) {
1389 g_clear_error(&error);
1390 password = input_dialog_with_invisible(_("Enter password"),
1391 _("This document is locked and requires a password before it can be opened."),
1392 "");
1393 viewer->pdf_doc = poppler_document_new_from_file(viewer->fsname, password, &error);
1394 g_free(password);
1397 viewer->num_pages = poppler_document_get_n_pages(viewer->pdf_doc);
1399 g_signal_handlers_block_by_func(G_OBJECT(viewer->cur_page), pdf_viewer_spin_change_page_cb,(gpointer *)viewer);
1400 gtk_spin_button_set_range(GTK_SPIN_BUTTON(viewer->cur_page),
1402 (gdouble)viewer->num_pages );
1404 g_signal_handlers_unblock_by_func(G_OBJECT(viewer->cur_page), pdf_viewer_spin_change_page_cb,(gpointer *)viewer);
1405 gtk_spin_button_spin(GTK_SPIN_BUTTON(viewer->cur_page), GTK_SPIN_HOME, 1);
1406 tmp = g_strdup_printf(_("%s Document"),pdf_viewer_mimepart_get_type(viewer->to_load) == TYPE_PDF ? "PDF":"Postscript");
1407 CLAWS_SET_TIP(
1408 GTK_WIDGET(viewer->icon_type_ebox),
1409 tmp);
1410 g_free(tmp);
1412 tmp = g_strdup_printf(_("of %d"), viewer->num_pages);
1413 gtk_label_set_text(GTK_LABEL(viewer->doc_label), tmp);
1414 g_free(tmp);
1416 pdf_viewer_show_controls(viewer, TRUE);
1417 main_window_cursor_normal(mainwindow_get_mainwindow());
1419 if (viewer->pdf_doc == NULL) {
1420 stock_pixbuf_gdk(STOCK_PIXMAP_MIME_APPLICATION,
1421 &viewer->icon_pixbuf);
1423 gtk_image_set_from_pixbuf(GTK_IMAGE(viewer->icon_type), viewer->icon_pixbuf);
1424 if (error) {
1425 strretchomp(error->message);
1426 alertpanel_error("%s", error->message);
1427 } else {
1428 alertpanel_error(_("PDF rendering failed for an unknown reason."));
1430 pdf_viewer_show_controls(viewer, FALSE);
1431 g_error_free(error);
1432 return;
1435 if (page_num == 1) {
1436 gtk_widget_set_sensitive(viewer->first_page, FALSE);
1437 gtk_widget_set_sensitive(viewer->prev_page, FALSE);
1439 else {
1440 gtk_widget_set_sensitive(viewer->first_page, TRUE);
1441 gtk_widget_set_sensitive(viewer->prev_page, TRUE);
1444 if (page_num == viewer->num_pages) {
1445 gtk_widget_set_sensitive(viewer->last_page, FALSE);
1446 gtk_widget_set_sensitive(viewer->next_page, FALSE);
1448 else {
1449 gtk_widget_set_sensitive(viewer->last_page, TRUE);
1450 gtk_widget_set_sensitive(viewer->next_page, TRUE);
1453 /* check for the index if exists */
1454 pdf_viewer_set_index_button_sensitive((PdfViewer *) viewer);
1456 if (page_num > 0 && page_num <= viewer->num_pages) {
1458 GTK_EVENTS_FLUSH();
1460 if (viewer->pdf_page) {
1461 g_object_unref(G_OBJECT(viewer->pdf_page));
1464 viewer->pdf_page = poppler_document_get_page(viewer->pdf_doc, page_num - 1);
1466 if (viewer->pdf_page == NULL) {
1467 g_warning("page not found");
1468 return;
1471 if (viewer->rotate == 90 || viewer->rotate == 270) {
1472 poppler_page_get_size(viewer->pdf_page, &viewer->height, &viewer->width);
1474 else {
1475 poppler_page_get_size(viewer->pdf_page, &viewer->width, &viewer->height);
1478 if (viewer->last_rect && viewer->last_page_result &&
1479 viewer->last_page_result->page_num == page_num) {
1480 pdf_viewer_render_selection(viewer, viewer->last_rect, viewer->last_page_result);
1482 else {
1483 pdf_viewer_render_page(viewer->pdf_page, viewer->pdf_view, viewer->width,
1484 viewer->height, viewer->zoom, viewer->rotate);
1488 /* Get Links Mapping */
1489 if (viewer->link_map) {
1490 poppler_page_free_link_mapping(viewer->link_map);
1492 viewer->link_map = poppler_page_get_link_mapping(viewer->pdf_page);
1498 static void pdf_viewer_show_mimepart(MimeViewer *_viewer, const gchar *infile,
1499 MimeInfo *partinfo)
1501 PdfViewer *viewer = (PdfViewer *) _viewer;
1502 gchar buf[4096];
1503 const gchar *charset = NULL;
1504 MessageView *messageview = ((MimeViewer *)viewer)->mimeview
1505 ?((MimeViewer *)viewer)->mimeview->messageview
1506 : NULL;
1508 viewer->rotate = 0;
1509 viewer->to_load = partinfo;
1511 if (messageview)
1512 messageview->updating = TRUE;
1514 memset(buf, 0, sizeof(buf));
1515 debug_print("pdf_viewer_show_mimepart\n");
1517 if (viewer->filename != NULL) {
1518 claws_unlink(viewer->filename);
1519 g_free(viewer->filename);
1520 viewer->filename = NULL;
1523 viewer->mimeinfo = NULL;
1525 if (partinfo) {
1526 viewer->target_filename = procmime_get_part_file_name(partinfo);
1527 viewer->filename = procmime_get_tmp_file_name(partinfo);
1528 viewer->fsname = g_filename_to_uri(viewer->filename, NULL, NULL);
1531 if (partinfo && !(procmime_get_part(viewer->filename, partinfo) < 0)) {
1533 if (messageview && messageview->forced_charset)
1534 charset = _viewer->mimeview->messageview->forced_charset;
1535 else
1536 charset = procmime_mimeinfo_get_parameter(partinfo, "charset");
1538 if (charset == NULL) {
1539 charset = conv_get_locale_charset_str();
1542 debug_print("using charset %s\n", charset);
1544 viewer->mimeinfo = partinfo;
1547 pdf_viewer_update((MimeViewer *)viewer, TRUE, 1);
1549 if (messageview)
1550 messageview->updating = FALSE;
1553 static void pdf_viewer_clear(MimeViewer *_viewer)
1555 PdfViewer *viewer = (PdfViewer *) _viewer;
1556 GtkAdjustment *vadj;
1558 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(viewer->doc_index), FALSE);
1559 gtk_widget_hide(viewer->frame_index);
1561 debug_print("pdf_viewer_clear\n");
1562 viewer->to_load = NULL;
1564 if (viewer->pdf_doc) {
1565 g_object_unref(G_OBJECT(viewer->pdf_doc));
1566 viewer->pdf_doc = NULL;
1569 vadj = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(viewer->scrollwin));
1570 gtk_adjustment_set_value(vadj, 0.0);
1571 g_signal_emit_by_name(G_OBJECT(vadj), "value-changed", 0);
1572 vadj = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(viewer->scrollwin_index));
1573 gtk_adjustment_set_value(vadj, 0.0);
1574 g_signal_emit_by_name(G_OBJECT(vadj), "value-changed", 0);
1575 gtk_tree_store_clear(GTK_TREE_STORE(viewer->index_model));
1576 gtk_image_set_from_pixbuf(GTK_IMAGE(viewer->pdf_view), NULL);
1579 static void pdf_viewer_destroy(MimeViewer *_viewer)
1581 PdfViewer *viewer = (PdfViewer *) _viewer;
1583 debug_print("pdf_viewer_destroy\n");
1585 if (viewer->pdf_index) poppler_index_iter_free(viewer->pdf_index);
1587 poppler_page_free_link_mapping (viewer->link_map);
1588 g_object_unref(GTK_WIDGET(viewer->vbox));
1589 g_object_unref(GTK_WIDGET(viewer->pdf_view));
1590 g_object_unref(GTK_WIDGET(viewer->doc_index_pane));
1591 g_object_unref(GTK_WIDGET(viewer->scrollwin));
1592 g_object_unref(GTK_WIDGET(viewer->scrollwin_index));
1593 claws_unlink(viewer->filename);
1594 g_free(viewer->filename);
1595 g_free(viewer);
1598 static gboolean pdf_viewer_scroll_page(MimeViewer *_viewer, gboolean up)
1600 PdfViewer *viewer = (PdfViewer *)_viewer;
1601 GtkAdjustment *vadj = gtk_scrolled_window_get_vadjustment(
1602 GTK_SCROLLED_WINDOW(viewer->scrollwin));
1604 gint cur_p = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(viewer->cur_page));
1606 if (viewer->pdf_view == NULL) return FALSE;
1608 if (!gtkutils_scroll_page(GTK_WIDGET(viewer->pdf_view), vadj, up)) {
1609 if (!up && cur_p != viewer->num_pages) {
1610 gtk_spin_button_spin(GTK_SPIN_BUTTON(viewer->cur_page), GTK_SPIN_STEP_FORWARD, 1);
1611 vadj = gtk_scrolled_window_get_vadjustment(
1612 GTK_SCROLLED_WINDOW(viewer->scrollwin));
1613 gtk_adjustment_set_value(vadj, 0.0);
1614 g_signal_emit_by_name(G_OBJECT(vadj), "value-changed", 0);
1615 return TRUE;
1617 else if (up && cur_p != 1) {
1618 gtk_spin_button_spin(GTK_SPIN_BUTTON(viewer->cur_page), GTK_SPIN_STEP_BACKWARD, 1);
1619 vadj = gtk_scrolled_window_get_vadjustment(
1620 GTK_SCROLLED_WINDOW(viewer->scrollwin));
1621 gtk_adjustment_set_value(vadj,
1622 gtk_adjustment_get_upper(vadj) - gtk_adjustment_get_page_size(vadj));
1623 g_signal_emit_by_name(G_OBJECT(vadj), "value-changed", 0);
1624 return TRUE;
1626 return FALSE;
1628 else return TRUE;
1631 static void pdf_viewer_scroll_one_line(MimeViewer *_viewer, gboolean up)
1633 PdfViewer *viewer = (PdfViewer *)_viewer;
1634 GtkAdjustment *vadj = gtk_scrolled_window_get_vadjustment(
1635 GTK_SCROLLED_WINDOW(viewer->scrollwin));
1636 gint cur_p;
1637 cur_p = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(viewer->cur_page));
1639 if (viewer->pdf_view == NULL) return;
1640 debug_print("up: %d\n", up);
1641 if (gtk_adjustment_get_value(vadj) < (gtk_adjustment_get_upper(vadj) - gtk_adjustment_get_page_size(vadj))) {
1642 gtkutils_scroll_one_line(GTK_WIDGET(viewer->pdf_view), vadj, up);
1644 else {
1645 if (cur_p != viewer->num_pages) {
1646 pdf_viewer_scroll_page((MimeViewer *)viewer, up);
1652 #define ADD_BUTTON_TO_GRID(widget, stock_image) \
1653 widget = gtk_button_new(); \
1654 img = stock_pixmap_widget(stock_image); \
1655 gtk_button_set_image(GTK_BUTTON(widget), img); \
1656 gtk_grid_attach(GTK_GRID(viewer->widgets_table), GTK_WIDGET(widget), \
1657 col, 0, 1, 1); \
1658 col++;
1659 #define ADD_TOGGLE_BUTTON_TO_GRID(widget, stock_image) \
1660 widget = gtk_toggle_button_new(); \
1661 img = stock_pixmap_widget(stock_image); \
1662 gtk_button_set_image(GTK_BUTTON(widget), img); \
1663 gtk_grid_attach(GTK_GRID(viewer->widgets_table), GTK_WIDGET(widget), \
1664 col, 0, 1, 1); \
1665 col++;
1667 #define ADD_SEP_TO_GRID \
1668 sep = gtk_label_new(""); \
1669 gtk_grid_attach(GTK_GRID(viewer->widgets_table), GTK_WIDGET(sep), \
1670 col, 0, 1, 1); \
1671 gtk_grid_set_column_spacing(GTK_GRID(viewer->widgets_table), 3); \
1672 col++;
1674 #if POPPLER_HAS_CAIRO
1675 static PangoContext *pdf_viewer_get_pango_context(gpointer data)
1677 return NULL;
1680 static gpointer pdf_viewer_get_data_to_print(gpointer data, gint sel_start, gint sel_end)
1682 return NULL; /* we don't need it */
1685 static void pdf_viewer_cb_begin_print(GtkPrintOperation *op, GtkPrintContext *context,
1686 gpointer user_data)
1688 PrintData *print_data;
1689 PopplerDocument *pdf_doc;
1690 gint n_pages = 0;
1691 print_data = (PrintData*) user_data;
1692 pdf_doc = (PopplerDocument *)printing_get_renderer_data(print_data);
1694 debug_print("Preparing print job...\n");
1696 n_pages = poppler_document_get_n_pages(pdf_doc);
1697 printing_set_n_pages(print_data, n_pages);
1698 gtk_print_operation_set_n_pages(op, n_pages);
1700 debug_print("Starting print job...\n");
1703 static void pdf_viewer_cb_draw_page(GtkPrintOperation *op, GtkPrintContext *context,
1704 int page_nr, gpointer user_data)
1706 cairo_t *cr;
1707 PrintData *print_data;
1708 PopplerDocument *pdf_doc;
1709 PopplerPage *pdf_page;
1711 print_data = (PrintData*) user_data;
1712 pdf_doc = (PopplerDocument *)printing_get_renderer_data(print_data);
1713 pdf_page = poppler_document_get_page(pdf_doc, page_nr);
1715 cr = gtk_print_context_get_cairo_context(context);
1716 cairo_scale(cr, printing_get_zoom(print_data), printing_get_zoom(print_data));
1717 cairo_set_source_rgb(cr, 0., 0., 0.);
1719 poppler_page_render_for_printing(pdf_page, cr);
1721 g_object_unref(G_OBJECT(pdf_page));
1723 debug_print("Sent page %d to printer\n", page_nr+1);
1726 static void pdf_viewer_print(MimeViewer *mviewer)
1728 PdfViewer *viewer = (PdfViewer *)mviewer;
1729 PrintRenderer *pdf_renderer = g_new0(PrintRenderer, 1);
1730 MainWindow *mainwin = mainwindow_get_mainwindow();
1732 pdf_renderer->get_pango_context = pdf_viewer_get_pango_context;
1733 pdf_renderer->get_data_to_print = pdf_viewer_get_data_to_print;
1734 pdf_renderer->cb_begin_print = pdf_viewer_cb_begin_print;
1735 pdf_renderer->cb_draw_page = pdf_viewer_cb_draw_page;
1737 printing_print_full(mainwin ? GTK_WINDOW(mainwin->window):NULL,
1738 pdf_renderer, viewer->pdf_doc, -1, -1, NULL);
1740 g_free(pdf_renderer);
1742 #endif
1744 static MimeViewer *pdf_viewer_create(void)
1746 PdfViewer *viewer;
1747 GtkTreeViewColumn *column;
1748 GtkCellRenderer *renderer;
1749 GtkTreeStore *tree_store;
1750 GtkWidget *sep;
1751 GtkWidget *img;
1752 gint col = 0;
1754 viewer = g_new0(PdfViewer, 1);
1755 debug_print("pdf_viewer_create\n");
1757 viewer->last_x = 0;
1758 viewer->last_y = 0;
1759 viewer->mimeviewer.factory = &pdf_viewer_factory;
1760 viewer->mimeviewer.get_widget = pdf_viewer_get_widget;
1761 viewer->mimeviewer.show_mimepart = pdf_viewer_show_mimepart;
1762 viewer->mimeviewer.clear_viewer = pdf_viewer_clear;
1763 viewer->mimeviewer.destroy_viewer = pdf_viewer_destroy;
1764 viewer->mimeviewer.text_search = pdf_viewer_text_search;
1765 viewer->mimeviewer.scroll_page = pdf_viewer_scroll_page;
1766 viewer->mimeviewer.scroll_one_line = pdf_viewer_scroll_one_line;
1767 #if POPPLER_HAS_CAIRO
1768 viewer->mimeviewer.print = pdf_viewer_print;
1769 #endif
1770 viewer->scrollwin = gtk_scrolled_window_new(NULL, NULL);
1771 gtk_widget_set_name(GTK_WIDGET(viewer->scrollwin), "document");
1772 viewer->scrollwin_index = gtk_scrolled_window_new(NULL, NULL);
1773 viewer->pdf_view_ebox = gtk_event_box_new();
1774 gtk_event_box_set_visible_window(GTK_EVENT_BOX(viewer->pdf_view_ebox), FALSE);
1776 viewer->mimeinfo = NULL;
1778 viewer->pdf_view = gtk_image_new();
1779 gtk_widget_set_events(viewer->pdf_view_ebox,
1780 GDK_BUTTON_RELEASE_MASK
1781 | GDK_POINTER_MOTION_MASK
1782 | GDK_BUTTON_PRESS_MASK
1783 | GDK_BUTTON_MOTION_MASK
1785 gtk_container_add (GTK_CONTAINER(viewer->pdf_view_ebox), viewer->pdf_view);
1786 viewer->icon_type = gtk_image_new();
1787 viewer->icon_type_ebox = gtk_event_box_new();
1789 gtk_container_add(GTK_CONTAINER(viewer->icon_type_ebox), viewer->icon_type);
1791 viewer->doc_label = gtk_label_new("");
1793 viewer->widgets_table = gtk_grid_new();
1795 viewer->doc_index_pane = gtk_paned_new(GTK_ORIENTATION_HORIZONTAL);
1796 gtk_widget_set_name(GTK_WIDGET(viewer->doc_index_pane), "document_pane");
1798 viewer->frame_index = gtk_frame_new(NULL);
1799 gtk_widget_set_name(GTK_WIDGET(viewer->frame_index), "document_index");
1800 gtk_frame_set_shadow_type(GTK_FRAME(viewer->frame_index), GTK_SHADOW_IN);
1801 gtk_widget_set_size_request(viewer->frame_index, 18, -1);
1802 gtk_frame_set_label(GTK_FRAME(viewer->frame_index), _("Document Index"));
1804 ADD_SEP_TO_GRID
1805 ADD_BUTTON_TO_GRID(viewer->first_page, STOCK_PIXMAP_FIRST_ARROW)
1806 ADD_BUTTON_TO_GRID(viewer->prev_page, STOCK_PIXMAP_LEFT_ARROW)
1807 viewer->cur_page = gtk_spin_button_new_with_range(0.0, 0.0, 1.0);
1808 viewer->zoom_scroll = gtk_spin_button_new_with_range(0.20, 8.0, 0.20);
1809 gtk_spin_button_set_value(GTK_SPIN_BUTTON(viewer->zoom_scroll), 1.0);
1810 viewer->zoom = 1.0;
1811 gtk_spin_button_set_numeric(GTK_SPIN_BUTTON(viewer->cur_page), TRUE);
1812 gtk_spin_button_set_numeric(GTK_SPIN_BUTTON(viewer->zoom_scroll), TRUE);
1813 gtk_grid_attach(GTK_GRID(viewer->widgets_table), GTK_WIDGET(viewer->cur_page),
1814 col, 0, 1, 1);
1816 col++;
1817 gtk_grid_attach(GTK_GRID(viewer->widgets_table), GTK_WIDGET(viewer->doc_label),
1818 col, 0, 1, 1);
1819 col++;
1821 ADD_BUTTON_TO_GRID(viewer->next_page, STOCK_PIXMAP_RIGHT_ARROW)
1822 ADD_BUTTON_TO_GRID(viewer->last_page, STOCK_PIXMAP_LAST_ARROW)
1823 ADD_SEP_TO_GRID
1824 ADD_BUTTON_TO_GRID(viewer->zoom_fit, STOCK_PIXMAP_ZOOM_FIT)
1825 ADD_BUTTON_TO_GRID(viewer->zoom_in, STOCK_PIXMAP_ZOOM_IN)
1826 gtk_grid_attach(GTK_GRID(viewer->widgets_table), GTK_WIDGET(viewer->zoom_scroll),
1827 col, 0, 1, 1);
1828 col++;
1829 ADD_BUTTON_TO_GRID(viewer->zoom_out, STOCK_PIXMAP_ZOOM_OUT)
1830 ADD_BUTTON_TO_GRID(viewer->zoom_width, STOCK_PIXMAP_ZOOM_WIDTH)
1831 ADD_SEP_TO_GRID
1832 ADD_BUTTON_TO_GRID(viewer->rotate_left, STOCK_PIXMAP_ROTATE_LEFT)
1833 ADD_BUTTON_TO_GRID(viewer->rotate_right, STOCK_PIXMAP_ROTATE_RIGHT)
1834 ADD_SEP_TO_GRID
1835 ADD_BUTTON_TO_GRID(viewer->print, STOCK_PIXMAP_PRINTER)
1836 ADD_BUTTON_TO_GRID(viewer->doc_info, STOCK_PIXMAP_DOC_INFO)
1837 ADD_TOGGLE_BUTTON_TO_GRID(viewer->doc_index, STOCK_PIXMAP_DOC_INDEX)
1839 gtk_scrolled_window_set_policy(
1840 GTK_SCROLLED_WINDOW(viewer->scrollwin),
1841 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1843 gtk_scrolled_window_set_shadow_type(
1844 GTK_SCROLLED_WINDOW(viewer->scrollwin),
1845 GTK_SHADOW_IN);
1847 gtk_container_add(
1848 GTK_CONTAINER(viewer->scrollwin),
1849 viewer->pdf_view_ebox);
1851 viewer->vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 4);
1852 viewer->hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 4);
1854 gtk_widget_set_name(GTK_WIDGET(viewer->vbox), "pdf_viewer");
1855 gtk_widget_set_name(GTK_WIDGET(viewer->hbox), "buttons");
1857 /* treeview */
1858 tree_store = gtk_tree_store_new(N_INDEX_COLUMNS,
1859 G_TYPE_STRING,
1860 G_TYPE_INT,
1861 G_TYPE_DOUBLE);
1863 viewer->index_list = gtk_tree_view_new_with_model(GTK_TREE_MODEL(tree_store));
1865 renderer = gtk_cell_renderer_text_new();
1866 column = gtk_tree_view_column_new_with_attributes(_("Name"), renderer, "text", 0, NULL);
1867 gtk_tree_view_append_column(GTK_TREE_VIEW(viewer->index_list), column);
1868 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(viewer->index_list), FALSE);
1870 viewer->index_model = GTK_TREE_MODEL(tree_store);
1871 g_object_unref(tree_store);
1873 gtk_tree_selection_set_mode(gtk_tree_view_get_selection(GTK_TREE_VIEW(viewer->index_list)),
1874 GTK_SELECTION_SINGLE);
1876 g_signal_connect(G_OBJECT(viewer->index_list), "row_activated",
1877 G_CALLBACK(pdf_viewer_index_row_activated),
1878 viewer);
1880 gtk_scrolled_window_set_policy(
1881 GTK_SCROLLED_WINDOW(viewer->scrollwin_index),
1882 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1884 gtk_scrolled_window_set_shadow_type(
1885 GTK_SCROLLED_WINDOW(viewer->scrollwin_index),
1886 GTK_SHADOW_IN);
1888 gtk_container_add(
1889 GTK_CONTAINER(viewer->scrollwin_index),
1890 viewer->index_list);
1892 /* end treeview */
1894 stock_pixbuf_gdk(STOCK_PIXMAP_MIME_TEXT_PLAIN,
1895 &viewer->icon_pixbuf);
1897 gtk_image_set_from_pixbuf(GTK_IMAGE(viewer->icon_type),
1898 viewer->icon_pixbuf);
1900 /* pack widgets*/
1901 gtk_box_pack_start(GTK_BOX(viewer->hbox), viewer->icon_type_ebox, FALSE, FALSE, 0);
1902 gtk_box_pack_start(GTK_BOX(viewer->hbox), viewer->widgets_table, FALSE, FALSE, 0);
1904 gtk_container_add(GTK_CONTAINER(viewer->frame_index), viewer->scrollwin_index);
1906 gtk_paned_pack1(GTK_PANED(viewer->doc_index_pane), viewer->frame_index, FALSE, FALSE);
1907 gtk_paned_pack2(GTK_PANED(viewer->doc_index_pane), viewer->scrollwin, FALSE, FALSE);
1909 gtk_box_pack_start(GTK_BOX(viewer->vbox), viewer->hbox, FALSE, FALSE, 0);
1910 gtk_box_pack_start(GTK_BOX(viewer->vbox), viewer->doc_index_pane, TRUE, TRUE, 0);
1911 /* show widgets */
1912 gtk_widget_show(GTK_WIDGET(viewer->doc_index_pane));
1913 g_object_ref(GTK_WIDGET(viewer->doc_index_pane));
1914 gtk_widget_show(GTK_WIDGET(viewer->scrollwin));
1915 g_object_ref(GTK_WIDGET(viewer->scrollwin));
1916 gtk_widget_show(GTK_WIDGET(viewer->icon_type_ebox));
1917 g_object_ref(GTK_WIDGET(viewer->icon_type_ebox));
1918 gtk_widget_show(GTK_WIDGET(viewer->pdf_view_ebox));
1919 g_object_ref(GTK_WIDGET(viewer->pdf_view_ebox));
1920 gtk_widget_show(GTK_WIDGET(viewer->scrollwin_index));
1921 g_object_ref(GTK_WIDGET(viewer->scrollwin_index));
1922 gtk_widget_show(GTK_WIDGET(viewer->hbox));
1923 g_object_ref(GTK_WIDGET(viewer->hbox));
1924 gtk_widget_show(GTK_WIDGET(viewer->vbox));
1925 g_object_ref(GTK_WIDGET(viewer->vbox));
1927 gtk_widget_show(GTK_WIDGET(viewer->widgets_table));
1928 g_object_ref(GTK_WIDGET(viewer->widgets_table));
1930 gtk_widget_show(GTK_WIDGET(viewer->cur_page));
1931 g_object_ref(GTK_WIDGET(viewer->cur_page));
1933 gtk_widget_show(GTK_WIDGET(viewer->first_page));
1934 g_object_ref(GTK_WIDGET(viewer->first_page));
1936 gtk_widget_show(GTK_WIDGET(viewer->last_page));
1937 g_object_ref(GTK_WIDGET(viewer->last_page));
1939 gtk_widget_show(GTK_WIDGET(viewer->prev_page));
1940 g_object_ref(GTK_WIDGET(viewer->prev_page));
1942 gtk_widget_show(GTK_WIDGET(viewer->next_page));
1943 g_object_ref(GTK_WIDGET(viewer->next_page));
1945 gtk_widget_show(GTK_WIDGET(viewer->zoom_in));
1946 g_object_ref(GTK_WIDGET(viewer->zoom_in));
1947 gtk_widget_show(GTK_WIDGET(viewer->zoom_out));
1948 g_object_ref(GTK_WIDGET(viewer->zoom_out));
1949 gtk_widget_show(GTK_WIDGET(viewer->zoom_fit));
1950 g_object_ref(GTK_WIDGET(viewer->zoom_fit));
1951 gtk_widget_show(GTK_WIDGET(viewer->zoom_width));
1952 g_object_ref(GTK_WIDGET(viewer->zoom_width));
1954 gtk_widget_show(GTK_WIDGET(viewer->rotate_right));
1955 g_object_ref(GTK_WIDGET(viewer->rotate_right));
1956 gtk_widget_show(GTK_WIDGET(viewer->rotate_left));
1957 g_object_ref(GTK_WIDGET(viewer->rotate_left));
1958 gtk_widget_show(GTK_WIDGET(viewer->print));
1959 g_object_ref(GTK_WIDGET(viewer->print));
1960 gtk_widget_show(GTK_WIDGET(viewer->doc_info));
1961 g_object_ref(GTK_WIDGET(viewer->doc_info));
1962 gtk_widget_show(GTK_WIDGET(viewer->doc_index));
1963 g_object_ref(GTK_WIDGET(viewer->doc_index));
1965 gtk_widget_show(GTK_WIDGET(viewer->doc_label));
1966 g_object_ref(GTK_WIDGET(viewer->doc_label));
1967 gtk_widget_show(GTK_WIDGET(viewer->icon_type));
1968 g_object_ref(GTK_WIDGET(viewer->icon_type));
1969 gtk_widget_show(GTK_WIDGET(viewer->pdf_view));
1970 g_object_ref(GTK_WIDGET(viewer->pdf_view));
1971 gtk_widget_show(GTK_WIDGET(viewer->zoom_scroll));
1972 g_object_ref(GTK_WIDGET(viewer->zoom_scroll));
1974 gtk_widget_show(GTK_WIDGET(viewer->index_list));
1975 g_object_ref(GTK_WIDGET(viewer->index_list));
1977 /* Set Tooltips*/
1978 CLAWS_SET_TIP(viewer->first_page,
1979 _("First Page"));
1981 CLAWS_SET_TIP(viewer->prev_page,
1982 _("Previous Page"));
1984 CLAWS_SET_TIP(viewer->next_page,
1985 _("Next Page"));
1987 CLAWS_SET_TIP(viewer->last_page,
1988 _("Last Page"));
1990 CLAWS_SET_TIP(viewer->zoom_in,
1991 _("Zoom In"));
1992 CLAWS_SET_TIP(viewer->zoom_out,
1993 _("Zoom Out"));
1995 CLAWS_SET_TIP(viewer->zoom_fit,
1996 _("Fit Page"));
1998 CLAWS_SET_TIP(viewer->zoom_width,
1999 _("Fit Page Width"));
2001 CLAWS_SET_TIP(viewer->rotate_left,
2002 _("Rotate Left"));
2004 CLAWS_SET_TIP(viewer->rotate_right,
2005 _("Rotate Right"));
2007 CLAWS_SET_TIP(viewer->print,
2008 _("Print Document"));
2010 CLAWS_SET_TIP(viewer->doc_info,
2011 _("Document Info"));
2013 CLAWS_SET_TIP(viewer->doc_index,
2014 _("Document Index"));
2015 CLAWS_SET_TIP(viewer->cur_page,
2016 _("Page Number"));
2017 CLAWS_SET_TIP(viewer->zoom_scroll,
2018 _("Zoom Factor"));
2019 /* Connect Signals */
2020 g_signal_connect(G_OBJECT(viewer->cur_page),
2021 "value-changed",
2022 G_CALLBACK(pdf_viewer_spin_change_page_cb),
2023 (gpointer) viewer);
2025 g_signal_connect(G_OBJECT(viewer->first_page),
2026 "clicked",
2027 G_CALLBACK(pdf_viewer_button_first_page_cb),
2028 (gpointer) viewer);
2029 g_signal_connect(G_OBJECT(viewer->prev_page),
2030 "clicked",
2031 G_CALLBACK(pdf_viewer_button_prev_page_cb),
2032 (gpointer) viewer);
2033 g_signal_connect(G_OBJECT(viewer->next_page),
2034 "clicked",
2035 G_CALLBACK(pdf_viewer_button_next_page_cb),
2036 (gpointer) viewer);
2037 g_signal_connect(G_OBJECT(viewer->last_page),
2038 "clicked",
2039 G_CALLBACK(pdf_viewer_button_last_page_cb),
2040 (gpointer) viewer);
2041 g_signal_connect(G_OBJECT(viewer->zoom_in),
2042 "clicked",
2043 G_CALLBACK(pdf_viewer_button_zoom_in_cb),
2044 (gpointer) viewer);
2045 g_signal_connect(G_OBJECT(viewer->zoom_out),
2046 "clicked",
2047 G_CALLBACK(pdf_viewer_button_zoom_out_cb),
2048 (gpointer) viewer);
2049 g_signal_connect(G_OBJECT(viewer->zoom_scroll),
2050 "value-changed",
2051 G_CALLBACK(pdf_viewer_spin_zoom_scroll_cb),
2052 (gpointer) viewer);
2054 g_signal_connect(G_OBJECT(viewer->zoom_fit),
2055 "clicked",
2056 G_CALLBACK(pdf_viewer_button_zoom_fit_cb),
2057 (gpointer) viewer);
2059 g_signal_connect(G_OBJECT(viewer->zoom_width),
2060 "clicked",
2061 G_CALLBACK(pdf_viewer_button_zoom_width_cb),
2062 (gpointer) viewer);
2064 g_signal_connect(G_OBJECT(viewer->rotate_right),
2065 "clicked",
2066 G_CALLBACK(pdf_viewer_button_rotate_right_cb),
2067 (gpointer) viewer);
2069 g_signal_connect(G_OBJECT(viewer->rotate_left),
2070 "clicked",
2071 G_CALLBACK(pdf_viewer_button_rotate_left_cb),
2072 (gpointer) viewer);
2074 g_signal_connect(G_OBJECT(viewer->print),
2075 "clicked",
2076 G_CALLBACK(pdf_viewer_button_print_cb),
2077 (gpointer) viewer);
2079 g_signal_connect(G_OBJECT(viewer->doc_info),
2080 "clicked",
2081 G_CALLBACK(pdf_viewer_button_document_info_cb),
2082 (gpointer) viewer);
2084 g_signal_connect(G_OBJECT(viewer->doc_index),
2085 "clicked",
2086 G_CALLBACK(pdf_viewer_show_document_index_cb),
2087 (gpointer) viewer);
2088 g_signal_connect(G_OBJECT(viewer->scrollwin),
2089 "scroll-event",
2090 G_CALLBACK(pdf_viewer_scroll_cb),
2091 (gpointer) viewer);
2092 g_signal_connect(G_OBJECT(viewer->pdf_view_ebox),
2093 "button_press_event",
2094 G_CALLBACK(pdf_viewer_button_press_events_cb),
2095 (gpointer) viewer);
2096 g_signal_connect(G_OBJECT(viewer->pdf_view_ebox),
2097 "button_release_event",
2098 G_CALLBACK(pdf_viewer_mouse_scroll_destroy_cb),
2099 (gpointer) viewer);
2100 g_signal_connect(G_OBJECT(viewer->pdf_view_ebox),
2101 "motion_notify_event",
2102 G_CALLBACK(pdf_viewer_move_events_cb),
2103 (gpointer) viewer);
2105 viewer->target_filename = NULL;
2106 viewer->filename = NULL;
2107 viewer->fsname = NULL;
2109 return(MimeViewer *) viewer;
2112 #undef ADD_BUTTON_TO_GRID
2113 #undef ADD_TOGGLE_BUTTON_TO_GRID
2114 #undef ADD_SEP_TO_GRID
2115 #undef SEP_H_PADDING
2117 static MimeViewerFactory pdf_viewer_factory =
2119 content_types,
2121 pdf_viewer_create,
2124 gint plugin_init(gchar **error)
2126 gchar *gspath = NULL;
2128 msg = g_strdup_printf(_("This plugin enables the viewing of PDF and PostScript "
2129 "attachments using the Poppler %s Lib and the gs tool.\n\n"
2130 "Any feedback is welcome: iwkse@claws-mail.org"
2131 ), poppler_get_version());
2133 if (!check_plugin_version(MAKE_NUMERIC_VERSION(3,8,1,46),
2134 VERSION_NUMERIC, _("PDF Viewer"), error)) return -1;
2136 if ((gspath = g_find_program_in_path("gs")) == NULL) {
2137 gchar *pmsg = msg;
2138 msg = g_strdup_printf(_("Warning: could not find ghostscript binary (gs) required "
2139 "for %s plugin to process PostScript attachments, only PDF "
2140 "attachments will be displayed. To enable PostScript "
2141 "support please install gs program.\n\n%s"
2142 ), _("PDF Viewer"), pmsg);
2143 g_free(pmsg);
2145 else {
2146 g_free(gspath);
2149 mimeview_register_viewer_factory(&pdf_viewer_factory);
2150 return 0;
2153 gboolean plugin_done(void)
2155 g_free(msg);
2156 mimeview_unregister_viewer_factory(&pdf_viewer_factory);
2157 return TRUE;
2160 const gchar *plugin_name(void)
2162 return _("PDF Viewer");
2165 const gchar *plugin_desc(void)
2167 return msg;
2170 const gchar *plugin_type(void)
2172 return "GTK3";
2175 const gchar *plugin_licence(void)
2177 return "GPL3+";
2180 const gchar *plugin_version(void)
2182 return VERSION;
2185 struct PluginFeature *plugin_provides(void)
2187 static struct PluginFeature features[] =
2188 { {PLUGIN_MIMEVIEWER, "application/pdf"},
2189 {PLUGIN_MIMEVIEWER, "application/postscript"},
2190 {PLUGIN_NOTHING, NULL} };
2191 return features;