Evince
Evince is a document viewer capable of displaying multiple and single page document formats like PDF and Postscript.
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
ev-properties-view.c
Go to the documentation of this file.
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19  */
20 
21 #include "config.h"
22 
23 #include <string.h>
24 #include <sys/time.h>
25 #include <time.h>
26 
27 #ifdef HAVE__NL_MEASUREMENT_MEASUREMENT
28 #include <langinfo.h>
29 #endif
30 
31 #include <glib/gi18n-lib.h>
32 #include <gtk/gtk.h>
33 
34 #include "ev-properties-view.h"
35 
36 typedef enum {
53 } Property;
54 
55 typedef struct {
57  const char *label;
58 } PropertyInfo;
59 
60 static const PropertyInfo properties_info[] = {
61  { TITLE_PROPERTY, N_("Title:") },
62  { URI_PROPERTY, N_("Location:") },
63  { SUBJECT_PROPERTY, N_("Subject:") },
64  { AUTHOR_PROPERTY, N_("Author:") },
65  { KEYWORDS_PROPERTY, N_("Keywords:") },
66  { PRODUCER_PROPERTY, N_("Producer:") },
67  { CREATOR_PROPERTY, N_("Creator:") },
68  { CREATION_DATE_PROPERTY, N_("Created:") },
69  { MOD_DATE_PROPERTY, N_("Modified:") },
70  { N_PAGES_PROPERTY, N_("Number of Pages:") },
71  { LINEARIZED_PROPERTY, N_("Optimized:") },
72  { FORMAT_PROPERTY, N_("Format:") },
73  { SECURITY_PROPERTY, N_("Security:") },
74  { PAPER_SIZE_PROPERTY, N_("Paper Size:") },
75  { FILE_SIZE_PROPERTY, N_("Size:") }
76 };
77 
79  GtkVBox base_instance;
80 
81  GtkWidget *grid;
82  GtkWidget *labels[N_PROPERTIES];
83  gchar *uri;
84  guint64 file_size;
85 };
86 
88  GtkVBoxClass base_class;
89 };
90 
91 G_DEFINE_TYPE (EvPropertiesView, ev_properties_view, GTK_TYPE_BOX)
92 
93 static void
94 ev_properties_view_dispose (GObject *object)
95 {
97 
98  if (properties->uri) {
99  g_free (properties->uri);
100  properties->uri = NULL;
101  }
102 
103  G_OBJECT_CLASS (ev_properties_view_parent_class)->dispose (object);
104 }
105 
106 static void
108 {
109  GObjectClass *g_object_class = G_OBJECT_CLASS (properties_class);
110 
111  g_object_class->dispose = ev_properties_view_dispose;
112 }
113 
114 /* This is cut out of gconvert.c from glib (and mildly modified). Not all
115  backends give valid UTF-8 for properties, so we make sure that is.
116  */
117 static gchar *
118 make_valid_utf8 (const gchar *name)
119 {
120  GString *string;
121  const gchar *remainder, *invalid;
122  gint remaining_bytes, valid_bytes;
123 
124  string = NULL;
125  remainder = name;
126  remaining_bytes = strlen (name);
127 
128  while (remaining_bytes != 0)
129  {
130  if (g_utf8_validate (remainder, remaining_bytes, &invalid))
131  break;
132  valid_bytes = invalid - remainder;
133 
134  if (string == NULL)
135  string = g_string_sized_new (remaining_bytes);
136 
137  g_string_append_len (string, remainder, valid_bytes);
138  g_string_append_c (string, '?');
139 
140  remaining_bytes -= valid_bytes + 1;
141  remainder = invalid + 1;
142  }
143 
144  if (string == NULL)
145  return g_strdup (name);
146 
147  g_string_append (string, remainder);
148 
149  g_assert (g_utf8_validate (string->str, -1, NULL));
150 
151  return g_string_free (string, FALSE);
152 }
153 
154 static void
156  GtkGrid *grid,
157  Property property,
158  const gchar *text,
159  gint *row)
160 {
161  GtkWidget *property_label = NULL;
162  GtkWidget *value_label = NULL;
163  gchar *markup;
164  gchar *valid_text;
165 
166  if (!properties->labels[property]) {
167  property_label = gtk_label_new (NULL);
168  g_object_set (G_OBJECT (property_label), "xalign", 0.0, NULL);
169  markup = g_strdup_printf ("<b>%s</b>", _(properties_info[property].label));
170  gtk_label_set_markup (GTK_LABEL (property_label), markup);
171  g_free (markup);
172 
173  gtk_grid_attach (grid, property_label, 0, *row, 1, 1);
174  gtk_widget_show (property_label);
175  }
176 
177  if (!properties->labels[property]) {
178  value_label = gtk_label_new (NULL);
179 
180  g_object_set (G_OBJECT (value_label),
181  "xalign", 0.0,
182  "width_chars", 25,
183  "selectable", TRUE,
184  "ellipsize", PANGO_ELLIPSIZE_END,
185  NULL);
186  } else {
187  value_label = properties->labels[property];
188  }
189 
190  if (text == NULL || text[0] == '\000') {
191  /* translators: This is used when a document property does
192  not have a value. Examples:
193  Author: None
194  Keywords: None
195  */
196  markup = g_markup_printf_escaped ("<i>%s</i>", _("None"));
197  gtk_label_set_markup (GTK_LABEL (value_label), markup);
198  g_free (markup);
199  } else {
200  valid_text = make_valid_utf8 (text ? text : "");
201  gtk_label_set_text (GTK_LABEL (value_label), valid_text);
202  g_free (valid_text);
203  }
204 
205  if (!properties->labels[property]) {
206  gtk_grid_attach (grid, value_label, 1, *row, 1, 1);
207  properties->labels[property] = value_label;
208  }
209 
210  if (property_label && value_label) {
211  atk_object_add_relationship (gtk_widget_get_accessible (property_label),
212  ATK_RELATION_LABEL_FOR,
213  gtk_widget_get_accessible (value_label));
214  atk_object_add_relationship (gtk_widget_get_accessible (value_label),
215  ATK_RELATION_LABELLED_BY,
216  gtk_widget_get_accessible (property_label));
217  }
218 
219  gtk_widget_show (value_label);
220 
221  *row += 1;
222 }
223 
224 static GtkUnit
226 {
227  /* Translate to the default units to use for presenting
228  * lengths to the user. Translate to default:inch if you
229  * want inches, otherwise translate to default:mm.
230  * Do *not* translate it to "predefinito:mm", if it
231  * it isn't default:mm or default:inch it will not work
232  */
233  gchar *e = _("default:mm");
234 
235 #ifdef HAVE__NL_MEASUREMENT_MEASUREMENT
236  gchar *imperial = NULL;
237 
238  imperial = nl_langinfo (_NL_MEASUREMENT_MEASUREMENT);
239  if (imperial && imperial[0] == 2)
240  return GTK_UNIT_INCH; /* imperial */
241  if (imperial && imperial[0] == 1)
242  return GTK_UNIT_MM; /* metric */
243 #endif
244 
245  if (strcmp (e, "default:mm") == 0)
246  return GTK_UNIT_MM;
247  if (strcmp (e, "default:inch") == 0)
248  return GTK_UNIT_INCH;
249 
250  g_warning ("Whoever translated default:mm did so wrongly.\n");
251 
252  return GTK_UNIT_MM;
253 }
254 
255 static gdouble
256 get_tolerance (gdouble size)
257 {
258  if (size < 150.0f)
259  return 1.5f;
260  else if (size >= 150.0f && size <= 600.0f)
261  return 2.0f;
262  else
263  return 3.0f;
264 }
265 
266 static char *
268 {
269  GList *paper_sizes, *l;
270  gchar *exact_size;
271  gchar *str = NULL;
272  GtkUnit units;
273 
274  units = get_default_user_units ();
275 
276  if (units == GTK_UNIT_MM) {
277  exact_size = g_strdup_printf(_("%.0f × %.0f mm"),
278  info->paper_width,
279  info->paper_height);
280  } else {
281  exact_size = g_strdup_printf (_("%.2f × %.2f inch"),
282  info->paper_width / 25.4f,
283  info->paper_height / 25.4f);
284  }
285 
286  paper_sizes = gtk_paper_size_get_paper_sizes (FALSE);
287 
288  for (l = paper_sizes; l && l->data; l = g_list_next (l)) {
289  GtkPaperSize *size = (GtkPaperSize *) l->data;
290  gdouble paper_width;
291  gdouble paper_height;
292  gdouble width_tolerance;
293  gdouble height_tolerance;
294 
295  paper_width = gtk_paper_size_get_width (size, GTK_UNIT_MM);
296  paper_height = gtk_paper_size_get_height (size, GTK_UNIT_MM);
297 
298  width_tolerance = get_tolerance (paper_width);
299  height_tolerance = get_tolerance (paper_height);
300 
301  if (ABS (info->paper_height - paper_height) <= height_tolerance &&
302  ABS (info->paper_width - paper_width) <= width_tolerance) {
303  /* Note to translators: first placeholder is the paper name (eg.
304  * A4), second placeholder is the paper size (eg. 297x210 mm) */
305  str = g_strdup_printf (_("%s, Portrait (%s)"),
306  gtk_paper_size_get_display_name (size),
307  exact_size);
308  } else if (ABS (info->paper_width - paper_height) <= height_tolerance &&
309  ABS (info->paper_height - paper_width) <= width_tolerance) {
310  /* Note to translators: first placeholder is the paper name (eg.
311  * A4), second placeholder is the paper size (eg. 297x210 mm) */
312  str = g_strdup_printf ( _("%s, Landscape (%s)"),
313  gtk_paper_size_get_display_name (size),
314  exact_size);
315  }
316  }
317 
318  g_list_foreach (paper_sizes, (GFunc) gtk_paper_size_free, NULL);
319  g_list_free (paper_sizes);
320 
321  if (str != NULL) {
322  g_free (exact_size);
323  return str;
324  }
325 
326  return exact_size;
327 }
328 
329 void
331 {
332  GtkWidget *grid;
333  gchar *text;
334  gint row = 0;
335 
336  grid = properties->grid;
337 
338  if (info->fields_mask & EV_DOCUMENT_INFO_TITLE) {
339  set_property (properties, GTK_GRID (grid), TITLE_PROPERTY, info->title, &row);
340  }
341  set_property (properties, GTK_GRID (grid), URI_PROPERTY, properties->uri, &row);
342  if (info->fields_mask & EV_DOCUMENT_INFO_SUBJECT) {
343  set_property (properties, GTK_GRID (grid), SUBJECT_PROPERTY, info->subject, &row);
344  }
345  if (info->fields_mask & EV_DOCUMENT_INFO_AUTHOR) {
346  set_property (properties, GTK_GRID (grid), AUTHOR_PROPERTY, info->author, &row);
347  }
348  if (info->fields_mask & EV_DOCUMENT_INFO_KEYWORDS) {
349  set_property (properties, GTK_GRID (grid), KEYWORDS_PROPERTY, info->keywords, &row);
350  }
351  if (info->fields_mask & EV_DOCUMENT_INFO_PRODUCER) {
352  set_property (properties, GTK_GRID (grid), PRODUCER_PROPERTY, info->producer, &row);
353  }
354  if (info->fields_mask & EV_DOCUMENT_INFO_CREATOR) {
355  set_property (properties, GTK_GRID (grid), CREATOR_PROPERTY, info->creator, &row);
356  }
357  if (info->fields_mask & EV_DOCUMENT_INFO_CREATION_DATE) {
358  text = ev_document_misc_format_date (info->creation_date);
359  set_property (properties, GTK_GRID (grid), CREATION_DATE_PROPERTY, text, &row);
360  g_free (text);
361  }
362  if (info->fields_mask & EV_DOCUMENT_INFO_MOD_DATE) {
363  text = ev_document_misc_format_date (info->modified_date);
364  set_property (properties, GTK_GRID (grid), MOD_DATE_PROPERTY, text, &row);
365  g_free (text);
366  }
367  if (info->fields_mask & EV_DOCUMENT_INFO_FORMAT) {
368  set_property (properties, GTK_GRID (grid), FORMAT_PROPERTY, info->format, &row);
369  }
370  if (info->fields_mask & EV_DOCUMENT_INFO_N_PAGES) {
371  text = g_strdup_printf ("%d", info->n_pages);
372  set_property (properties, GTK_GRID (grid), N_PAGES_PROPERTY, text, &row);
373  g_free (text);
374  }
375  if (info->fields_mask & EV_DOCUMENT_INFO_LINEARIZED) {
376  set_property (properties, GTK_GRID (grid), LINEARIZED_PROPERTY, info->linearized, &row);
377  }
378  if (info->fields_mask & EV_DOCUMENT_INFO_SECURITY) {
379  set_property (properties, GTK_GRID (grid), SECURITY_PROPERTY, info->security, &row);
380  }
381  if (info->fields_mask & EV_DOCUMENT_INFO_PAPER_SIZE) {
382  text = ev_regular_paper_size (info);
383  set_property (properties, GTK_GRID (grid), PAPER_SIZE_PROPERTY, text, &row);
384  g_free (text);
385  }
386  if (properties->file_size) {
387  text = g_format_size (properties->file_size);
388  set_property (properties, GTK_GRID (grid), FILE_SIZE_PROPERTY, text, &row);
389  g_free (text);
390  }
391 }
392 
393 static void
395 {
396  properties->grid = gtk_grid_new ();
397  gtk_grid_set_column_spacing (GTK_GRID (properties->grid), 12);
398  gtk_grid_set_row_spacing (GTK_GRID (properties->grid), 6);
399  gtk_container_set_border_width (GTK_CONTAINER (properties->grid), 12);
400  gtk_box_pack_start (GTK_BOX (properties), properties->grid, TRUE, TRUE, 0);
401  gtk_widget_show (properties->grid);
402 }
403 
404 void
405 ev_properties_view_register_type (GTypeModule *module)
406 {
408 }
409 
410 GtkWidget *
412 {
414 
415  properties = g_object_new (EV_TYPE_PROPERTIES,
416  "orientation", GTK_ORIENTATION_VERTICAL,
417  NULL);
418  properties->uri = g_uri_unescape_string (ev_document_get_uri (document), NULL);
419  properties->file_size = ev_document_get_size (document);
420 
421  return GTK_WIDGET (properties);
422 }