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