1 /* $Id$ */
2 static char const _copyright[] =
3 "Copyright © 2010-2011 Sébastien Bocahu <zecrazytux@zecrazytux.net>\n"
4 "Copyright © 2011-2014 Pierre Pronchery <khorben@defora.org>";
5 static char const _license[] =
6 "This program is free software; you can redistribute it and/or modify\n"
7 "it under the terms of the GNU General Public License as published by the\n"
8 "Free Software Foundation, version 3 of the License.\n"
9 "\n"
10 "This program is distributed in the hope that it will be useful,\n"
11 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
12 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
13 "GNU General Public License for more details.\n"
14 "\n"
15 "You should have received a copy of the GNU General Public License\n"
16 "along with this program. If not, see <http://www.gnu.org/licenses/>.\n";
17
18
19
20 #include <sys/stat.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <errno.h>
24 #include <math.h>
25 #include <libintl.h>
26 #include <gdk/gdkkeysyms.h>
27 #include <poppler.h>
28 #include <Desktop.h>
29 #include "callbacks.h"
30 #include "pdfviewer.h"
31 #include "../config.h"
32 #define _(string) gettext(string)
33 #define N_(string) (string)
34
35 /* constants */
36 #ifndef PROGNAME
37 # define PROGNAME "pdfviewer"
38 #endif
39
40
41 /* PDFviewer */
42 /* private */
43 /* types */
44 typedef struct _PDF
45 {
46 PopplerDocument * document;
47
48 int pages;
49 int current;
50
51 GtkWidget * area;
52 cairo_surface_t * surface;
53 double scale;
54 } PDF;
55
56 struct _PDFviewer
57 {
58 PDF * pdf;
59
60 /* widgets */
61 PangoFontDescription * bold;
62 GtkWidget * window;
63 #ifndef EMBEDDED
64 GtkWidget * menubar;
65 #endif
66 GtkWidget * view;
67 GtkWidget * statusbar;
68 GtkWidget * toolbar;
69 GtkToolItem * tb_fullscreen;
70 /* about */
71 GtkWidget * ab_window;
72 };
73
74
75 /* variables */
76 static char const * _authors[] =
77 {
78 "Sébastien Bocahu <zecrazytux@zecrazytux.net>",
79 "Pierre Pronchery <khorben@defora.org>",
80 NULL
81 };
82
83 static DesktopAccel _pdfviewer_accel[] =
84 {
85 #ifdef EMBEDDED
86 { G_CALLBACK(on_contents), 0, GDK_KEY_F1 },
87 #endif
88 { G_CALLBACK(on_fullscreen), 0, GDK_KEY_F11 },
89 { G_CALLBACK(on_next), 0, GDK_KEY_Page_Down },
90 { G_CALLBACK(on_next), GDK_CONTROL_MASK, GDK_KEY_N },
91 #ifdef EMBEDDED
92 { G_CALLBACK(on_open), GDK_CONTROL_MASK, GDK_KEY_O },
93 { G_CALLBACK(on_pdf_close), GDK_CONTROL_MASK, GDK_KEY_W },
94 #endif
95 { G_CALLBACK(on_previous), 0, GDK_KEY_Page_Up },
96 { G_CALLBACK(on_previous), GDK_CONTROL_MASK, GDK_KEY_P },
97 { NULL, 0, 0 }
98 };
99
100 #ifndef EMBEDDED
101 static DesktopMenu _pdfviewer_menu_file[] =
102 {
103 { N_("_Open"), G_CALLBACK(on_file_open), GTK_STOCK_OPEN,
104 GDK_CONTROL_MASK, GDK_KEY_O },
105 { "", NULL, NULL, 0, 0 },
106 { N_("_Properties"), G_CALLBACK(on_file_properties),
107 GTK_STOCK_PROPERTIES, GDK_MOD1_MASK, GDK_KEY_Return },
108 { "", NULL, NULL, 0, 0 },
109 { N_("_Close"), G_CALLBACK(on_file_close), GTK_STOCK_CLOSE,
110 GDK_CONTROL_MASK, GDK_KEY_W },
111 { NULL, NULL, NULL, 0, 0 }
112 };
113
114 static DesktopMenu _pdfviewer_menu_edit[] =
115 {
116 { N_("_Preferences"), G_CALLBACK(on_edit_preferences),
117 GTK_STOCK_PREFERENCES, GDK_CONTROL_MASK, GDK_KEY_P },
118 { NULL, NULL, NULL, 0, 0 }
119 };
120
121 static DesktopMenu _pdfviewer_menu_view[] =
122 {
123 { N_("Zoom _in"), G_CALLBACK(on_view_zoom_in), "zoom-in",
124 GDK_CONTROL_MASK, GDK_KEY_plus },
125 { N_("Zoom _out"), G_CALLBACK(on_view_zoom_out), "zoom-out",
126 GDK_CONTROL_MASK, GDK_KEY_minus },
127 { N_("Normal size"), G_CALLBACK(on_view_normal_size), "zoom-original",
128 GDK_CONTROL_MASK, GDK_KEY_0 },
129 { "", NULL, NULL, 0, 0 },
130 #if GTK_CHECK_VERSION(2, 8, 0)
131 { N_("_Fullscreen"), G_CALLBACK(on_view_fullscreen),
132 GTK_STOCK_FULLSCREEN, 0, GDK_KEY_F11 },
133 #else
134 { N_("_Fullscreen"), G_CALLBACK(on_view_fullscreen), NULL, 0,
135 GDK_KEY_F11 },
136 #endif
137 { NULL, NULL, NULL, 0, 0 }
138 };
139
140 static DesktopMenu _pdfviewer_menu_help[] =
141 {
142 { N_("_Contents"), G_CALLBACK(on_help_contents), "help-contents", 0,
143 GDK_KEY_F1 },
144 { N_("_About"), G_CALLBACK(on_help_about),
145 #if GTK_CHECK_VERSION(2, 6, 0)
146 GTK_STOCK_ABOUT, 0, 0 },
147 #else
148 NULL, 0, 0 },
149 #endif
150 { NULL, NULL, NULL, 0, 0 }
151 };
152
153 static DesktopMenubar _pdfviewer_menubar[] =
154 {
155 { N_("_File"), _pdfviewer_menu_file },
156 { N_("_Edit"), _pdfviewer_menu_edit },
157 { N_("_View"), _pdfviewer_menu_view },
158 { N_("_Help"), _pdfviewer_menu_help },
159 { NULL, NULL }
160 };
161 #endif
162
163 static DesktopToolbar _pdfviewer_toolbar[] =
164 {
165 { N_("Open"), G_CALLBACK(on_open), GTK_STOCK_OPEN, 0, 0, NULL },
166 { "", NULL, NULL, 0, 0, NULL },
167 { N_("Far before"), G_CALLBACK(on_far_before), GTK_STOCK_MEDIA_PREVIOUS,
168 0, 0, NULL },
169 { N_("Previous"), G_CALLBACK(on_previous), GTK_STOCK_MEDIA_REWIND,
170 0, 0, NULL },
171 { N_("Next"), G_CALLBACK(on_next), GTK_STOCK_MEDIA_FORWARD, 0, 0, NULL },
172 { N_("Far after"), G_CALLBACK(on_far_after), GTK_STOCK_MEDIA_NEXT, 0, 0,
173 NULL },
174 { "", NULL, NULL, 0, 0, NULL },
175 { N_("Zoom in"), G_CALLBACK(on_zoom_in), GTK_STOCK_ZOOM_IN, 0, 0,
176 NULL },
177 { N_("Zoom out"), G_CALLBACK(on_zoom_out), GTK_STOCK_ZOOM_OUT, 0, 0,
178 NULL },
179 { NULL, NULL, NULL, 0, 0, NULL }
180 };
181
182
183 /* prototypes */
184 static void _pdfviewer_set_title(PDFviewer * pdfviewer);
185
186
187 /* public */
188 /* functions */
189 /* pdfviewer_new */
pdfviewer_new(void)190 PDFviewer * pdfviewer_new(void)
191 {
192 PDFviewer * pdfviewer;
193 GtkAccelGroup * group;
194 GtkSettings * settings;
195 GtkWidget * vbox;
196 GtkWidget * widget;
197 GtkToolItem * toolitem;
198
199 if((pdfviewer = malloc(sizeof(*pdfviewer))) == NULL)
200 return NULL;
201 settings = gtk_settings_get_default();
202 pdfviewer->pdf = NULL;
203 /* widgets */
204 pdfviewer->bold = pango_font_description_new();
205 pango_font_description_set_weight(pdfviewer->bold, PANGO_WEIGHT_BOLD);
206 group = gtk_accel_group_new();
207 pdfviewer->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
208 gtk_window_add_accel_group(GTK_WINDOW(pdfviewer->window), group);
209 g_object_unref(group);
210 gtk_window_set_default_size(GTK_WINDOW(pdfviewer->window), 600, 400);
211 _pdfviewer_set_title(pdfviewer);
212 #if GTK_CHECK_VERSION(2, 6, 0)
213 gtk_window_set_icon_name(GTK_WINDOW(pdfviewer->window),
214 "gnome-mime-application-pdf");
215 #endif
216 g_signal_connect_swapped(G_OBJECT(pdfviewer->window), "delete-event",
217 G_CALLBACK(on_closex), pdfviewer);
218 vbox = gtk_vbox_new(FALSE, 0);
219 /* menubar */
220 #ifndef EMBEDDED
221 pdfviewer->menubar = desktop_menubar_create(_pdfviewer_menubar,
222 pdfviewer, group);
223 gtk_box_pack_start(GTK_BOX(vbox), pdfviewer->menubar, FALSE, FALSE, 0);
224 #endif
225 desktop_accel_create(_pdfviewer_accel, pdfviewer, group);
226 /* toolbar */
227 pdfviewer->toolbar = desktop_toolbar_create(_pdfviewer_toolbar,
228 pdfviewer, group);
229 set_prevnext_sensitivity(pdfviewer);
230 #if GTK_CHECK_VERSION(2, 8, 0)
231 toolitem = gtk_toggle_tool_button_new_from_stock(GTK_STOCK_FULLSCREEN);
232 #else
233 toolitem = gtk_toggle_tool_button_new_from_stock(GTK_STOCK_ZOOM_FIT);
234 #endif
235 pdfviewer->tb_fullscreen = toolitem;
236 g_signal_connect_swapped(G_OBJECT(toolitem), "toggled", G_CALLBACK(
237 on_fullscreen), pdfviewer);
238 gtk_toolbar_insert(GTK_TOOLBAR(pdfviewer->toolbar), toolitem, -1);
239 gtk_box_pack_start(GTK_BOX(vbox), pdfviewer->toolbar, FALSE, FALSE, 0);
240 /* view */
241 widget = gtk_scrolled_window_new(NULL, NULL);
242 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(widget),
243 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
244
245 pdfviewer->view = gtk_drawing_area_new();
246 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(widget),
247 pdfviewer->view);
248 gtk_box_pack_start(GTK_BOX(vbox), widget, TRUE, TRUE, 0);
249
250 /* statusbar */
251 pdfviewer->statusbar = gtk_statusbar_new();
252 gtk_box_pack_start(GTK_BOX(vbox), pdfviewer->statusbar,
253 FALSE, FALSE, 0);
254
255 /* about */
256 pdfviewer->ab_window = NULL;
257 gtk_container_add(GTK_CONTAINER(pdfviewer->window), vbox);
258 gtk_widget_show_all(pdfviewer->window);
259 return pdfviewer;
260 }
261
262
263 /* pdfviewer_delete */
pdfviewer_delete(PDFviewer * pdfviewer)264 void pdfviewer_delete(PDFviewer * pdfviewer)
265 {
266 #ifdef DEBUG
267 fprintf(stderr, "DEBUG: %s()\n", __func__);
268 #endif
269 #if 0 /* FIXME */
270 if(pdfviewer->pdf != NULL)
271 pdf_delete(pdfviewer->pdf);
272 #endif
273 pdf_close(pdfviewer);
274 pango_font_description_free(pdfviewer->bold);
275 if(pdfviewer->window != NULL)
276 gtk_widget_destroy(pdfviewer->window);
277 free(pdfviewer);
278 }
279
280
281 /* accessors */
282 /* pdfviewer_set_fullscreen */
pdfviewer_set_fullscreen(PDFviewer * pdfviewer,gboolean fullscreen)283 void pdfviewer_set_fullscreen(PDFviewer * pdfviewer, gboolean fullscreen)
284 {
285 if(fullscreen == TRUE)
286 {
287 #ifndef EMBEDDED
288 gtk_widget_hide(pdfviewer->menubar);
289 #endif
290 gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(
291 pdfviewer->tb_fullscreen), TRUE);
292 gtk_window_fullscreen(GTK_WINDOW(pdfviewer->window));
293 }
294 else
295 {
296 #ifndef EMBEDDED
297 gtk_widget_show(pdfviewer->menubar);
298 #endif
299 gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(
300 pdfviewer->tb_fullscreen), FALSE);
301 gtk_window_unfullscreen(GTK_WINDOW(pdfviewer->window));
302 }
303 }
304
305
306 /* useful */
307 /* pdfviewer_about */
308 static gboolean _about_on_closex(GtkWidget * widget);
309
pdfviewer_about(PDFviewer * pdfviewer)310 void pdfviewer_about(PDFviewer * pdfviewer)
311 {
312 if(pdfviewer->ab_window != NULL)
313 {
314 gtk_widget_show(pdfviewer->ab_window);
315 return;
316 }
317 pdfviewer->ab_window = desktop_about_dialog_new();
318 gtk_window_set_transient_for(GTK_WINDOW(pdfviewer->ab_window),
319 GTK_WINDOW(pdfviewer->window));
320 g_signal_connect(G_OBJECT(pdfviewer->ab_window), "delete-event",
321 G_CALLBACK(_about_on_closex), NULL);
322 desktop_about_dialog_set_authors(pdfviewer->ab_window, _authors);
323 desktop_about_dialog_set_comments(pdfviewer->ab_window,
324 _("PDF viewer for the DeforaOS desktop"));
325 desktop_about_dialog_set_copyright(pdfviewer->ab_window, _copyright);
326 desktop_about_dialog_set_license(pdfviewer->ab_window, _license);
327 desktop_about_dialog_set_logo_icon_name(pdfviewer->ab_window,
328 "document-print-preview");
329 desktop_about_dialog_set_name(pdfviewer->ab_window, PACKAGE);
330 desktop_about_dialog_set_version(pdfviewer->ab_window, VERSION);
331 desktop_about_dialog_set_website(pdfviewer->ab_window,
332 "http://www.defora.org/");
333 gtk_widget_show(pdfviewer->ab_window);
334 }
335
_about_on_closex(GtkWidget * widget)336 static gboolean _about_on_closex(GtkWidget * widget)
337 {
338 gtk_widget_hide(widget);
339 return TRUE;
340 }
341
342
343 /* pdfviewer_error */
pdfviewer_error(PDFviewer * pdfviewer,char const * message,int ret)344 int pdfviewer_error(PDFviewer * pdfviewer, char const * message, int ret)
345 {
346 GtkWidget * dialog;
347
348 dialog = gtk_message_dialog_new(GTK_WINDOW(pdfviewer->window),
349 GTK_DIALOG_DESTROY_WITH_PARENT,
350 GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE,
351 #if GTK_CHECK_VERSION(2, 6, 0)
352 "%s", _("Error"));
353 gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog),
354 #endif
355 "%s", message);
356 gtk_window_set_title(GTK_WINDOW(dialog), _("Error"));
357 g_signal_connect(G_OBJECT(dialog), "response", G_CALLBACK(
358 gtk_widget_destroy), NULL);
359 gtk_widget_show(dialog);
360 return ret;
361 }
362
363
364 /* pdfviewer_close */
pdfviewer_close(PDFviewer * pdfviewer)365 void pdfviewer_close(PDFviewer * pdfviewer)
366 {
367 #ifdef DEBUG
368 fprintf(stderr, "DEBUG: %s()\n", __func__);
369 #endif
370 gtk_widget_hide(pdfviewer->window);
371 gtk_main_quit();
372 }
373
374
375 /* pdfviewer_fullscreen_toggle */
pdfviewer_fullscreen_toggle(PDFviewer * pdfviewer)376 void pdfviewer_fullscreen_toggle(PDFviewer * pdfviewer)
377 {
378 GdkWindow * window;
379
380 #if GTK_CHECK_VERSION(2, 14, 0)
381 window = gtk_widget_get_window(pdfviewer->window);
382 #else
383 window = pdfviewer->window->window;
384 #endif
385 if((gdk_window_get_state(window) & GDK_WINDOW_STATE_FULLSCREEN)
386 != GDK_WINDOW_STATE_FULLSCREEN)
387 pdfviewer_set_fullscreen(pdfviewer, TRUE);
388 else
389 pdfviewer_set_fullscreen(pdfviewer, FALSE);
390 }
391
392
393 /* pdfviewer_open */
pdfviewer_open(PDFviewer * pdfviewer,char const * filename)394 int pdfviewer_open(PDFviewer * pdfviewer, char const * filename)
395 {
396 int ret;
397
398 #ifdef DEBUG
399 fprintf(stderr, "DEBUG: %s()\n", __func__);
400 #endif
401 /* FIXME handle errors */
402 if(filename == NULL)
403 return pdfviewer_open_dialog(pdfviewer);
404 if((ret = pdf_open(pdfviewer, filename)) != 0)
405 return ret;
406 _pdfviewer_set_title(pdfviewer);
407 return 0;
408 }
409
410
411 /* pdfviewer_properties */
412 static GtkWidget * _properties_label(PDFviewer * pdfviewer,
413 GtkSizeGroup * group, char const * label, char const * value);
414 static GtkWidget * _properties_label_date(PDFviewer * pdfviewer,
415 GtkSizeGroup * group, char const * label, time_t t);
416
pdfviewer_properties(PDFviewer * pdfviewer)417 void pdfviewer_properties(PDFviewer * pdfviewer)
418 {
419 GtkWidget * dialog;
420 GtkSizeGroup * group;
421 GtkWidget * vbox;
422 GtkWidget * hbox;
423 GtkWidget * widget;
424 char * p;
425 time_t t;
426
427 if(pdfviewer->pdf == NULL)
428 return;
429 dialog = gtk_dialog_new_with_buttons(_("Properties of FIXME"),
430 GTK_WINDOW(pdfviewer->window),
431 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
432 GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, NULL);
433 gtk_window_set_default_size(GTK_WINDOW(dialog), 300, 200);
434 group = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
435 #if GTK_CHECK_VERSION(2, 14, 0)
436 vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
437 #else
438 vbox = dialog->vbox;
439 #endif
440 gtk_box_set_spacing(GTK_BOX(vbox), 4);
441 /* title */
442 widget = gtk_entry_new();
443 if((p = poppler_document_get_title(pdfviewer->pdf->document)) != NULL)
444 gtk_entry_set_text(GTK_ENTRY(widget), p);
445 gtk_editable_set_editable(GTK_EDITABLE(widget), FALSE);
446 gtk_box_pack_start(GTK_BOX(vbox), widget, FALSE, TRUE, 0);
447 free(p);
448 /* author */
449 p = poppler_document_get_author(pdfviewer->pdf->document);
450 hbox = _properties_label(pdfviewer, group, "Author: ", p);
451 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0);
452 free(p);
453 /* subject */
454 p = poppler_document_get_subject(pdfviewer->pdf->document);
455 hbox = _properties_label(pdfviewer, group, "Subject: ", p);
456 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0);
457 free(p);
458 /* keywords */
459 p = poppler_document_get_keywords(pdfviewer->pdf->document);
460 hbox = _properties_label(pdfviewer, group, "Keywords: ", p);
461 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0);
462 free(p);
463 /* creator */
464 p = poppler_document_get_creator(pdfviewer->pdf->document);
465 hbox = _properties_label(pdfviewer, group, "Creator: ", p);
466 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0);
467 free(p);
468 /* producer */
469 p = poppler_document_get_producer(pdfviewer->pdf->document);
470 hbox = _properties_label(pdfviewer, group, "Producer: ", p);
471 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0);
472 free(p);
473 /* creation time */
474 t = poppler_document_get_creation_date(pdfviewer->pdf->document);
475 hbox = _properties_label_date(pdfviewer, group, "Created on: ", t);
476 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0);
477 /* modification time */
478 t = poppler_document_get_modification_date(pdfviewer->pdf->document);
479 hbox = _properties_label_date(pdfviewer, group, "Modified on: ", t);
480 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0);
481 gtk_widget_show_all(vbox);
482 gtk_dialog_run(GTK_DIALOG(dialog));
483 gtk_widget_destroy(dialog);
484 }
485
_properties_label(PDFviewer * pdfviewer,GtkSizeGroup * group,char const * label,char const * value)486 static GtkWidget * _properties_label(PDFviewer * pdfviewer,
487 GtkSizeGroup * group, char const * label, char const * value)
488 {
489 GtkWidget * hbox;
490 GtkWidget * widget;
491
492 hbox = gtk_hbox_new(FALSE, 4);
493 widget = gtk_label_new(label);
494 gtk_widget_modify_font(widget, pdfviewer->bold);
495 gtk_misc_set_alignment(GTK_MISC(widget), 0.0, 0.5);
496 gtk_size_group_add_widget(group, widget);
497 gtk_box_pack_start(GTK_BOX(hbox), widget, FALSE, TRUE, 0);
498 widget = gtk_label_new((value != NULL) ? value : "");
499 gtk_label_set_ellipsize(GTK_LABEL(widget), PANGO_ELLIPSIZE_END);
500 gtk_misc_set_alignment(GTK_MISC(widget), 0.0, 0.5);
501 gtk_box_pack_start(GTK_BOX(hbox), widget, TRUE, TRUE, 0);
502 return hbox;
503 }
504
_properties_label_date(PDFviewer * pdfviewer,GtkSizeGroup * group,char const * label,time_t t)505 static GtkWidget * _properties_label_date(PDFviewer * pdfviewer,
506 GtkSizeGroup * group, char const * label, time_t t)
507 {
508 char buf[256];
509 struct tm tm;
510
511 localtime_r(&t, &tm);
512 strftime(buf, sizeof(buf), "%b %d %Y, %H:%M:%S", &tm);
513 return _properties_label(pdfviewer, group, label, buf);
514 }
515
516
517 /* pdf_open */
pdf_open(PDFviewer * pdfviewer,const char * filename)518 int pdf_open(PDFviewer * pdfviewer, const char * filename)
519 {
520 gchar * uri;
521 gchar * p;
522 PDF * pdf;
523 GError * error = NULL;
524
525 #ifdef DEBUG
526 fprintf(stderr, "DEBUG: %s(\"%s\")\n", __func__, uri);
527 #endif
528 if(filename == NULL)
529 /* XXX report error */
530 return -1;
531 if(filename[0] == '/')
532 uri = g_strdup_printf("%s%s", "file://", filename);
533 else
534 {
535 p = g_get_current_dir();
536 uri = g_strdup_printf("%s%s/%s", "file://", p, filename);
537 g_free(p);
538 }
539 pdf = g_new0(PDF, 1);
540 if(uri == NULL || pdf == NULL)
541 {
542 g_free(pdf);
543 g_free(uri);
544 /* XXX report error */
545 return -1;
546 }
547 pdf->document = poppler_document_new_from_file(uri, NULL, &error);
548 g_free(uri);
549 if(pdf->document == NULL)
550 {
551 if(error != NULL)
552 {
553 fprintf(stderr, PROGNAME ": %s: %s\n", filename,
554 error->message);
555 g_error_free(error);
556 }
557 g_free(pdf);
558 return -1;
559 }
560 /* close the current document if any was opened */
561 pdf_close(pdfviewer);
562 pdfviewer->pdf = pdf;
563 pdf->pages = poppler_document_get_n_pages(pdf->document);
564 pdf_update_current(pdfviewer, '=', 0);
565 /* pdfviewer->pdf->scale = 1.0; */
566 pdf_load_page(pdfviewer);
567 return 0;
568 }
569
570
571 /* pdfviewer_open_dialog */
pdfviewer_open_dialog(PDFviewer * pdfviewer)572 int pdfviewer_open_dialog(PDFviewer * pdfviewer)
573 {
574 int ret;
575 GtkWidget * dialog;
576 GtkFileFilter * filter;
577 char * filename = NULL;
578
579 #ifdef DEBUG
580 fprintf(stderr, "DEBUG: %s()\n", __func__);
581 #endif
582 dialog = gtk_file_chooser_dialog_new(_("Open file..."),
583 GTK_WINDOW(pdfviewer->window),
584 GTK_FILE_CHOOSER_ACTION_OPEN,
585 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
586 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, NULL);
587 filter = gtk_file_filter_new();
588 gtk_file_filter_set_name(filter, _("PDF documents"));
589 gtk_file_filter_add_mime_type(filter, "application/pdf");
590 gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), filter);
591 filter = gtk_file_filter_new();
592 gtk_file_filter_set_name(filter, _("All files"));
593 gtk_file_filter_add_pattern(filter, "*");
594 gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), filter);
595 if(gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT)
596 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(
597 dialog));
598 gtk_widget_destroy(dialog);
599 if(filename == NULL)
600 return 0;
601 ret = pdfviewer_open(pdfviewer, filename);
602 g_free(filename);
603 return ret;
604 }
605
606
607 /* pdfviewer_show_preferences */
pdfviewer_show_preferences(PDFviewer * pdfviewer,gboolean show)608 void pdfviewer_show_preferences(PDFviewer * pdfviewer, gboolean show)
609 {
610 /* FIXME implement */
611 }
612
613
614 /* pdf_close */
pdf_close(PDFviewer * pdfviewer)615 void pdf_close(PDFviewer * pdfviewer)
616 {
617 GdkWindow * window;
618
619 #ifdef DEBUG
620 fprintf(stderr, "DEBUG: %s()\n", __func__);
621 #endif
622 #if GTK_CHECK_VERSION(2, 14, 0)
623 window = gtk_widget_get_window(pdfviewer->view);
624 #else
625 window = pdfviewer->view->window;
626 #endif
627 if(window != NULL)
628 gdk_window_clear(window);
629 if(pdfviewer->pdf != NULL)
630 free(pdfviewer->pdf);
631 pdfviewer->pdf = NULL;
632 }
633
634
635 /* pdf_load_page */
pdf_load_page(PDFviewer * pdfviewer)636 void pdf_load_page(PDFviewer * pdfviewer)
637 {
638 PopplerPage *page;
639 cairo_t *cr;
640 gdouble width, height;
641 GtkAllocation view_allocation;
642
643 #ifdef DEBUG
644 fprintf(stderr, "DEBUG: %s()\n", __func__);
645 #endif
646 if((page = poppler_document_get_page(pdfviewer->pdf->document,
647 pdfviewer->pdf->current)) == NULL)
648 /* FIXME prevent this from happening but keep the check in */
649 return;
650 poppler_page_get_size(page, &width, &height);
651
652 if(!pdfviewer->pdf->scale) {
653 /* gdk_drawable_get_size(gtk_widget_get_window(pdfviewer->view), &w, &h); */
654 #ifdef DEBUG
655 fprintf(stderr, "DEBUG: %s() scale not set!\n", __func__);
656 #endif
657 #if GTK_CHECK_VERSION(2, 18, 0)
658 gtk_widget_get_allocation(pdfviewer->view, &view_allocation);
659 pdfviewer->pdf->scale = ((view_allocation.width - 20) / width);
660 #else
661 /* FIXME implement or re-work */
662 #endif
663 #if 0
664 pdfviewer->pdf->scale = (view_allocation.height / height); /* view whole page */
665 #endif
666 }
667
668 gtk_statusbar_push(GTK_STATUSBAR(pdfviewer->statusbar),
669 gtk_statusbar_get_context_id(
670 GTK_STATUSBAR(pdfviewer->statusbar), "read-page"),
671 g_strdup_printf(_("Page %d/%d"),
672 pdfviewer->pdf->current + 1, pdfviewer->pdf->pages));
673
674 if (pdfviewer->pdf->surface)
675 cairo_surface_destroy (pdfviewer->pdf->surface);
676 pdfviewer->pdf->surface = NULL;
677
678 #ifdef DEBUG
679 fprintf(stderr, "DEBUG: %s() scale: %f\n", __func__,
680 pdfviewer->pdf->scale);
681 #endif
682 pdfviewer->pdf->surface = cairo_image_surface_create(
683 CAIRO_FORMAT_ARGB32, ceil(pdfviewer->pdf->scale * width),
684 ceil(pdfviewer->pdf->scale * height));
685 cr = cairo_create(pdfviewer->pdf->surface);
686 cairo_save(cr);
687 cairo_scale(cr, pdfviewer->pdf->scale, pdfviewer->pdf->scale);
688 poppler_page_render(page, cr);
689 cairo_restore(cr);
690 cairo_destroy(cr);
691 g_object_unref(page);
692
693 g_signal_connect(pdfviewer->view, "expose-event", G_CALLBACK(
694 pdf_render_area), pdfviewer->pdf);
695
696 gtk_widget_set_size_request(pdfviewer->view,
697 pdfviewer->pdf->scale * ceil(width),
698 pdfviewer->pdf->scale * ceil(height));
699 gtk_widget_queue_draw(pdfviewer->view);
700 }
701
702
703 /* pdf_render_area */
pdf_render_area(GtkWidget * area,GdkEventExpose * event,void * data)704 void pdf_render_area(GtkWidget *area, GdkEventExpose *event, void * data)
705 {
706 PDF * pdf = data;
707 GdkWindow * window;
708 cairo_t *cr;
709
710 #ifdef DEBUG
711 fprintf(stderr, "DEBUG: %s()\n", __func__);
712 #endif
713 #if GTK_CHECK_VERSION(2, 14, 0)
714 window = gtk_widget_get_window(area);
715 #else
716 window = area->window;
717 #endif
718 if(window != NULL)
719 gdk_window_clear(window);
720 if(pdf == NULL)
721 return;
722 cr = gdk_cairo_create(window);
723 cairo_set_source_surface(cr, pdf->surface, 0, 0);
724 cairo_paint(cr);
725 cairo_destroy(cr);
726 }
727
728
729 /* pdf_update_current */
pdf_update_current(PDFviewer * pdfviewer,const char op,int n)730 void pdf_update_current(PDFviewer * pdfviewer, const char op, int n)
731 {
732 #ifdef DEBUG
733 fprintf(stderr, "DEBUG: %s()\n", __func__);
734 #endif
735 switch(op)
736 {
737 case '=': if((n >= 0) &&
738 (n <= (pdfviewer->pdf->pages - 1)))
739 pdfviewer->pdf->current = n;
740 break;
741 case '+': if((pdfviewer->pdf->current + n)
742 <= pdfviewer->pdf->pages)
743 pdfviewer->pdf->current =
744 pdfviewer->pdf->current + n;
745 break;
746 case '-': if((pdfviewer->pdf->current - n) >= 0)
747 pdfviewer->pdf->current =
748 pdfviewer->pdf->current - n;
749 break;
750 }
751 set_prevnext_sensitivity(pdfviewer);
752 }
753
754 /* set_prevnext_sensitivity */
set_prevnext_sensitivity(PDFviewer * pdfviewer)755 void set_prevnext_sensitivity(PDFviewer * pdfviewer)
756 {
757 GtkToolbar * toolbar = GTK_TOOLBAR(pdfviewer->toolbar);
758 gboolean farbefore, prev, next, farafter;
759
760 if(pdfviewer->pdf != NULL) {
761 /* XXX s/5/preferences/ */
762 farbefore = (pdfviewer->pdf->current > 5) ? TRUE : FALSE;
763 prev = (pdfviewer->pdf->current > 0) ? TRUE : FALSE;
764 next = (pdfviewer->pdf->current + 1 < pdfviewer->pdf->pages)
765 ? TRUE : FALSE;
766 farafter = (pdfviewer->pdf->current + 5 < pdfviewer->pdf->pages)
767 ? TRUE : FALSE;
768 } else {
769 farbefore = FALSE;
770 prev = FALSE;
771 next = FALSE;
772 farafter = FALSE;
773 }
774 gtk_widget_set_sensitive(GTK_WIDGET(
775 gtk_toolbar_get_nth_item(toolbar, 2)), farbefore);
776 gtk_widget_set_sensitive(GTK_WIDGET(
777 gtk_toolbar_get_nth_item(toolbar, 3)), prev);
778 gtk_widget_set_sensitive(GTK_WIDGET(
779 gtk_toolbar_get_nth_item(toolbar, 4)), next);
780 gtk_widget_set_sensitive(GTK_WIDGET(
781 gtk_toolbar_get_nth_item(toolbar, 5)), farafter);
782 }
783
784
785 /* pdf_update_scale */
pdf_update_scale(PDFviewer * pdfviewer,const char op,double n)786 void pdf_update_scale(PDFviewer * pdfviewer, const char op, double n)
787 {
788 #ifdef DEBUG
789 fprintf(stderr, "DEBUG: %s('%c', %f)\n", __func__, op, n);
790 #endif
791 switch(op)
792 {
793 case '=':
794 pdfviewer->pdf->scale = n;
795 break;
796 case '+':
797 pdfviewer->pdf->scale = pdfviewer->pdf->scale + n;
798 break;
799 case '-':
800 if((pdfviewer->pdf->scale - n) > 0)
801 pdfviewer->pdf->scale =
802 pdfviewer->pdf->scale - n;
803 break;
804 }
805 pdf_load_page(pdfviewer);
806 }
807
808
809 /* private */
810 /* functions */
811 /* pdfviewer_set_title */
_pdfviewer_set_title(PDFviewer * pdfviewer)812 static void _pdfviewer_set_title(PDFviewer * pdfviewer)
813 {
814 char const * title = _("(Untitled)");
815 char * p = NULL;
816 char buf[256];
817
818 if(pdfviewer->pdf != NULL)
819 if((p = poppler_document_get_title(pdfviewer->pdf->document))
820 != NULL)
821 /* FIXME use the filename instead */
822 title = p;
823 snprintf(buf, sizeof(buf), "%s%s", _("PDF viewer - "), title);
824 gtk_window_set_title(GTK_WINDOW(pdfviewer->window), buf);
825 free(p);
826 }
827