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-recent-view.c
Go to the documentation of this file.
1 /* this file is part of evince, a gnome document viewer
2  *
3  * Copyright (C) 2013 Aakash Goenka
4  * Copyright (C) 2014 Carlos Garcia Campos
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 #include "config.h"
21 
22 #include <glib/gi18n.h>
23 #include <gtk/gtk.h>
24 #include <gdk-pixbuf/gdk-pixbuf.h>
25 #include <cairo-gobject.h>
26 
27 #include "ev-recent-view.h"
28 #include "ev-file-helpers.h"
29 #include "gd-icon-utils.h"
30 #include "gd-two-lines-renderer.h"
31 #include "ev-document-misc.h"
32 #include "ev-document-model.h"
33 #include "ev-jobs.h"
34 #include "ev-job-scheduler.h"
35 
36 #ifdef HAVE_LIBGNOME_DESKTOP
37 #define GNOME_DESKTOP_USE_UNSTABLE_API
38 #include <libgnome-desktop/gnome-desktop-thumbnail.h>
39 #endif
40 
41 typedef enum {
49 
51  GtkWidget *view;
52  GtkListStore *model;
53  GtkRecentManager *recent_manager;
54  GtkTreePath *pressed_item_tree_path;
56 
57 #ifdef HAVE_LIBGNOME_DESKTOP
58  GnomeDesktopThumbnailFactory *thumbnail_factory;
59 #endif
60 };
61 
62 enum {
65 };
66 
67 static guint signals[NUM_SIGNALS] = { 0, };
68 
69 G_DEFINE_TYPE (EvRecentView, ev_recent_view, GTK_TYPE_SCROLLED_WINDOW)
70 
71 #define ICON_VIEW_SIZE 128
72 #define MAX_RECENT_VIEW_ITEMS 20
73 
74 typedef struct {
76  char *uri;
77  time_t mtime;
78  GtkTreeRowReference *row;
79  GCancellable *cancellable;
81  guint needs_metadata : 1;
82  guint needs_thumbnail : 1;
84 
85 static void
87 {
88  GtkTreePath *path;
89  GtkTreeIter iter;
90 
91  if (data->job) {
92  g_signal_handlers_disconnect_by_data (data->job, data->ev_recent_view);
93  ev_job_cancel (data->job);
94  g_object_unref (data->job);
95  }
96 
97  g_clear_object (&data->cancellable);
98  g_free (data->uri);
99 
100  path = gtk_tree_row_reference_get_path (data->row);
101  if (path) {
102  gtk_tree_model_get_iter (GTK_TREE_MODEL (data->ev_recent_view->priv->model), &iter, path);
103  gtk_list_store_set (data->ev_recent_view->priv->model, &iter,
105  -1);
106  gtk_tree_path_free (path);
107  }
108  gtk_tree_row_reference_free (data->row);
109 
110  g_slice_free (GetDocumentInfoAsyncData, data);
111 }
112 
113 static gboolean
114 ev_recent_view_clear_async_data (GtkTreeModel *model,
115  GtkTreePath *path,
116  GtkTreeIter *iter,
117  EvRecentView *ev_recent_view)
118 {
120 
121  gtk_tree_model_get (model, iter, EV_RECENT_VIEW_COLUMN_ASYNC_DATA, &data, -1);
122 
123  if (data != NULL)
124  g_cancellable_cancel (data->cancellable);
125 
126  return FALSE;
127 }
128 
129 static void
131 {
132  EvRecentViewPrivate *priv = ev_recent_view->priv;
133 
134  gtk_tree_model_foreach (GTK_TREE_MODEL (priv->model),
135  (GtkTreeModelForeachFunc)ev_recent_view_clear_async_data,
136  ev_recent_view);
137 
138  gtk_list_store_clear (priv->model);
139 }
140 
141 static void
143 {
144  EvRecentView *ev_recent_view = EV_RECENT_VIEW (obj);
145  EvRecentViewPrivate *priv = ev_recent_view->priv;
146 
147  if (priv->model) {
148  ev_recent_view_clear_model (ev_recent_view);
149  g_object_unref (priv->model);
150  priv->model = NULL;
151  }
152 
154  g_signal_handler_disconnect (priv->recent_manager,
157  }
158  priv->recent_manager = NULL;
159 
160 #ifdef HAVE_LIBGNOME_DESKTOP
161  g_clear_object (&priv->thumbnail_factory);
162 #endif
163 
164  G_OBJECT_CLASS (ev_recent_view_parent_class)->dispose (obj);
165 }
166 
167 static gint
168 compare_recent_items (GtkRecentInfo *a,
169  GtkRecentInfo *b)
170 {
171  gboolean has_ev_a, has_ev_b;
172  const gchar *evince = g_get_application_name ();
173 
174  has_ev_a = gtk_recent_info_has_application (a, evince);
175  has_ev_b = gtk_recent_info_has_application (b, evince);
176 
177  if (has_ev_a && has_ev_b) {
178  time_t time_a, time_b;
179 
180  time_a = gtk_recent_info_get_modified (a);
181  time_b = gtk_recent_info_get_modified (b);
182 
183  return (time_b - time_a);
184  } else if (has_ev_a) {
185  return -1;
186  } else if (has_ev_b) {
187  return 1;
188  }
189 
190  return 0;
191 }
192 
193 static gboolean
194 on_query_tooltip_event (GtkWidget *widget,
195  gint x,
196  gint y,
197  gboolean keyboard_tip,
198  GtkTooltip *tooltip,
199  EvRecentView *ev_recent_view)
200 {
201  EvRecentViewPrivate *priv = ev_recent_view->priv;
202  GtkTreeModel *model;
203  GtkTreeIter iter;
204  GtkTreePath *path = NULL;
205  gchar *uri;
206 
207  model = gtk_icon_view_get_model (GTK_ICON_VIEW (priv->view));
208  if (!gtk_icon_view_get_tooltip_context (GTK_ICON_VIEW (priv->view),
209  &x, &y, keyboard_tip,
210  &model, &path, &iter))
211  return FALSE;
212 
213  gtk_tree_model_get (GTK_TREE_MODEL (priv->model), &iter,
215  -1);
216 
217  gtk_tooltip_set_text (tooltip, uri);
218  g_free (uri);
219 
220  gtk_icon_view_set_tooltip_item (GTK_ICON_VIEW (priv->view),
221  tooltip, path);
222  gtk_tree_path_free (path);
223 
224  return TRUE;
225 }
226 
227 static gboolean
228 on_button_release_event (GtkWidget *view,
229  GdkEventButton *event,
230  EvRecentView *ev_recent_view)
231 {
232  EvRecentViewPrivate *priv = ev_recent_view->priv;
233  GtkTreePath *path;
234 
235  /* eat double/triple click events */
236  if (event->type != GDK_BUTTON_RELEASE)
237  return TRUE;
238 
239  if (priv->pressed_item_tree_path == NULL)
240  return FALSE;
241 
242  path = gtk_icon_view_get_path_at_pos (GTK_ICON_VIEW (priv->view), event->x, event->y);
243  if (path == NULL)
244  return FALSE;
245 
246  if (gtk_tree_path_compare (path, priv->pressed_item_tree_path) == 0) {
247  g_clear_pointer (&priv->pressed_item_tree_path, gtk_tree_path_free);
248  gtk_icon_view_item_activated (GTK_ICON_VIEW (priv->view), path);
249  gtk_tree_path_free (path);
250 
251  return TRUE;
252  }
253 
254  g_clear_pointer (&priv->pressed_item_tree_path, gtk_tree_path_free);
255  gtk_tree_path_free (path);
256 
257  return FALSE;
258 }
259 
260 static gboolean
261 on_button_press_event (GtkWidget *view,
262  GdkEventButton *event,
263  EvRecentView *ev_recent_view)
264 {
265  EvRecentViewPrivate *priv = ev_recent_view->priv;
266 
267  g_clear_pointer (&priv->pressed_item_tree_path, gtk_tree_path_free);
268  priv->pressed_item_tree_path =
269  gtk_icon_view_get_path_at_pos (GTK_ICON_VIEW (priv->view), event->x, event->y);
270 
271  return TRUE;
272 }
273 
274 static void
275 on_icon_view_item_activated (GtkIconView *iconview,
276  GtkTreePath *path,
277  EvRecentView *ev_recent_view)
278 {
279  EvRecentViewPrivate *priv = ev_recent_view->priv;
280  GtkTreeIter iter;
281  gchar *uri;
282 
283  if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (priv->model), &iter, path))
284  return;
285 
286  gtk_tree_model_get (GTK_TREE_MODEL (priv->model), &iter,
288  -1);
289  g_signal_emit (ev_recent_view, signals[ITEM_ACTIVATED], 0, uri);
290  g_free (uri);
291 }
292 
293 static void
295  cairo_surface_t *thumbnail)
296 {
297  EvRecentViewPrivate *priv = data->ev_recent_view->priv;
298  GtkTreePath *path;
299  GtkTreeIter iter;
300  GtkBorder border;
301  cairo_surface_t *surface;
302 
303  data->needs_thumbnail = FALSE;
304 
305  border.left = 4;
306  border.right = 3;
307  border.top = 3;
308  border.bottom = 6;
309 
310  surface = gd_embed_image_in_frame (thumbnail,
311  "resource:///org/gnome/evince/shell/ui/thumbnail-frame.png",
312  &border, &border);
313 
314  path = gtk_tree_row_reference_get_path (data->row);
315  if (path != NULL) {
316  gtk_tree_model_get_iter (GTK_TREE_MODEL (priv->model), &iter, path);
317  gtk_list_store_set (priv->model, &iter,
319  -1);
320  gtk_tree_path_free (path);
321  }
322 
323  cairo_surface_destroy (surface);
324 }
325 
326 #ifdef HAVE_LIBGNOME_DESKTOP
327 static void
328 ev_rencent_view_ensure_desktop_thumbnail_factory (EvRecentView *ev_recent_view)
329 {
330  if (ev_recent_view->priv->thumbnail_factory)
331  return;
332 
333  ev_recent_view->priv->thumbnail_factory = gnome_desktop_thumbnail_factory_new (GNOME_DESKTOP_THUMBNAIL_SIZE_LARGE);
334 }
335 
336 static void
337 save_thumbnail_in_cache_thread (GTask *task,
338  EvRecentView *ev_recent_view,
340  GCancellable *cancellable)
341 {
342  GdkPixbuf *thumbnail;
343  cairo_surface_t *surface;
344 
345  surface = EV_JOB_THUMBNAIL (data->job)->thumbnail_surface;
346  thumbnail = gdk_pixbuf_get_from_surface (surface, 0, 0,
347  cairo_image_surface_get_width (surface),
348  cairo_image_surface_get_height (surface));
349 
350  gnome_desktop_thumbnail_factory_save_thumbnail (ev_recent_view->priv->thumbnail_factory,
351  thumbnail, data->uri, data->mtime);
352  g_object_unref (thumbnail);
353 
354  g_task_return_boolean (task, TRUE);
355 }
356 
357 static void
358 save_thumbnail_in_cache_cb (EvRecentView *ev_recent_view,
359  GAsyncResult *result,
361 {
363 }
364 #endif /* HAVE_LIBGNOME_DESKTOP */
365 
366 static void
368 {
369 #ifdef HAVE_LIBGNOME_DESKTOP
370  GTask *task;
371 
372  ev_rencent_view_ensure_desktop_thumbnail_factory (data->ev_recent_view);
373  task = g_task_new (data->ev_recent_view, data->cancellable,
374  (GAsyncReadyCallback)save_thumbnail_in_cache_cb, data);
375  g_task_set_task_data (task, data, NULL);
376  g_task_run_in_thread (task, (GTaskThreadFunc)save_thumbnail_in_cache_thread);
377  g_object_unref (task);
378 #else
380 #endif /* HAVE_LIBGNOME_DESKTOP */
381 }
382 
383 static void
386 {
387  if (g_cancellable_is_cancelled (data->cancellable) ||
388  ev_job_is_failed (EV_JOB (job))) {
390  return;
391  }
392 
395 }
396 
397 static void
400 {
401  EvRecentViewPrivate *priv = data->ev_recent_view->priv;
402  EvDocument *document = EV_JOB (job_load)->document;
403 
404  if (g_cancellable_is_cancelled (data->cancellable) ||
405  ev_job_is_failed (EV_JOB (job_load))) {
407  return;
408  }
409 
410  g_clear_object (&data->job);
411 
412  if (data->needs_thumbnail) {
413  gdouble width, height;
414  gint target_width, target_height;
415 
416  ev_document_get_page_size (document, 0, &width, &height);
417  if (height < width) {
418  target_width = ICON_VIEW_SIZE;
419  target_height = (int)(ICON_VIEW_SIZE * height / width + 0.5);
420  } else {
421  target_width = (int)(ICON_VIEW_SIZE * width / height + 0.5);
422  target_height = ICON_VIEW_SIZE;
423  }
424 
425  data->job = ev_job_thumbnail_new_with_target_size (document, 0, 0, target_width, target_height);
428  g_signal_connect (data->job, "finished",
430  data);
432  }
433 
434  if (data->needs_metadata) {
435  const EvDocumentInfo *info;
436  GtkTreePath *path;
437  GtkTreeIter iter;
438  GFile *file;
439  GFileInfo *file_info = g_file_info_new ();
440 
441  path = gtk_tree_row_reference_get_path (data->row);
442  if (path)
443  gtk_tree_model_get_iter (GTK_TREE_MODEL (priv->model), &iter, path);
444 
445  info = ev_document_get_info (document);
446  if (info->fields_mask & EV_DOCUMENT_INFO_TITLE && info->title && info->title[0] != '\0') {
447  if (path) {
448  gtk_list_store_set (priv->model, &iter,
450  -1);
451  }
452  g_file_info_set_attribute_string (file_info, "metadata::evince::title", info->title);
453  } else {
454  g_file_info_set_attribute_string (file_info, "metadata::evince::title", "");
455  }
456  if (info->fields_mask & EV_DOCUMENT_INFO_AUTHOR && info->author && info->author[0] != '\0') {
457  if (path) {
458  gtk_list_store_set (priv->model, &iter,
460  -1);
461  }
462  g_file_info_set_attribute_string (file_info, "metadata::evince::author", info->author);
463  } else {
464  g_file_info_set_attribute_string (file_info, "metadata::evince::author", "");
465  }
466 
467  gtk_tree_path_free (path);
468 
469  file = g_file_new_for_uri (data->uri);
470  g_file_set_attributes_async (file, file_info, G_FILE_QUERY_INFO_NONE, G_PRIORITY_DEFAULT, NULL, NULL, NULL);
471  g_object_unref (file);
472  }
473 
474  if (!data->job)
476 }
477 
478 static void
480 {
481  data->job = EV_JOB (ev_job_load_new (data->uri));
482  g_signal_connect (data->job, "finished",
484  data);
486 }
487 
488 #ifdef HAVE_LIBGNOME_DESKTOP
489 static void
490 get_thumbnail_from_cache_thread (GTask *task,
491  EvRecentView *ev_recent_view,
493  GCancellable *cancellable)
494 {
495  GFile *file;
496  GFileInfo *info;
497  gchar *path;
498  GdkPixbuf *thumbnail;
499  cairo_surface_t *surface = NULL;
500 
501  if (g_task_return_error_if_cancelled (task))
502  return;
503 
504  file = g_file_new_for_uri (data->uri);
505  info = g_file_query_info (file, G_FILE_ATTRIBUTE_TIME_MODIFIED, G_FILE_QUERY_INFO_NONE,
506  data->cancellable, NULL);
507  g_object_unref (file);
508 
509  if (!info) {
510  g_task_return_pointer (task, NULL, NULL);
511  return;
512  }
513 
514  data->mtime = g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_TIME_MODIFIED);
515  g_object_unref (info);
516 
517  path = gnome_desktop_thumbnail_factory_lookup (ev_recent_view->priv->thumbnail_factory,
518  data->uri, data->mtime);
519  if (!path) {
520  g_task_return_pointer (task, NULL, NULL);
521  return;
522  }
523 
524  thumbnail = gdk_pixbuf_new_from_file (path, NULL);
525  g_free (path);
526 
527  if (thumbnail) {
528  gint width, height;
529  gint target_width, target_height;
530 
531  width = gdk_pixbuf_get_width (thumbnail);
532  height = gdk_pixbuf_get_height (thumbnail);
533 
534  if (height < width) {
535  target_width = ICON_VIEW_SIZE;
536  target_height = (int)(ICON_VIEW_SIZE * height / width + 0.5);
537  } else {
538  target_width = (int)(ICON_VIEW_SIZE * width / height + 0.5);
539  target_height = ICON_VIEW_SIZE;
540  }
541 
542  if (width < target_width || height < target_height) {
543  GdkPixbuf *scaled;
544 
545  scaled = gdk_pixbuf_scale_simple (thumbnail,
546  target_width,
547  target_height,
548  GDK_INTERP_TILES);
549  g_object_unref (thumbnail);
550  thumbnail = scaled;
551  } else if (width != target_width || height != target_height) {
552  GdkPixbuf *scaled;
553 
554  scaled = gdk_pixbuf_scale_simple (thumbnail,
555  target_width,
556  target_height,
557  GDK_INTERP_HYPER);
558  g_object_unref (thumbnail);
559  thumbnail = scaled;
560  }
561 
562  surface = ev_document_misc_surface_from_pixbuf (thumbnail);
563  g_object_unref (thumbnail);
564  }
565 
566  g_task_return_pointer (task, surface, (GDestroyNotify)cairo_surface_destroy);
567 }
568 
569 static void
570 get_thumbnail_from_cache_cb (EvRecentView *ev_recent_view,
571  GAsyncResult *result,
573 {
574  GTask *task = G_TASK (result);
575  cairo_surface_t *thumbnail;
576 
577  if (g_cancellable_is_cancelled (data->cancellable)) {
579  return;
580  }
581 
582  thumbnail = g_task_propagate_pointer (task, NULL);
583  if (thumbnail) {
584  add_thumbnail_to_model (data, thumbnail);
585  cairo_surface_destroy (thumbnail);
586  }
587 
588  if (data->needs_metadata || data->needs_thumbnail)
590  else
592 }
593 #endif /* HAVE_LIBGNOME_DESKTOP */
594 
595 static void
597 {
598 #ifdef HAVE_LIBGNOME_DESKTOP
599  GTask *task;
600 
601  ev_rencent_view_ensure_desktop_thumbnail_factory (data->ev_recent_view);
602  task = g_task_new (data->ev_recent_view, data->cancellable,
603  (GAsyncReadyCallback)get_thumbnail_from_cache_cb, data);
604  g_task_set_task_data (task, data, NULL);
605  g_task_run_in_thread (task, (GTaskThreadFunc)get_thumbnail_from_cache_thread);
606  g_object_unref (task);
607 #else
609 #endif /* HAVE_LIBGNOME_DESKTOP */
610 }
611 
612 static void
614 {
615  if (data->needs_thumbnail) {
617  return;
618  }
619 
620  if (data->needs_metadata) {
622  return;
623  }
624 
626 }
627 
628 static void
630  GAsyncResult *result,
632 {
633  GFileInfo *info;
634  const char *title = NULL;
635  const char *author = NULL;
636  char **attrs;
637  guint i;
638 
639  if (g_cancellable_is_cancelled (data->cancellable)) {
641  return;
642  }
643 
644  info = g_file_query_info_finish (file, result, NULL);
645  if (!info) {
646  get_document_info (data);
647  return;
648  }
649 
650  if (!g_file_info_has_namespace (info, "metadata")) {
651  get_document_info (data);
652  g_object_unref (info);
653 
654  return;
655  }
656 
657  attrs = g_file_info_list_attributes (info, "metadata");
658  for (i = 0; attrs[i]; i++) {
659  if (g_str_equal (attrs[i], "metadata::evince::title")) {
660  title = g_file_info_get_attribute_string (info, attrs[i]);
661  } else if (g_str_equal (attrs[i], "metadata::evince::author")) {
662  author = g_file_info_get_attribute_string (info, attrs[i]);
663  }
664 
665  if (title && author)
666  break;
667  }
668  g_strfreev (attrs);
669 
670  if (title || author) {
671  GtkTreePath *path;
672 
673  data->needs_metadata = FALSE;
674 
675  path = gtk_tree_row_reference_get_path (data->row);
676  if (path) {
677  GtkTreeIter iter;
678 
679  gtk_tree_model_get_iter (GTK_TREE_MODEL (data->ev_recent_view->priv->model), &iter, path);
680 
681  if (title && title[0] != '\0') {
682  gtk_list_store_set (data->ev_recent_view->priv->model, &iter,
684  -1);
685  }
686 
687  if (author && author[0] != '\0') {
688  gtk_list_store_set (data->ev_recent_view->priv->model, &iter,
690  -1);
691  }
692 
693  gtk_tree_path_free (path);
694  }
695  }
696 
697  g_object_unref (info);
698 
699  get_document_info (data);
700 }
701 
704  const gchar *uri,
705  GtkTreePath *path)
706 {
707  GFile *file;
709 
710  data = g_slice_new0 (GetDocumentInfoAsyncData);
711  data->ev_recent_view = ev_recent_view;
712  data->uri = g_strdup (uri);
713  data->row = gtk_tree_row_reference_new (GTK_TREE_MODEL (ev_recent_view->priv->model), path);;
714  data->cancellable = g_cancellable_new ();
715  data->needs_metadata = TRUE;
716  data->needs_thumbnail = TRUE;
717 
718  file = g_file_new_for_uri (uri);
719  g_file_query_info_async (file, "metadata::*", G_FILE_QUERY_INFO_NONE,
720  G_PRIORITY_DEFAULT, data->cancellable,
721  (GAsyncReadyCallback)document_query_info_cb,
722  data);
723  g_object_unref (file);
724 
725  return data;
726 }
727 
728 static void
730 {
731  GList *items, *l;
732  guint n_items = 0;
733  const gchar *evince = g_get_application_name ();
734  EvRecentViewPrivate *priv = ev_recent_view->priv;
735 
736  items = gtk_recent_manager_get_items (priv->recent_manager);
737  items = g_list_sort (items, (GCompareFunc) compare_recent_items);
738 
739  gtk_list_store_clear (priv->model);
740 
741  for (l = items; l && l->data; l = g_list_next (l)) {
743  GtkTreePath *path;
744  GtkRecentInfo *info;
745  const gchar *uri;
746  GdkPixbuf *pixbuf;
747  cairo_surface_t *thumbnail = NULL;
748  GtkTreeIter iter;
749 
750  info = (GtkRecentInfo *) l->data;
751 
752  if (!gtk_recent_info_has_application (info, evince))
753  continue;
754 
755  if (gtk_recent_info_is_local (info) && !gtk_recent_info_exists (info))
756  continue;
757 
758  uri = gtk_recent_info_get_uri (info);
759  pixbuf = gtk_recent_info_get_icon (info, ICON_VIEW_SIZE);
760  if (pixbuf) {
761  thumbnail = ev_document_misc_surface_from_pixbuf (pixbuf);
762  g_object_unref (pixbuf);
763  }
764 
765  gtk_list_store_append (priv->model, &iter);
766  path = gtk_tree_model_get_path (GTK_TREE_MODEL (priv->model), &iter);
767  data = ev_recent_view_get_document_info (ev_recent_view, uri, path);
768  gtk_tree_path_free (path);
769 
770  gtk_list_store_set (priv->model, &iter,
772  EV_RECENT_VIEW_COLUMN_PRIMARY_TEXT, gtk_recent_info_get_display_name (info),
774  EV_RECENT_VIEW_COLUMN_ICON, thumbnail,
776  -1);
777 
778  if (thumbnail != NULL)
779  cairo_surface_destroy (thumbnail);
780 
781  if (++n_items == MAX_RECENT_VIEW_ITEMS)
782  break;
783  }
784 
785  g_list_free_full (items, (GDestroyNotify)gtk_recent_info_unref);
786 }
787 
788 static void
789 ev_recent_view_constructed (GObject *object)
790 {
791  EvRecentView *ev_recent_view = EV_RECENT_VIEW (object);
792  EvRecentViewPrivate *priv = ev_recent_view->priv;
793  GtkCellRenderer *renderer;
794 
795  G_OBJECT_CLASS (ev_recent_view_parent_class)->constructed (object);
796 
797  priv->view = gtk_icon_view_new_with_model (GTK_TREE_MODEL (priv->model));
798 
799  gtk_icon_view_set_column_spacing (GTK_ICON_VIEW (priv->view), 20);
800  gtk_icon_view_set_margin (GTK_ICON_VIEW (priv->view), 16);
801  gtk_icon_view_set_selection_mode (GTK_ICON_VIEW (priv->view), GTK_SELECTION_NONE);
802  gtk_widget_set_hexpand (priv->view, TRUE);
803  gtk_widget_set_vexpand (priv->view, TRUE);
804  gtk_widget_set_has_tooltip (priv->view, TRUE);
805 
806  renderer = gtk_cell_renderer_pixbuf_new ();
807  g_object_set (renderer, "xalign", 0.5, "yalign", 0.5, NULL);
808 
809  gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (priv->view), renderer, FALSE);
810  gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (priv->view), renderer,
811  "surface", EV_RECENT_VIEW_COLUMN_ICON);
812 
813  renderer = gd_two_lines_renderer_new ();
814  g_object_set (renderer,
815  "xalign", 0.5,
816  "alignment", PANGO_ALIGN_CENTER,
817  "wrap-mode", PANGO_WRAP_WORD_CHAR,
818  "wrap-width", 128,
819  "text-lines", 3,
820  NULL);
821  gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (priv->view), renderer, FALSE);
822  gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (priv->view), renderer,
824  gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (priv->view), renderer,
826 
827  g_signal_connect (priv->view, "button-press-event",
828  G_CALLBACK (on_button_press_event),
829  ev_recent_view);
830  g_signal_connect (priv->view, "button-release-event",
831  G_CALLBACK (on_button_release_event),
832  ev_recent_view);
833  g_signal_connect (priv->view, "item-activated",
834  G_CALLBACK (on_icon_view_item_activated),
835  ev_recent_view);
836  g_signal_connect (priv->view, "query-tooltip",
837  G_CALLBACK (on_query_tooltip_event),
838  ev_recent_view);
839 
840  gtk_style_context_add_class (gtk_widget_get_style_context (priv->view), "content-view");
841  gtk_container_add (GTK_CONTAINER (ev_recent_view), priv->view);
842  gtk_widget_show (priv->view);
843 
844  ev_recent_view_refresh (ev_recent_view);
845 }
846 
847 static void
849 {
850  EvRecentViewPrivate *priv;
851 
852  ev_recent_view->priv = G_TYPE_INSTANCE_GET_PRIVATE (ev_recent_view, EV_TYPE_RECENT_VIEW, EvRecentViewPrivate);
853 
854  priv = ev_recent_view->priv;
855  priv->recent_manager = gtk_recent_manager_get_default ();
856  priv->model = gtk_list_store_new (NUM_COLUMNS,
857  G_TYPE_STRING,
858  G_TYPE_STRING,
859  G_TYPE_STRING,
860  CAIRO_GOBJECT_TYPE_SURFACE,
861  G_TYPE_POINTER);
862 
863  gtk_widget_set_hexpand (GTK_WIDGET (ev_recent_view), TRUE);
864  gtk_widget_set_vexpand (GTK_WIDGET (ev_recent_view), TRUE);
865  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (ev_recent_view),
866  GTK_POLICY_NEVER,
867  GTK_POLICY_AUTOMATIC);
869  g_signal_connect_swapped (priv->recent_manager,
870  "changed",
871  G_CALLBACK (ev_recent_view_refresh),
872  ev_recent_view);
873 }
874 
875 static void
877 {
878  GObjectClass *g_object_class = G_OBJECT_CLASS (klass);
879 
880  g_object_class->constructed = ev_recent_view_constructed;
881  g_object_class->dispose = ev_recent_view_dispose;
882 
884  g_signal_new ("item-activated",
886  G_SIGNAL_RUN_LAST,
887  0, NULL, NULL,
888  g_cclosure_marshal_generic,
889  G_TYPE_NONE, 1,
890  G_TYPE_STRING);
891 
892  g_type_class_add_private (klass, sizeof (EvRecentViewPrivate));
893 }
894 
895 GtkWidget *
897 {
898  return GTK_WIDGET (g_object_new (EV_TYPE_RECENT_VIEW, NULL));
899 }