]> www.fi.muni.cz Git - evince.git/blob - properties/ev-properties-view.c
4162fddd3dc8bbe8049c6d3f92e6ba1cbfbfba3d
[evince.git] / properties / ev-properties-view.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; c-indent-level: 8 -*- */
2 /* this file is part of evince, a gnome document viewer
3  *
4  *  Copyright (C) 2005 Red Hat, Inc
5  *
6  * Evince is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * Evince is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
19  */
20
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24
25 #include "ev-properties-view.h"
26 #include "ev-document-fonts.h"
27
28 #include <glib/gi18n.h>
29 #include <gtk/gtk.h>
30 #include <gtk/gtkversion.h>
31 #include <glade/glade.h>
32 #include <time.h>
33 #include <sys/time.h>
34 #include <string.h>
35
36 #ifdef HAVE__NL_MEASUREMENT_MEASUREMENT
37 #include <langinfo.h>
38 #endif
39
40 typedef enum
41 {
42         TITLE_PROPERTY,
43         SUBJECT_PROPERTY,
44         AUTHOR_PROPERTY,
45         KEYWORDS_PROPERTY,
46         PRODUCER_PROPERTY,
47         CREATOR_PROPERTY,
48         CREATION_DATE_PROPERTY,
49         MOD_DATE_PROPERTY,
50         N_PAGES_PROPERTY,
51         LINEARIZED_PROPERTY,
52         FORMAT_PROPERTY,
53         SECURITY_PROPERTY,
54         PAPER_SIZE_PROPERTY
55 } Property;
56
57 typedef struct
58 {
59         Property property;
60         const char *label_id;
61 } PropertyInfo;
62
63 static const PropertyInfo properties_info[] = {
64         { TITLE_PROPERTY, "title" },
65         { SUBJECT_PROPERTY, "subject" },
66         { AUTHOR_PROPERTY, "author" },
67         { KEYWORDS_PROPERTY, "keywords" },
68         { PRODUCER_PROPERTY, "producer" },
69         { CREATOR_PROPERTY, "creator" },
70         { CREATION_DATE_PROPERTY, "created" },
71         { MOD_DATE_PROPERTY, "modified" },
72         { N_PAGES_PROPERTY, "pages" },
73         { LINEARIZED_PROPERTY, "optimized" },
74         { FORMAT_PROPERTY, "version" },
75         { SECURITY_PROPERTY, "security" },
76         { PAPER_SIZE_PROPERTY, "papersize" }
77 };
78
79 struct _EvPropertiesView {
80         GtkVBox base_instance;
81
82         GladeXML *xml;
83 };
84
85 struct _EvPropertiesViewClass {
86         GtkVBoxClass base_class;
87 };
88
89 G_DEFINE_TYPE (EvPropertiesView, ev_properties_view, GTK_TYPE_VBOX)
90
91 static void
92 ev_properties_view_dispose (GObject *object)
93 {
94         EvPropertiesView *properties = EV_PROPERTIES_VIEW (object);
95
96         if (properties->xml) {
97                 g_object_unref (properties->xml);
98                 properties->xml = NULL;
99         }
100
101         G_OBJECT_CLASS (ev_properties_view_parent_class)->dispose (object);
102 }
103
104 static void
105 ev_properties_view_class_init (EvPropertiesViewClass *properties_class)
106 {
107         GObjectClass *g_object_class = G_OBJECT_CLASS (properties_class);
108
109         g_object_class->dispose = ev_properties_view_dispose;
110 }
111
112 /* Returns a locale specific date and time representation */
113 static char *
114 ev_properties_view_format_date (GTime utime)
115 {
116         time_t time = (time_t) utime;
117         struct tm t;
118         char s[256];
119         const char *fmt_hack = "%c";
120         size_t len;
121
122         if (time == 0 || !localtime_r (&time, &t)) return NULL;
123
124         len = strftime (s, sizeof (s), fmt_hack, &t);
125         if (len == 0 || s[0] == '\0') return NULL;
126
127         return g_locale_to_utf8 (s, -1, NULL, NULL, NULL);
128 }
129
130 /* This is cut out of gconvert.c from glib (and mildly modified).  Not all
131    backends give valid UTF-8 for properties, so we make sure that is.
132  */
133 static gchar *
134 make_valid_utf8 (const gchar *name)
135 {
136   GString *string;
137   const gchar *remainder, *invalid;
138   gint remaining_bytes, valid_bytes;
139   
140   string = NULL;
141   remainder = name;
142   remaining_bytes = strlen (name);
143   
144   while (remaining_bytes != 0) 
145     {
146       if (g_utf8_validate (remainder, remaining_bytes, &invalid)) 
147         break;
148       valid_bytes = invalid - remainder;
149     
150       if (string == NULL) 
151         string = g_string_sized_new (remaining_bytes);
152
153       g_string_append_len (string, remainder, valid_bytes);
154       g_string_append_c (string, '?');
155       
156       remaining_bytes -= valid_bytes + 1;
157       remainder = invalid + 1;
158     }
159   
160   if (string == NULL)
161     return g_strdup (name);
162   
163   g_string_append (string, remainder);
164
165   g_assert (g_utf8_validate (string->str, -1, NULL));
166   
167   return g_string_free (string, FALSE);
168 }
169
170 static void
171 set_property (GladeXML *xml, Property property, const char *text)
172 {
173         GtkWidget *widget;
174         char *valid_text;
175
176         widget = glade_xml_get_widget (xml, properties_info[property].label_id);
177         g_return_if_fail (GTK_IS_LABEL (widget));
178
179         if (text == NULL || text[0] == '\000') {
180                 gchar *markup;
181
182                 markup = g_markup_printf_escaped ("<i>%s</i>", _("None"));
183                 gtk_label_set_markup (GTK_LABEL (widget), markup);
184                 g_free (markup);
185
186                 return;
187         }
188         text = text ? text : "";
189
190         valid_text = make_valid_utf8 (text);
191
192         gtk_label_set_text (GTK_LABEL (widget), valid_text);
193
194         g_free (valid_text);
195 }
196
197 #if GTK_CHECK_VERSION (2, 11, 0)
198 static GtkUnit
199 get_default_user_units (void)
200 {
201         /* Translate to the default units to use for presenting
202          * lengths to the user. Translate to default:inch if you
203          * want inches, otherwise translate to default:mm.
204          * Do *not* translate it to "predefinito:mm", if it
205          * it isn't default:mm or default:inch it will not work
206          */
207         gchar *e = _("default:mm");
208
209 #ifdef HAVE__NL_MEASUREMENT_MEASUREMENT
210         gchar *imperial = NULL;
211         
212         imperial = nl_langinfo (_NL_MEASUREMENT_MEASUREMENT);
213         if (imperial && imperial[0] == 2)
214                 return GTK_UNIT_INCH;  /* imperial */
215         if (imperial && imperial[0] == 1)
216                 return GTK_UNIT_MM;  /* metric */
217 #endif
218
219         if (strcmp (e, "default:mm") == 0)
220                 return GTK_UNIT_MM;
221         if (strcmp (e, "default:inch") == 0)
222                 return GTK_UNIT_INCH;
223         
224         g_warning ("Whoever translated default:mm did so wrongly.\n");
225                                 
226         return GTK_UNIT_MM;
227 }
228
229 static gdouble
230 get_tolerance (gdouble size)
231 {
232         if (size < 150.0f)
233                 return 1.5f;
234         else if (size >= 150.0f && size <= 600.0f)
235                 return 2.0f;
236         else
237                 return 3.0f;
238 }
239
240 static char *
241 ev_regular_paper_size (const EvDocumentInfo *info)
242 {
243         GList *paper_sizes, *l;
244         gchar *exact_size;
245         gchar *str = NULL;
246         GtkUnit units;
247
248         units = get_default_user_units ();
249
250         if (units == GTK_UNIT_MM) {
251                 exact_size = g_strdup_printf(_("%.0f x %.0f mm"),
252                                              info->paper_width,
253                                              info->paper_height);
254         } else {
255                 exact_size = g_strdup_printf (_("%.2f x %.2f inch"),
256                                               info->paper_width  / 25.4f,
257                                               info->paper_height / 25.4f);
258         }
259
260         paper_sizes = gtk_paper_size_get_paper_sizes (FALSE);
261         
262         for (l = paper_sizes; l && l->data; l = g_list_next (l)) {
263                 GtkPaperSize *size = (GtkPaperSize *) l->data;
264                 gdouble paper_width;
265                 gdouble paper_height;
266                 gdouble width_tolerance;
267                 gdouble height_tolerance;
268
269                 paper_width = gtk_paper_size_get_width (size, GTK_UNIT_MM);
270                 paper_height = gtk_paper_size_get_height (size, GTK_UNIT_MM);
271
272                 width_tolerance = get_tolerance (paper_width);
273                 height_tolerance = get_tolerance (paper_height);
274
275                 if (ABS (info->paper_height - paper_height) <= height_tolerance &&
276                     ABS (info->paper_width  - paper_width) <= width_tolerance) {
277                         /* Note to translators: first placeholder is the paper name (eg.
278                          * A4), second placeholder is the paper size (eg. 297x210 mm) */
279                         str = g_strdup_printf (_("%s, Portrait (%s)"),
280                                                gtk_paper_size_get_display_name (size),
281                                                exact_size);
282                 } else if (ABS (info->paper_width  - paper_height) <= height_tolerance &&
283                            ABS (info->paper_height - paper_width) <= width_tolerance) {
284                         /* Note to translators: first placeholder is the paper name (eg.
285                          * A4), second placeholder is the paper size (eg. 297x210 mm) */
286                         str = g_strdup_printf ( _("%s, Landscape (%s)"),
287                                                 gtk_paper_size_get_display_name (size),
288                                                 exact_size);
289                 }
290         }
291
292         g_list_foreach (paper_sizes, (GFunc) gtk_paper_size_free, NULL);
293         g_list_free (paper_sizes);
294
295         if (str != NULL) {
296                 g_free (exact_size);
297                 return str;
298         }
299         
300         return exact_size;
301 }
302 #else /* ! GTK 2.11.0 */
303 /*
304  * All values are in mm. 
305  * Source: http://en.wikipedia.org/wiki/Paper_size
306  */
307 struct regular_paper_size {
308         double width;
309         double height;
310         double width_tolerance;
311         double height_tolerance;
312         const char *description;
313 } const regular_paper_sizes[] = {
314         // ISO 216 paper sizes
315         {  841.0f, 1189.0f, 3.0f, 3.0f, "A0"  },
316         {  594.0f,  841.0f, 2.0f, 3.0f, "A1"  },
317         {  420.0f,  594.0f, 2.0f, 2.0f, "A2"  },
318         {  297.0f,  420.0f, 2.0f, 2.0f, "A3"  },
319         {  210.0f,  297.0f, 2.0f, 2.0f, "A4"  },
320         {  148.0f,  210.0f, 1.5f, 2.0f, "A5"  },
321         {  105.0f,  148.0f, 1.5f, 1.5f, "A6"  },
322         {   74.0f,  105.0f, 1.5f, 1.5f, "A7"  },
323         {   52.0f,   74.0f, 1.5f, 1.5f, "A8"  },
324         {   37.0f,   52.0f, 1.5f, 1.5f, "A9"  },
325         {   26.0f,   37.0f, 1.5f, 1.5f, "A10" },
326         { 1000.0f, 1414.0f, 3.0f, 3.0f, "B0"  },
327         {  707.0f, 1000.0f, 3.0f, 3.0f, "B1"  },
328         {  500.0f,  707.0f, 2.0f, 3.0f, "B2"  },
329         {  353.0f,  500.0f, 2.0f, 2.0f, "B3"  },
330         {  250.0f,  353.0f, 2.0f, 2.0f, "B4"  },
331         {  176.0f,  250.0f, 2.0f, 2.0f, "B5"  },
332         {  125.0f,  176.0f, 1.5f, 2.0f, "B6"  },
333         {   88.0f,  125.0f, 1.5f, 1.5f, "B7"  },
334         {   62.0f,   88.0f, 1.5f, 1.5f, "B8"  },
335         {   44.0f,   62.0f, 1.5f, 1.5f, "B9"  },
336         {   31.0f,   44.0f, 1.5f, 1.5f, "B10" },
337         {  917.0f, 1297.0f, 3.0f, 3.0f, "C0"  },
338         {  648.0f,  917.0f, 3.0f, 3.0f, "C1"  },
339         {  458.0f,  648.0f, 2.0f, 3.0f, "C2"  },
340         {  324.0f,  458.0f, 2.0f, 2.0f, "C3"  },
341         {  229.0f,  324.0f, 2.0f, 2.0f, "C4"  },
342         {  162.0f,  229.0f, 2.0f, 2.0f, "C5"  },
343         {  114.0f,  162.0f, 1.5f, 2.0f, "C6"  },
344         {   81.0f,  114.0f, 1.5f, 1.5f, "C7"  },
345         {   57.0f,   81.0f, 1.5f, 1.5f, "C8"  },
346         {   40.0f,   57.0f, 1.5f, 1.5f, "C9"  },
347         {   28.0f,   40.0f, 1.5f, 1.5f, "C10" },
348
349         // US paper sizes
350         {  279.0f,  216.0f, 3.0f, 3.0f, "Letter" },
351         {  356.0f,  216.0f, 3.0f, 3.0f, "Legal"  },
352         {  432.0f,  279.0f, 3.0f, 3.0f, "Ledger" }
353 };
354
355 typedef enum {
356   EV_UNIT_INCH,
357   EV_UNIT_MM
358 } EvUnit; 
359
360 static EvUnit
361 ev_get_default_user_units (void)
362 {
363   /* Translate to the default units to use for presenting
364    * lengths to the user. Translate to default:inch if you
365    * want inches, otherwise translate to default:mm.
366    * Do *not* translate it to "predefinito:mm", if it
367    * it isn't default:mm or default:inch it will not work
368    */
369   gchar *e = _("default:mm");
370
371 #ifdef HAVE__NL_MEASUREMENT_MEASUREMENT
372   gchar *imperial = NULL;
373
374   imperial = nl_langinfo (_NL_MEASUREMENT_MEASUREMENT);
375   if (imperial && imperial[0] == 2 )
376     return EV_UNIT_INCH;  /* imperial */
377   if (imperial && imperial[0] == 1 )
378     return EV_UNIT_MM;  /* metric */
379 #endif
380
381   if (strcmp (e, "default:inch")==0)
382     return EV_UNIT_INCH;
383   else if (strcmp (e, "default:mm"))
384     g_warning ("Whoever translated default:mm did so wrongly.\n");
385   return EV_UNIT_MM;
386 }
387
388 static char *
389 ev_regular_paper_size (const EvDocumentInfo *info)
390 {
391         const struct regular_paper_size *size;   
392         EvUnit unit;
393         char *exact_size = NULL;
394         char *str = NULL;
395         int i;
396
397         unit = ev_get_default_user_units ();    
398
399         if (unit == EV_UNIT_INCH)
400                 /* Imperial measurement (inches) */
401                 exact_size = g_strdup_printf( _("%.2f x %.2f in"),
402                                               info->paper_width  / 25.4f,
403                                               info->paper_height / 25.4f );
404         else
405                 /* Metric measurement (millimeters) */
406                 exact_size = g_strdup_printf( _("%.0f x %.0f mm"),
407                                               info->paper_width,
408                                               info->paper_height );
409         
410         for (i = G_N_ELEMENTS ( regular_paper_sizes ) - 1; i >= 0; i--) {
411                 size = &regular_paper_sizes[i];
412
413                 if ( ABS( info->paper_height - size->height ) <= size->height_tolerance &&
414                      ABS( info->paper_width  - size->width  ) <= size->width_tolerance ) {
415                         /* Note to translators: first placeholder is the paper name (eg.
416                          * A4), second placeholder is the paper size (eg. 297x210 mm) */
417                         str = g_strdup_printf ( _("%s, Portrait (%s)"),
418                                                 size->description,
419                                                 exact_size );
420                 } else if ( ABS( info->paper_width  - size->height ) <= size->height_tolerance &&
421                             ABS( info->paper_height - size->width  ) <= size->width_tolerance ) {
422                         /* Note to translators: first placeholder is the paper name (eg.
423                          * A4), second placeholder is the paper size (eg. 297x210 mm) */
424                         str = g_strdup_printf ( _("%s, Landscape (%s)"),
425                                                 size->description,
426                                                 exact_size );
427                 }
428         }
429
430         if (str != NULL) {
431                 g_free (exact_size);
432                 return str;
433         } else
434                 return exact_size;
435 }
436 #endif /* GTK 2.11.0 */
437
438 void
439 ev_properties_view_set_info (EvPropertiesView *properties, const EvDocumentInfo *info)
440 {
441         GladeXML *xml = properties->xml;
442         char *text;
443
444         if (info->fields_mask & EV_DOCUMENT_INFO_TITLE) {
445                 set_property (xml, TITLE_PROPERTY, info->title);
446         }
447         if (info->fields_mask & EV_DOCUMENT_INFO_SUBJECT) {
448                 set_property (xml, SUBJECT_PROPERTY, info->subject);
449         }
450         if (info->fields_mask & EV_DOCUMENT_INFO_AUTHOR) {
451                 set_property (xml, AUTHOR_PROPERTY, info->author);
452         }
453         if (info->fields_mask & EV_DOCUMENT_INFO_KEYWORDS) {
454                 set_property (xml, KEYWORDS_PROPERTY, info->keywords);
455         }
456         if (info->fields_mask & EV_DOCUMENT_INFO_PRODUCER) {
457                 set_property (xml, PRODUCER_PROPERTY, info->producer);
458         }
459         if (info->fields_mask & EV_DOCUMENT_INFO_CREATOR) {
460                 set_property (xml, CREATOR_PROPERTY, info->creator);
461         }
462         if (info->fields_mask & EV_DOCUMENT_INFO_CREATION_DATE) {
463                 text = ev_properties_view_format_date (info->creation_date);
464                 set_property (xml, CREATION_DATE_PROPERTY, text);
465                 g_free (text);
466         }
467         if (info->fields_mask & EV_DOCUMENT_INFO_MOD_DATE) {
468                 text = ev_properties_view_format_date (info->modified_date);
469                 set_property (xml, MOD_DATE_PROPERTY, text);
470                 g_free (text);
471         }
472         if (info->fields_mask & EV_DOCUMENT_INFO_FORMAT) {
473                 text = g_strdup_printf ("%s", info->format);
474                 set_property (xml, FORMAT_PROPERTY, text);
475                 g_free (text);
476         }
477         if (info->fields_mask & EV_DOCUMENT_INFO_N_PAGES) {
478                 text = g_strdup_printf ("%d", info->n_pages);
479                 set_property (xml, N_PAGES_PROPERTY, text);
480                 g_free (text);
481         }
482         if (info->fields_mask & EV_DOCUMENT_INFO_LINEARIZED) {
483                 set_property (xml, LINEARIZED_PROPERTY, info->linearized);
484         }
485         if (info->fields_mask & EV_DOCUMENT_INFO_SECURITY) {
486                 set_property (xml, SECURITY_PROPERTY, info->security);
487         }
488         if (info->fields_mask & EV_DOCUMENT_INFO_PAPER_SIZE) {
489                 text = ev_regular_paper_size (info);
490                 set_property (xml, PAPER_SIZE_PROPERTY, text);
491                 g_free (text);
492         }
493 }
494
495 static void
496 ev_properties_view_init (EvPropertiesView *properties)
497 {
498         GladeXML *xml;
499
500         /* Create a new GladeXML object from XML file glade_file */
501         xml = glade_xml_new (DATADIR "/evince-properties.glade", "general_page_root", GETTEXT_PACKAGE);
502         properties->xml = xml;
503         g_assert (xml != NULL);
504
505         gtk_box_pack_start (GTK_BOX (properties),
506                             glade_xml_get_widget (xml, "general_page_root"),
507                             TRUE, TRUE, 0);
508 }
509
510 void
511 ev_properties_view_register_type (GTypeModule *module)
512 {
513         ev_properties_view_get_type ();
514 }
515
516 GtkWidget *
517 ev_properties_view_new (void)
518 {
519         EvPropertiesView *properties;
520
521         properties = g_object_new (EV_TYPE_PROPERTIES, NULL);
522
523         return GTK_WIDGET (properties);
524 }