]> www.fi.muni.cz Git - evince.git/blob - properties/ev-properties-view.c
No need for a configure check; just use the GTK_CHECK_VERSION macro. Bug
[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:inch") == 0)
220                 return GTK_UNIT_INCH;
221         else if (strcmp (e, "default:mm") == 0)
222                 g_warning ("Whoever translated default:mm did so wrongly.\n");
223                                 
224         return GTK_UNIT_MM;
225 }
226
227 static gdouble
228 get_tolerance (gdouble size)
229 {
230         if (size < 150.0f)
231                 return 1.5f;
232         else if (size >= 150.0f && size <= 600.0f)
233                 return 2.0f;
234         else
235                 return 3.0f;
236 }
237
238 static char *
239 ev_regular_paper_size (const EvDocumentInfo *info)
240 {
241         GList *paper_sizes, *l;
242         gchar *exact_size;
243         gchar *str = NULL;
244         GtkUnit units;
245
246         units = get_default_user_units ();
247
248         if (units == GTK_UNIT_MM) {
249                 exact_size = g_strdup_printf(_("%.0f x %.0f mm"),
250                                              info->paper_width,
251                                              info->paper_height);
252         } else {
253                 exact_size = g_strdup_printf (_("%.2f x %.2f inch"),
254                                               info->paper_width  / 25.4f,
255                                               info->paper_height / 25.4f);
256         }
257
258         paper_sizes = gtk_paper_size_get_paper_sizes (FALSE);
259         
260         for (l = paper_sizes; l && l->data; l = g_list_next (l)) {
261                 GtkPaperSize *size = (GtkPaperSize *) l->data;
262                 gdouble paper_width;
263                 gdouble paper_height;
264                 gdouble width_tolerance;
265                 gdouble height_tolerance;
266
267                 paper_width = gtk_paper_size_get_width (size, GTK_UNIT_MM);
268                 paper_height = gtk_paper_size_get_height (size, GTK_UNIT_MM);
269
270                 width_tolerance = get_tolerance (paper_width);
271                 height_tolerance = get_tolerance (paper_height);
272
273                 if (ABS (info->paper_height - paper_height) <= height_tolerance &&
274                     ABS (info->paper_width  - paper_width) <= width_tolerance) {
275                         /* Note to translators: first placeholder is the paper name (eg.
276                          * A4), second placeholder is the paper size (eg. 297x210 mm) */
277                         str = g_strdup_printf (_("%s, Portrait (%s)"),
278                                                gtk_paper_size_get_display_name (size),
279                                                exact_size);
280                 } else if (ABS (info->paper_width  - paper_height) <= height_tolerance &&
281                            ABS (info->paper_height - paper_width) <= width_tolerance) {
282                         /* Note to translators: first placeholder is the paper name (eg.
283                          * A4), second placeholder is the paper size (eg. 297x210 mm) */
284                         str = g_strdup_printf ( _("%s, Landscape (%s)"),
285                                                 gtk_paper_size_get_display_name (size),
286                                                 exact_size);
287                 }
288         }
289
290         g_list_foreach (paper_sizes, (GFunc) gtk_paper_size_free, NULL);
291         g_list_free (paper_sizes);
292
293         if (str != NULL) {
294                 g_free (exact_size);
295                 return str;
296         }
297         
298         return exact_size;
299 }
300 #else /* ! GTK 2.11.0 */
301 /*
302  * All values are in mm. 
303  * Source: http://en.wikipedia.org/wiki/Paper_size
304  */
305 struct regular_paper_size {
306         double width;
307         double height;
308         double width_tolerance;
309         double height_tolerance;
310         const char *description;
311 } const regular_paper_sizes[] = {
312         // ISO 216 paper sizes
313         {  841.0f, 1189.0f, 3.0f, 3.0f, "A0"  },
314         {  594.0f,  841.0f, 2.0f, 3.0f, "A1"  },
315         {  420.0f,  594.0f, 2.0f, 2.0f, "A2"  },
316         {  297.0f,  420.0f, 2.0f, 2.0f, "A3"  },
317         {  210.0f,  297.0f, 2.0f, 2.0f, "A4"  },
318         {  148.0f,  210.0f, 1.5f, 2.0f, "A5"  },
319         {  105.0f,  148.0f, 1.5f, 1.5f, "A6"  },
320         {   74.0f,  105.0f, 1.5f, 1.5f, "A7"  },
321         {   52.0f,   74.0f, 1.5f, 1.5f, "A8"  },
322         {   37.0f,   52.0f, 1.5f, 1.5f, "A9"  },
323         {   26.0f,   37.0f, 1.5f, 1.5f, "A10" },
324         { 1000.0f, 1414.0f, 3.0f, 3.0f, "B0"  },
325         {  707.0f, 1000.0f, 3.0f, 3.0f, "B1"  },
326         {  500.0f,  707.0f, 2.0f, 3.0f, "B2"  },
327         {  353.0f,  500.0f, 2.0f, 2.0f, "B3"  },
328         {  250.0f,  353.0f, 2.0f, 2.0f, "B4"  },
329         {  176.0f,  250.0f, 2.0f, 2.0f, "B5"  },
330         {  125.0f,  176.0f, 1.5f, 2.0f, "B6"  },
331         {   88.0f,  125.0f, 1.5f, 1.5f, "B7"  },
332         {   62.0f,   88.0f, 1.5f, 1.5f, "B8"  },
333         {   44.0f,   62.0f, 1.5f, 1.5f, "B9"  },
334         {   31.0f,   44.0f, 1.5f, 1.5f, "B10" },
335         {  917.0f, 1297.0f, 3.0f, 3.0f, "C0"  },
336         {  648.0f,  917.0f, 3.0f, 3.0f, "C1"  },
337         {  458.0f,  648.0f, 2.0f, 3.0f, "C2"  },
338         {  324.0f,  458.0f, 2.0f, 2.0f, "C3"  },
339         {  229.0f,  324.0f, 2.0f, 2.0f, "C4"  },
340         {  162.0f,  229.0f, 2.0f, 2.0f, "C5"  },
341         {  114.0f,  162.0f, 1.5f, 2.0f, "C6"  },
342         {   81.0f,  114.0f, 1.5f, 1.5f, "C7"  },
343         {   57.0f,   81.0f, 1.5f, 1.5f, "C8"  },
344         {   40.0f,   57.0f, 1.5f, 1.5f, "C9"  },
345         {   28.0f,   40.0f, 1.5f, 1.5f, "C10" },
346
347         // US paper sizes
348         {  279.0f,  216.0f, 3.0f, 3.0f, "Letter" },
349         {  356.0f,  216.0f, 3.0f, 3.0f, "Legal"  },
350         {  432.0f,  279.0f, 3.0f, 3.0f, "Ledger" }
351 };
352
353 typedef enum {
354   EV_UNIT_INCH,
355   EV_UNIT_MM
356 } EvUnit; 
357
358 static EvUnit
359 ev_get_default_user_units (void)
360 {
361   /* Translate to the default units to use for presenting
362    * lengths to the user. Translate to default:inch if you
363    * want inches, otherwise translate to default:mm.
364    * Do *not* translate it to "predefinito:mm", if it
365    * it isn't default:mm or default:inch it will not work
366    */
367   gchar *e = _("default:mm");
368
369 #ifdef HAVE__NL_MEASUREMENT_MEASUREMENT
370   gchar *imperial = NULL;
371
372   imperial = nl_langinfo (_NL_MEASUREMENT_MEASUREMENT);
373   if (imperial && imperial[0] == 2 )
374     return EV_UNIT_INCH;  /* imperial */
375   if (imperial && imperial[0] == 1 )
376     return EV_UNIT_MM;  /* metric */
377 #endif
378
379   if (strcmp (e, "default:inch")==0)
380     return EV_UNIT_INCH;
381   else if (strcmp (e, "default:mm"))
382     g_warning ("Whoever translated default:mm did so wrongly.\n");
383   return EV_UNIT_MM;
384 }
385
386 static char *
387 ev_regular_paper_size (const EvDocumentInfo *info)
388 {
389         const struct regular_paper_size *size;   
390         EvUnit unit;
391         char *exact_size = NULL;
392         char *str = NULL;
393         int i;
394
395         unit = ev_get_default_user_units ();    
396
397         if (unit == EV_UNIT_INCH)
398                 /* Imperial measurement (inches) */
399                 exact_size = g_strdup_printf( _("%.2f x %.2f in"),
400                                               info->paper_width  / 25.4f,
401                                               info->paper_height / 25.4f );
402         else
403                 /* Metric measurement (millimeters) */
404                 exact_size = g_strdup_printf( _("%.0f x %.0f mm"),
405                                               info->paper_width,
406                                               info->paper_height );
407         
408         for (i = G_N_ELEMENTS ( regular_paper_sizes ) - 1; i >= 0; i--) {
409                 size = &regular_paper_sizes[i];
410
411                 if ( ABS( info->paper_height - size->height ) <= size->height_tolerance &&
412                      ABS( info->paper_width  - size->width  ) <= size->width_tolerance ) {
413                         /* Note to translators: first placeholder is the paper name (eg.
414                          * A4), second placeholder is the paper size (eg. 297x210 mm) */
415                         str = g_strdup_printf ( _("%s, Portrait (%s)"),
416                                                 size->description,
417                                                 exact_size );
418                 } else if ( ABS( info->paper_width  - size->height ) <= size->height_tolerance &&
419                             ABS( info->paper_height - size->width  ) <= size->width_tolerance ) {
420                         /* Note to translators: first placeholder is the paper name (eg.
421                          * A4), second placeholder is the paper size (eg. 297x210 mm) */
422                         str = g_strdup_printf ( _("%s, Landscape (%s)"),
423                                                 size->description,
424                                                 exact_size );
425                 }
426         }
427
428         if (str != NULL) {
429                 g_free (exact_size);
430                 return str;
431         } else
432                 return exact_size;
433 }
434 #endif /* GTK 2.11.0 */
435
436 void
437 ev_properties_view_set_info (EvPropertiesView *properties, const EvDocumentInfo *info)
438 {
439         GladeXML *xml = properties->xml;
440         char *text;
441
442         if (info->fields_mask & EV_DOCUMENT_INFO_TITLE) {
443                 set_property (xml, TITLE_PROPERTY, info->title);
444         }
445         if (info->fields_mask & EV_DOCUMENT_INFO_SUBJECT) {
446                 set_property (xml, SUBJECT_PROPERTY, info->subject);
447         }
448         if (info->fields_mask & EV_DOCUMENT_INFO_AUTHOR) {
449                 set_property (xml, AUTHOR_PROPERTY, info->author);
450         }
451         if (info->fields_mask & EV_DOCUMENT_INFO_KEYWORDS) {
452                 set_property (xml, KEYWORDS_PROPERTY, info->keywords);
453         }
454         if (info->fields_mask & EV_DOCUMENT_INFO_PRODUCER) {
455                 set_property (xml, PRODUCER_PROPERTY, info->producer);
456         }
457         if (info->fields_mask & EV_DOCUMENT_INFO_CREATOR) {
458                 set_property (xml, CREATOR_PROPERTY, info->creator);
459         }
460         if (info->fields_mask & EV_DOCUMENT_INFO_CREATION_DATE) {
461                 text = ev_properties_view_format_date (info->creation_date);
462                 set_property (xml, CREATION_DATE_PROPERTY, text);
463                 g_free (text);
464         }
465         if (info->fields_mask & EV_DOCUMENT_INFO_MOD_DATE) {
466                 text = ev_properties_view_format_date (info->modified_date);
467                 set_property (xml, MOD_DATE_PROPERTY, text);
468                 g_free (text);
469         }
470         if (info->fields_mask & EV_DOCUMENT_INFO_FORMAT) {
471                 text = g_strdup_printf ("%s", info->format);
472                 set_property (xml, FORMAT_PROPERTY, text);
473                 g_free (text);
474         }
475         if (info->fields_mask & EV_DOCUMENT_INFO_N_PAGES) {
476                 text = g_strdup_printf ("%d", info->n_pages);
477                 set_property (xml, N_PAGES_PROPERTY, text);
478                 g_free (text);
479         }
480         if (info->fields_mask & EV_DOCUMENT_INFO_LINEARIZED) {
481                 set_property (xml, LINEARIZED_PROPERTY, info->linearized);
482         }
483         if (info->fields_mask & EV_DOCUMENT_INFO_SECURITY) {
484                 set_property (xml, SECURITY_PROPERTY, info->security);
485         }
486         if (info->fields_mask & EV_DOCUMENT_INFO_PAPER_SIZE) {
487                 text = ev_regular_paper_size (info);
488                 set_property (xml, PAPER_SIZE_PROPERTY, text);
489                 g_free (text);
490         }
491 }
492
493 static void
494 ev_properties_view_init (EvPropertiesView *properties)
495 {
496         GladeXML *xml;
497
498         /* Create a new GladeXML object from XML file glade_file */
499         xml = glade_xml_new (DATADIR "/evince-properties.glade", "general_page_root", GETTEXT_PACKAGE);
500         properties->xml = xml;
501         g_assert (xml != NULL);
502
503         gtk_box_pack_start (GTK_BOX (properties),
504                             glade_xml_get_widget (xml, "general_page_root"),
505                             TRUE, TRUE, 0);
506 }
507
508 void
509 ev_properties_view_register_type (GTypeModule *module)
510 {
511         ev_properties_view_get_type ();
512 }
513
514 GtkWidget *
515 ev_properties_view_new (void)
516 {
517         EvPropertiesView *properties;
518
519         properties = g_object_new (EV_TYPE_PROPERTIES, NULL);
520
521         return GTK_WIDGET (properties);
522 }