1 /*
2 * Copyright (C) 2007 Carlos Garcia Campos <carlosgc@gnome.org>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2, or (at your option)
7 * any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
17 */
18
19 #include <gtk/gtk.h>
20 #include <stdio.h>
21 #include <unistd.h>
22 #include <time.h>
23
24 #include "utils.h"
25
26 void
pgd_table_add_property_with_custom_widget(GtkTable * table,const gchar * markup,GtkWidget * widget,gint * row)27 pgd_table_add_property_with_custom_widget (GtkTable *table,
28 const gchar *markup,
29 GtkWidget *widget,
30 gint *row)
31 {
32 GtkWidget *label;
33
34 label = gtk_label_new (NULL);
35 g_object_set (G_OBJECT (label), "xalign", 0.0, NULL);
36 gtk_label_set_markup (GTK_LABEL (label), markup);
37 gtk_table_attach (GTK_TABLE (table), label, 0, 1, *row, *row + 1,
38 GTK_FILL, GTK_FILL, 0, 0);
39 gtk_widget_show (label);
40
41 gtk_table_attach (GTK_TABLE (table), widget, 1, 2, *row, *row + 1,
42 GTK_FILL | GTK_EXPAND, GTK_FILL, 0, 0);
43 gtk_widget_show (widget);
44
45 *row += 1;
46 }
47
48 void
pgd_table_add_property_with_value_widget(GtkTable * table,const gchar * markup,GtkWidget ** value_widget,const gchar * value,gint * row)49 pgd_table_add_property_with_value_widget (GtkTable *table,
50 const gchar *markup,
51 GtkWidget **value_widget,
52 const gchar *value,
53 gint *row)
54 {
55 GtkWidget *label;
56
57 *value_widget = label = gtk_label_new (value);
58 g_object_set (G_OBJECT (label),
59 "xalign", 0.0,
60 "selectable", TRUE,
61 "ellipsize", PANGO_ELLIPSIZE_END,
62 NULL);
63 pgd_table_add_property_with_custom_widget (table, markup, label, row);
64 }
65
66 void
pgd_table_add_property(GtkTable * table,const gchar * markup,const gchar * value,gint * row)67 pgd_table_add_property (GtkTable *table,
68 const gchar *markup,
69 const gchar *value,
70 gint *row)
71 {
72 GtkWidget *label;
73
74 pgd_table_add_property_with_value_widget (table, markup, &label, value, row);
75 }
76
77 GtkWidget *
pgd_action_view_new(PopplerDocument * document)78 pgd_action_view_new (PopplerDocument *document)
79 {
80 GtkWidget *frame, *label;
81
82 frame = gtk_frame_new (NULL);
83 gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_NONE);
84 label = gtk_label_new (NULL);
85 gtk_label_set_markup (GTK_LABEL (label), "<b>Action Properties</b>");
86 gtk_frame_set_label_widget (GTK_FRAME (frame), label);
87 gtk_widget_show (label);
88
89 g_object_set_data (G_OBJECT (frame), "document", document);
90
91 return frame;
92 }
93
94 static void
pgd_action_view_add_destination(GtkWidget * action_view,GtkTable * table,PopplerDest * dest,gboolean remote,gint * row)95 pgd_action_view_add_destination (GtkWidget *action_view,
96 GtkTable *table,
97 PopplerDest *dest,
98 gboolean remote,
99 gint *row)
100 {
101 PopplerDocument *document;
102 GEnumValue *enum_value;
103 gchar *str;
104
105 pgd_table_add_property (table, "<b>Type:</b>", "Destination", row);
106
107 enum_value = g_enum_get_value ((GEnumClass *) g_type_class_ref (POPPLER_TYPE_DEST_TYPE), dest->type);
108 pgd_table_add_property (table, "<b>Destination Type:</b>", enum_value->value_name, row);
109
110 document = g_object_get_data (G_OBJECT (action_view), "document");
111
112 if (dest->type != POPPLER_DEST_NAMED) {
113 str = NULL;
114
115 if (document && !remote) {
116 PopplerPage *poppler_page;
117 gchar *page_label;
118
119 poppler_page = poppler_document_get_page (document, MAX (0, dest->page_num - 1));
120
121 g_object_get (G_OBJECT (poppler_page),
122 "label", &page_label,
123 NULL);
124 if (page_label) {
125 str = g_strdup_printf ("%d (%s)", dest->page_num, page_label);
126 g_free (page_label);
127 }
128 }
129
130 if (!str)
131 str = g_strdup_printf ("%d", dest->page_num);
132 pgd_table_add_property (table, "<b>Page:</b>", str, row);
133 g_free (str);
134
135 str = g_strdup_printf ("%.2f", dest->left);
136 pgd_table_add_property (table, "<b>Left:</b>", str, row);
137 g_free (str);
138
139 str = g_strdup_printf ("%.2f", dest->right);
140 pgd_table_add_property (table, "<b>Right:</b>", str, row);
141 g_free (str);
142
143 str = g_strdup_printf ("%.2f", dest->top);
144 pgd_table_add_property (table, "<b>Top:</b>", str, row);
145 g_free (str);
146
147 str = g_strdup_printf ("%.2f", dest->bottom);
148 pgd_table_add_property (table, "<b>Bottom:</b>", str, row);
149 g_free (str);
150
151 str = g_strdup_printf ("%.2f", dest->zoom);
152 pgd_table_add_property (table, "<b>Zoom:</b>", str, row);
153 g_free (str);
154 } else {
155 pgd_table_add_property (table, "<b>Named Dest:</b>", dest->named_dest, row);
156
157 if (document && !remote) {
158 PopplerDest *new_dest;
159
160 new_dest = poppler_document_find_dest (document, dest->named_dest);
161 if (new_dest) {
162 GtkWidget *new_table, *alignment;
163 gint new_row = 0;
164
165 alignment = gtk_alignment_new (0.5, 0.5, 1, 1);
166 gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), 5, 5, 12, 5);
167
168 new_table = gtk_table_new (8, 2, FALSE);
169 gtk_table_set_col_spacings (GTK_TABLE (new_table), 6);
170 gtk_table_set_row_spacings (GTK_TABLE (new_table), 6);
171 gtk_table_attach_defaults (table, alignment, 0, 2, *row, *row + 1);
172 gtk_widget_show (alignment);
173
174 pgd_action_view_add_destination (action_view, GTK_TABLE (new_table),
175 new_dest, FALSE, &new_row);
176 poppler_dest_free (new_dest);
177
178 gtk_container_add (GTK_CONTAINER (alignment), new_table);
179 gtk_widget_show (new_table);
180
181 *row += 1;
182 }
183 }
184 }
185 }
186
187 static const gchar *
get_movie_op(PopplerActionMovieOperation op)188 get_movie_op (PopplerActionMovieOperation op)
189 {
190 switch (op) {
191 case POPPLER_ACTION_MOVIE_PLAY:
192 return "Play";
193 case POPPLER_ACTION_MOVIE_PAUSE:
194 return "Pause";
195 case POPPLER_ACTION_MOVIE_RESUME:
196 return "Resume";
197 case POPPLER_ACTION_MOVIE_STOP:
198 return "Stop";
199 }
200 return NULL;
201 }
202
203 static void
free_tmp_file(GFile * tmp_file)204 free_tmp_file (GFile *tmp_file)
205 {
206
207 g_file_delete (tmp_file, NULL, NULL);
208 g_object_unref (tmp_file);
209 }
210
211 static gboolean
save_helper(const gchar * buf,gsize count,gpointer data,GError ** error)212 save_helper (const gchar *buf,
213 gsize count,
214 gpointer data,
215 GError **error)
216 {
217 gint fd = GPOINTER_TO_INT (data);
218
219 return write (fd, buf, count) == count;
220 }
221
222 static void
pgd_action_view_play_rendition(GtkWidget * button,PopplerMedia * media)223 pgd_action_view_play_rendition (GtkWidget *button,
224 PopplerMedia *media)
225 {
226 GFile *file = NULL;
227 gchar *uri;
228
229 if (poppler_media_is_embedded (media)) {
230 gint fd;
231 gchar *tmp_filename = NULL;
232
233 fd = g_file_open_tmp (NULL, &tmp_filename, NULL);
234 if (fd != -1) {
235 if (poppler_media_save_to_callback (media, save_helper, GINT_TO_POINTER (fd), NULL)) {
236 file = g_file_new_for_path (tmp_filename);
237 g_object_set_data_full (G_OBJECT (media),
238 "tmp-file", g_object_ref (file),
239 (GDestroyNotify)free_tmp_file);
240 } else {
241 g_free (tmp_filename);
242 }
243 close (fd);
244 } else if (tmp_filename) {
245 g_free (tmp_filename);
246 }
247
248 } else {
249 const gchar *filename;
250
251 filename = poppler_media_get_filename (media);
252 if (g_path_is_absolute (filename)) {
253 file = g_file_new_for_path (filename);
254 } else if (g_strrstr (filename, "://")) {
255 file = g_file_new_for_uri (filename);
256 } else {
257 gchar *cwd;
258 gchar *path;
259
260 // FIXME: relative to doc uri, not cwd
261 cwd = g_get_current_dir ();
262 path = g_build_filename (cwd, filename, NULL);
263 g_free (cwd);
264
265 file = g_file_new_for_path (path);
266 g_free (path);
267 }
268 }
269
270 if (file) {
271 uri = g_file_get_uri (file);
272 g_object_unref (file);
273 if (uri) {
274 gtk_show_uri (gtk_widget_get_screen (button),
275 uri, GDK_CURRENT_TIME, NULL);
276 g_free (uri);
277 }
278 }
279 }
280
281 static void
pgd_action_view_do_action_layer(GtkWidget * button,GList * state_list)282 pgd_action_view_do_action_layer (GtkWidget *button,
283 GList *state_list)
284 {
285 GList *l, *m;
286
287 for (l = state_list; l; l = g_list_next (l)) {
288 PopplerActionLayer *action_layer = (PopplerActionLayer *)l->data;
289
290 for (m = action_layer->layers; m; m = g_list_next (m)) {
291 PopplerLayer *layer = (PopplerLayer *)m->data;
292
293 switch (action_layer->action) {
294 case POPPLER_ACTION_LAYER_ON:
295 poppler_layer_show (layer);
296 break;
297 case POPPLER_ACTION_LAYER_OFF:
298 poppler_layer_hide (layer);
299 break;
300 case POPPLER_ACTION_LAYER_TOGGLE:
301 if (poppler_layer_is_visible (layer))
302 poppler_layer_hide (layer);
303 else
304 poppler_layer_show (layer);
305 break;
306 }
307 }
308 }
309 }
310
311 void
pgd_action_view_set_action(GtkWidget * action_view,PopplerAction * action)312 pgd_action_view_set_action (GtkWidget *action_view,
313 PopplerAction *action)
314 {
315 GtkWidget *alignment;
316 GtkWidget *table;
317 gint row = 0;
318
319 alignment = gtk_bin_get_child (GTK_BIN (action_view));
320 if (alignment) {
321 gtk_container_remove (GTK_CONTAINER (action_view), alignment);
322 }
323
324 alignment = gtk_alignment_new (0.5, 0.5, 1, 1);
325 gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), 5, 5, 12, 5);
326 gtk_container_add (GTK_CONTAINER (action_view), alignment);
327 gtk_widget_show (alignment);
328
329 if (!action)
330 return;
331
332 table = gtk_table_new (10, 2, FALSE);
333 gtk_table_set_col_spacings (GTK_TABLE (table), 6);
334 gtk_table_set_row_spacings (GTK_TABLE (table), 6);
335
336 pgd_table_add_property (GTK_TABLE (table), "<b>Title:</b>", action->any.title, &row);
337
338 switch (action->type) {
339 case POPPLER_ACTION_UNKNOWN:
340 pgd_table_add_property (GTK_TABLE (table), "<b>Type:</b>", "Unknown", &row);
341 break;
342 case POPPLER_ACTION_NONE:
343 pgd_table_add_property (GTK_TABLE (table), "<b>Type:</b>", "None", &row);
344 break;
345 case POPPLER_ACTION_GOTO_DEST:
346 pgd_action_view_add_destination (action_view, GTK_TABLE (table), action->goto_dest.dest, FALSE, &row);
347 break;
348 case POPPLER_ACTION_GOTO_REMOTE:
349 pgd_table_add_property (GTK_TABLE (table), "<b>Type:</b>", "Remote Destination", &row);
350 pgd_table_add_property (GTK_TABLE (table), "<b>Filename:</b>", action->goto_remote.file_name, &row);
351 pgd_action_view_add_destination (action_view, GTK_TABLE (table), action->goto_remote.dest, TRUE, &row);
352 break;
353 case POPPLER_ACTION_LAUNCH:
354 pgd_table_add_property (GTK_TABLE (table), "<b>Type:</b>", "Launch", &row);
355 pgd_table_add_property (GTK_TABLE (table), "<b>Filename:</b>", action->launch.file_name, &row);
356 pgd_table_add_property (GTK_TABLE (table), "<b>Params:</b>", action->launch.params, &row);
357 break;
358 case POPPLER_ACTION_URI:
359 pgd_table_add_property (GTK_TABLE (table), "<b>Type:</b>", "External URI", &row);
360 pgd_table_add_property (GTK_TABLE (table), "<b>URI</b>", action->uri.uri, &row);
361 break;
362 case POPPLER_ACTION_NAMED:
363 pgd_table_add_property (GTK_TABLE (table), "<b>Type:</b>", "Named Action", &row);
364 pgd_table_add_property (GTK_TABLE (table), "<b>Name:</b>", action->named.named_dest, &row);
365 break;
366 case POPPLER_ACTION_MOVIE: {
367 GtkWidget *movie_view = pgd_movie_view_new ();
368
369 pgd_table_add_property (GTK_TABLE (table), "<b>Type:</b>", "Movie", &row);
370 pgd_table_add_property (GTK_TABLE (table), "<b>Operation:</b>", get_movie_op (action->movie.operation), &row);
371 pgd_movie_view_set_movie (movie_view, action->movie.movie);
372 pgd_table_add_property_with_custom_widget (GTK_TABLE (table), "<b>Movie:</b>", movie_view, &row);
373 }
374 break;
375 case POPPLER_ACTION_RENDITION: {
376 gchar *text;
377
378 pgd_table_add_property (GTK_TABLE (table), "<b>Type:</b>", "Rendition", &row);
379 text = g_strdup_printf ("%d", action->rendition.op);
380 pgd_table_add_property (GTK_TABLE (table), "<b>Operation:</b>", text, &row);
381 g_free (text);
382 if (action->rendition.media) {
383 gboolean embedded = poppler_media_is_embedded (action->rendition.media);
384 GtkWidget *button;
385
386 pgd_table_add_property (GTK_TABLE (table), "<b>Embedded:</b>", embedded ? "Yes": "No", &row);
387 if (embedded) {
388 const gchar *mime_type = poppler_media_get_mime_type (action->rendition.media);
389 pgd_table_add_property (GTK_TABLE (table), "<b>Mime type:</b>",
390 mime_type ? mime_type : "",
391 &row);
392 } else {
393 pgd_table_add_property (GTK_TABLE (table), "<b>Filename:</b>",
394 poppler_media_get_filename (action->rendition.media),
395 &row);
396 }
397
398 button = gtk_button_new_from_stock (GTK_STOCK_MEDIA_PLAY);
399 g_signal_connect (button, "clicked",
400 G_CALLBACK (pgd_action_view_play_rendition),
401 action->rendition.media);
402 pgd_table_add_property_with_custom_widget (GTK_TABLE (table), NULL, button, &row);
403 gtk_widget_show (button);
404 }
405 }
406 break;
407 case POPPLER_ACTION_OCG_STATE: {
408 GList *l;
409 GtkWidget *button;
410
411 pgd_table_add_property (GTK_TABLE (table), "<b>Type:</b>", "OCGState", &row);
412
413 for (l = action->ocg_state.state_list; l; l = g_list_next (l)) {
414 PopplerActionLayer *action_layer = (PopplerActionLayer *)l->data;
415 gchar *text;
416 gint n_layers = g_list_length (action_layer->layers);
417
418 switch (action_layer->action) {
419 case POPPLER_ACTION_LAYER_ON:
420 text = g_strdup_printf ("%d layers On", n_layers);
421 break;
422 case POPPLER_ACTION_LAYER_OFF:
423 text = g_strdup_printf ("%d layers Off", n_layers);
424 break;
425 case POPPLER_ACTION_LAYER_TOGGLE:
426 text = g_strdup_printf ("%d layers Toggle", n_layers);
427 break;
428 }
429 pgd_table_add_property (GTK_TABLE (table), "<b>Action:</b>", text, &row);
430 g_free (text);
431 }
432
433 button = gtk_button_new_with_label ("Do action");
434 g_signal_connect (button, "clicked",
435 G_CALLBACK (pgd_action_view_do_action_layer),
436 action->ocg_state.state_list);
437 pgd_table_add_property_with_custom_widget (GTK_TABLE (table), NULL, button, &row);
438 gtk_widget_show (button);
439 }
440 break;
441 default:
442 g_assert_not_reached ();
443 }
444
445 gtk_container_add (GTK_CONTAINER (alignment), table);
446 gtk_widget_show (table);
447 }
448
449 gchar *
pgd_format_date(time_t utime)450 pgd_format_date (time_t utime)
451 {
452 time_t time = (time_t) utime;
453 char s[256];
454 const char *fmt_hack = "%c";
455 size_t len;
456 #ifdef HAVE_LOCALTIME_R
457 struct tm t;
458 if (time == 0 || !localtime_r (&time, &t)) return NULL;
459 len = strftime (s, sizeof (s), fmt_hack, &t);
460 #else
461 struct tm *t;
462 if (time == 0 || !(t = localtime (&time)) ) return NULL;
463 len = strftime (s, sizeof (s), fmt_hack, t);
464 #endif
465
466 if (len == 0 || s[0] == '\0') return NULL;
467
468 return g_locale_to_utf8 (s, -1, NULL, NULL, NULL);
469 }
470
471 GtkWidget *
pgd_movie_view_new(void)472 pgd_movie_view_new (void)
473 {
474 GtkWidget *frame, *label;
475
476 frame = gtk_frame_new (NULL);
477 gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_NONE);
478 label = gtk_label_new (NULL);
479 gtk_label_set_markup (GTK_LABEL (label), "<b>Movie Properties</b>");
480 gtk_frame_set_label_widget (GTK_FRAME (frame), label);
481 gtk_widget_show (label);
482
483 return frame;
484 }
485
486 static void
pgd_movie_view_play_movie(GtkWidget * button,PopplerMovie * movie)487 pgd_movie_view_play_movie (GtkWidget *button,
488 PopplerMovie *movie)
489 {
490 const gchar *filename;
491 GFile *file;
492 gchar *uri;
493
494 filename = poppler_movie_get_filename (movie);
495 if (g_path_is_absolute (filename)) {
496 file = g_file_new_for_path (filename);
497 } else if (g_strrstr (filename, "://")) {
498 file = g_file_new_for_uri (filename);
499 } else {
500 gchar *cwd;
501 gchar *path;
502
503 // FIXME: relative to doc uri, not cwd
504 cwd = g_get_current_dir ();
505 path = g_build_filename (cwd, filename, NULL);
506 g_free (cwd);
507
508 file = g_file_new_for_path (path);
509 g_free (path);
510 }
511
512 uri = g_file_get_uri (file);
513 g_object_unref (file);
514 if (uri) {
515 gtk_show_uri (gtk_widget_get_screen (button),
516 uri, GDK_CURRENT_TIME, NULL);
517 g_free (uri);
518 }
519 }
520
521 void
pgd_movie_view_set_movie(GtkWidget * movie_view,PopplerMovie * movie)522 pgd_movie_view_set_movie (GtkWidget *movie_view,
523 PopplerMovie *movie)
524 {
525 GtkWidget *alignment;
526 GtkWidget *table;
527 GtkWidget *button;
528 gint row = 0;
529
530 alignment = gtk_bin_get_child (GTK_BIN (movie_view));
531 if (alignment) {
532 gtk_container_remove (GTK_CONTAINER (movie_view), alignment);
533 }
534
535 alignment = gtk_alignment_new (0.5, 0.5, 1, 1);
536 gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), 5, 5, 12, 5);
537 gtk_container_add (GTK_CONTAINER (movie_view), alignment);
538 gtk_widget_show (alignment);
539
540 if (!movie)
541 return;
542
543 table = gtk_table_new (10, 2, FALSE);
544 gtk_table_set_col_spacings (GTK_TABLE (table), 6);
545 gtk_table_set_row_spacings (GTK_TABLE (table), 6);
546
547 pgd_table_add_property (GTK_TABLE (table), "<b>Filename:</b>", poppler_movie_get_filename (movie), &row);
548 pgd_table_add_property (GTK_TABLE (table), "<b>Need Poster:</b>", poppler_movie_need_poster (movie) ? "Yes" : "No", &row);
549 pgd_table_add_property (GTK_TABLE (table), "<b>Show Controls:</b>", poppler_movie_show_controls (movie) ? "Yes" : "No", &row);
550
551 button = gtk_button_new_from_stock (GTK_STOCK_MEDIA_PLAY);
552 g_signal_connect (button, "clicked",
553 G_CALLBACK (pgd_movie_view_play_movie),
554 movie);
555 pgd_table_add_property_with_custom_widget (GTK_TABLE (table), NULL, button, &row);
556 gtk_widget_show (button);
557
558 gtk_container_add (GTK_CONTAINER (alignment), table);
559 gtk_widget_show (table);
560 }
561