31 #include <glib/gi18n.h>
34 #include <cairo-gobject.h>
43 #define THUMBNAIL_WIDTH 100
47 #define MAX_ICON_VIEW_PAGE_COUNT 1500
109 ev_sidebar_thumbnails,
115 #define EV_SIDEBAR_THUMBNAILS_GET_PRIVATE(object) \
116 (G_TYPE_INSTANCE_GET_PRIVATE ((object), EV_TYPE_SIDEBAR_THUMBNAILS, EvSidebarThumbnailsPrivate));
119 #define EV_THUMBNAILS_SIZE_CACHE_KEY "ev-thumbnails-size-cache"
122 get_thumbnail_size_for_page (
EvDocument *document,
133 *width = MAX ((gint)(w * scale + 0.5), 1);
134 *height = MAX ((gint)(h * scale + 0.5), 1);
148 get_thumbnail_size_for_page (document, 0,
157 for (i = 0; i < n_pages; i++) {
158 thumb_size = &(cache->
sizes[i]);
159 get_thumbnail_size_for_page (document, i,
182 thumb_size = &(cache->
sizes[page]);
184 w = thumb_size->
width;
188 if (rotation == 0 || rotation == 180) {
189 if (width) *width = w;
190 if (height) *height = h;
192 if (width) *width = h;
193 if (height) *height = w;
201 g_free (cache->
sizes);
216 g_object_set_data_full (G_OBJECT (document),
230 GtkTreePath *start, *end;
233 if (sidebar->priv->tree_view) {
234 GtkTreeSelection *selection;
237 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (sidebar->priv->tree_view));
238 if (!gtk_tree_selection_get_selected (selection, NULL, &iter))
241 path = gtk_tree_model_get_path (GTK_TREE_MODEL (sidebar->priv->list_store), &iter);
242 if (!gtk_tree_view_get_visible_range (GTK_TREE_VIEW (sidebar->priv->tree_view), &start, &end)) {
243 gtk_tree_path_free (path);
249 selection = gtk_icon_view_get_selected_items (GTK_ICON_VIEW (sidebar->priv->icon_view));
253 path = (GtkTreePath *)selection->data;
256 g_assert (selection->next == NULL);
257 g_list_free (selection);
259 if (!gtk_icon_view_get_visible_range (GTK_ICON_VIEW (sidebar->priv->icon_view), &start, &end)) {
260 gtk_tree_path_free (path);
265 retval = gtk_tree_path_compare (path, start) >= 0 && gtk_tree_path_compare (path, end) <= 0;
266 gtk_tree_path_free (path);
267 gtk_tree_path_free (start);
268 gtk_tree_path_free (end);
278 if (sidebar_thumbnails->priv->loading_icons) {
279 g_hash_table_destroy (sidebar_thumbnails->priv->loading_icons);
280 sidebar_thumbnails->priv->loading_icons = NULL;
283 if (sidebar_thumbnails->priv->list_store) {
285 g_object_unref (sidebar_thumbnails->priv->list_store);
286 sidebar_thumbnails->priv->list_store = NULL;
289 G_OBJECT_CLASS (ev_sidebar_thumbnails_parent_class)->dispose (
object);
302 if (sidebar->priv->tree_view)
303 g_value_set_object (value, sidebar->priv->tree_view);
305 g_value_set_object (value, sidebar->priv->icon_view);
308 G_OBJECT_WARN_INVALID_PROPERTY_ID (
object, prop_id, pspec);
320 GTK_WIDGET_CLASS (ev_sidebar_thumbnails_parent_class)->map (widget);
339 g_signal_emit_by_name (sidebar->priv->vadjustment,
"value-changed");
344 GtkAllocation *allocation)
348 GTK_WIDGET_CLASS (ev_sidebar_thumbnails_parent_class)->size_allocate (widget, allocation);
350 if (allocation->width != sidebar->priv->width) {
353 sidebar->priv->width = allocation->width;
356 if (!sidebar->priv->model)
368 GObjectClass *g_object_class;
369 GtkWidgetClass *widget_class;
371 g_object_class = G_OBJECT_CLASS (ev_sidebar_thumbnails_class);
372 widget_class = GTK_WIDGET_CLASS (ev_sidebar_thumbnails_class);
379 #if GTK_CHECK_VERSION(3, 20, 0)
380 gtk_widget_class_set_css_name (widget_class,
"evsidebarthumbnails");
383 g_object_class_override_property (g_object_class,
393 GtkWidget *ev_sidebar_thumbnails;
396 "orientation", GTK_ORIENTATION_VERTICAL,
400 return ev_sidebar_thumbnails;
403 static cairo_surface_t *
409 cairo_surface_t *icon;
412 key = g_strdup_printf (
"%dx%d", width, height);
415 gboolean inverted_colors;
416 gint device_scale = 1;
418 #ifdef HAVE_HIDPI_SUPPORT
419 device_scale = gtk_widget_get_scale_factor (GTK_WIDGET (sidebar_thumbnails));
424 width * device_scale,
425 height * device_scale,
445 g_assert (start_page <= end_page);
447 path = gtk_tree_path_new_from_indices (start_page, -1);
448 for (result = gtk_tree_model_get_iter (GTK_TREE_MODEL (priv->
list_store), &iter, path);
449 result && start_page <= end_page;
450 result = gtk_tree_model_iter_next (GTK_TREE_MODEL (priv->
list_store), &iter), start_page ++) {
452 gboolean thumbnail_set;
454 gtk_tree_model_get (GTK_TREE_MODEL (priv->
list_store),
461 g_assert (job == NULL);
468 g_object_unref (job);
476 gtk_tree_path_free (path);
486 gdouble width, height;
487 gint thumbnail_height;
488 gint device_scale = 1;
490 #ifdef HAVE_HIDPI_SUPPORT
491 device_scale = gtk_widget_get_scale_factor (GTK_WIDGET (sidebar_thumbnails));
497 *width_return = thumbnail_height * device_scale;
501 *height_return = thumbnail_height * device_scale;
514 gint page = start_page;
516 g_assert (start_page <= end_page);
518 path = gtk_tree_path_new_from_indices (start_page, -1);
519 for (result = gtk_tree_model_get_iter (GTK_TREE_MODEL (priv->
list_store), &iter, path);
520 result && page <= end_page;
521 result = gtk_tree_model_iter_next (GTK_TREE_MODEL (priv->
list_store), &iter), page ++) {
523 gboolean thumbnail_set;
525 gtk_tree_model_get (GTK_TREE_MODEL (priv->
list_store), &iter,
530 if (job == NULL && !thumbnail_set) {
531 gint thumbnail_width, thumbnail_height;
532 get_size_for_page (sidebar_thumbnails, page, &thumbnail_width, &thumbnail_height);
536 thumbnail_width, thumbnail_height);
539 g_object_set_data_full (G_OBJECT (job),
"tree_iter",
540 gtk_tree_iter_copy (&iter),
541 (GDestroyNotify) gtk_tree_iter_free);
542 g_signal_connect (job,
"finished",
551 g_object_unref (job);
553 g_object_unref (job);
556 gtk_tree_path_free (path);
566 int old_start_page, old_end_page;
567 int n_pages_in_visible_range;
572 n_pages_in_visible_range = (end_page - start_page) + 1;
573 start_page = MAX (0, start_page - n_pages_in_visible_range);
574 end_page = MIN (priv->
n_pages - 1, end_page + n_pages_in_visible_range);
579 if (start_page == old_start_page &&
580 end_page == old_end_page)
584 if (old_start_page >= 0 && old_start_page < start_page)
585 cancel_running_jobs (sidebar_thumbnails, old_start_page, MIN (start_page - 1, old_end_page));
587 if (old_end_page > 0 && old_end_page > end_page)
588 cancel_running_jobs (sidebar_thumbnails, MAX (end_page + 1, old_start_page), old_end_page);
590 add_range (sidebar_thumbnails, start_page, end_page);
600 GtkTreePath *path = NULL;
601 GtkTreePath *path2 = NULL;
608 if (!gtk_widget_get_mapped (GTK_WIDGET (sidebar_thumbnails)))
611 page_size = gtk_adjustment_get_page_size (priv->
vadjustment);
616 value = gtk_adjustment_get_value (priv->
vadjustment);
619 if (! gtk_widget_get_realized (priv->
tree_view))
622 gtk_tree_view_convert_tree_to_bin_window_coords (GTK_TREE_VIEW (priv->
tree_view),
625 gtk_tree_view_convert_tree_to_bin_window_coords (GTK_TREE_VIEW (priv->
tree_view),
626 0, (
int) (value + page_size),
628 gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (priv->
tree_view),
631 gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (priv->
tree_view),
635 if (! gtk_widget_get_realized (priv->
icon_view))
637 if (! gtk_icon_view_get_visible_range (GTK_ICON_VIEW (priv->
icon_view), &path, &path2))
645 gtk_tree_path_get_indices (path)[0],
646 gtk_tree_path_get_indices (path2)[0]);
649 gtk_tree_path_free (path);
650 gtk_tree_path_free (path2);
659 gint prev_width = -1;
660 gint prev_height = -1;
662 for (i = 0; i < sidebar_thumbnails->priv->n_pages; i++) {
665 cairo_surface_t *loading_icon = NULL;
669 page_string = g_markup_printf_escaped (
"<i>%s</i>", page_label);
671 sidebar_thumbnails->priv->rotation,
673 if (!loading_icon || (width != prev_width && height != prev_height)) {
680 prev_height = height;
682 gtk_list_store_append (priv->
list_store, &iter);
689 g_free (page_string);
702 if (!gtk_tree_selection_get_selected (selection, NULL, &iter))
705 path = gtk_tree_model_get_path (GTK_TREE_MODEL (priv->
list_store),
707 page = gtk_tree_path_get_indices (path)[0];
708 gtk_tree_path_free (path);
722 selected = gtk_icon_view_get_selected_items (icon_view);
723 if (selected == NULL)
727 g_assert (selected->next == NULL);
729 path = selected->data;
730 page = gtk_tree_path_get_indices (path)[0];
732 gtk_tree_path_free (path);
733 g_list_free (selected);
742 GtkTreeSelection *selection;
743 GtkCellRenderer *renderer;
745 priv = ev_sidebar_thumbnails->priv;
748 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->
tree_view));
749 g_signal_connect (selection,
"changed",
751 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (priv->
tree_view),
FALSE);
752 renderer = g_object_new (GTK_TYPE_CELL_RENDERER_PIXBUF,
756 gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (priv->
tree_view), -1,
760 gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (priv->
tree_view), -1,
761 NULL, gtk_cell_renderer_text_new (),
771 GtkCellRenderer *renderer;
773 priv = ev_sidebar_thumbnails->priv;
777 renderer = g_object_new (GTK_TYPE_CELL_RENDERER_PIXBUF,
781 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (priv->
icon_view), renderer,
FALSE);
782 gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (priv->
icon_view),
783 renderer,
"surface", 1, NULL);
785 renderer = g_object_new (GTK_TYPE_CELL_RENDERER_TEXT,
786 "alignment", PANGO_ALIGN_CENTER,
787 "wrap-mode", PANGO_WRAP_WORD_CHAR,
793 gtk_cell_layout_pack_end (GTK_CELL_LAYOUT (priv->
icon_view), renderer,
FALSE);
794 gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (priv->
icon_view),
795 renderer,
"markup", 0, NULL);
796 g_signal_connect (priv->
icon_view,
"selection-changed",
827 signal_id = GPOINTER_TO_UINT (data);
832 g_signal_stop_emission (model, signal_id, 0);
845 CAIRO_GOBJECT_TYPE_SURFACE,
849 signal_id = g_signal_lookup (
"row-changed", GTK_TYPE_TREE_MODEL);
850 g_signal_connect (GTK_TREE_MODEL (priv->
list_store),
"row-changed",
852 GUINT_TO_POINTER (signal_id));
854 priv->
swindow = gtk_scrolled_window_new (NULL, NULL);
856 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->
swindow),
857 GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
858 priv->
vadjustment = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (priv->
swindow));
859 g_signal_connect_data (priv->
vadjustment,
"value-changed",
861 ev_sidebar_thumbnails, NULL,
862 G_CONNECT_SWAPPED | G_CONNECT_AFTER);
863 g_signal_connect_swapped (priv->
swindow,
"size-allocate",
865 ev_sidebar_thumbnails);
866 gtk_box_pack_start (GTK_BOX (ev_sidebar_thumbnails), priv->
swindow,
TRUE,
TRUE, 0);
868 g_signal_connect (ev_sidebar_thumbnails,
"notify::scale-factor",
872 gtk_widget_show_all (priv->
swindow);
879 GtkTreeView *tree_view;
882 path = gtk_tree_path_new_from_indices (page, -1);
884 if (sidebar->priv->tree_view) {
885 tree_view = GTK_TREE_VIEW (sidebar->priv->tree_view);
886 gtk_tree_view_set_cursor (tree_view, path, NULL,
FALSE);
887 gtk_tree_view_scroll_to_cell (tree_view, path, NULL,
FALSE, 0.0, 0.0);
888 }
else if (sidebar->priv->icon_view) {
890 g_signal_handlers_block_by_func
891 (sidebar->priv->icon_view,
894 gtk_icon_view_select_path (GTK_ICON_VIEW (sidebar->priv->icon_view), path);
896 g_signal_handlers_unblock_by_func
897 (sidebar->priv->icon_view,
900 gtk_icon_view_scroll_to_path (GTK_ICON_VIEW (sidebar->priv->icon_view), path,
FALSE, 0.0, 0.0);
903 gtk_tree_path_free (path);
926 if (sidebar_thumbnails->priv->loading_icons)
927 g_hash_table_remove_all (sidebar_thumbnails->priv->loading_icons);
929 if (sidebar_thumbnails->priv->document == NULL ||
930 sidebar_thumbnails->priv->n_pages <= 0)
933 model = sidebar_thumbnails->priv->model;
939 sidebar_thumbnails->priv->start_page = -1;
940 sidebar_thumbnails->priv->end_page = -1;
943 g_idle_add ((GSourceFunc)
refresh, sidebar_thumbnails);
953 sidebar_thumbnails->priv->rotation = rotation;
964 sidebar_thumbnails->priv->inverted_colors = inverted_colors;
972 GtkWidget *widget = GTK_WIDGET (sidebar_thumbnails);
975 cairo_surface_t *surface;
976 #ifdef HAVE_HIDPI_SUPPORT
983 #ifdef HAVE_HIDPI_SUPPORT
984 device_scale = gtk_widget_get_scale_factor (widget);
985 cairo_surface_set_device_scale (job->
thumbnail_surface, device_scale, device_scale);
992 iter = (GtkTreeIter *) g_object_get_data (G_OBJECT (job),
"tree_iter");
1001 cairo_surface_destroy (surface);
1003 gtk_widget_queue_draw (priv->
icon_view);
1029 (GDestroyNotify)g_free,
1030 (GDestroyNotify)cairo_surface_destroy);
1045 g_object_notify (G_OBJECT (sidebar_thumbnails),
"main_widget");
1047 gtk_widget_queue_resize (priv->
icon_view);
1057 g_object_notify (G_OBJECT (sidebar_thumbnails),
"main_widget");
1062 g_signal_connect_swapped (priv->
model,
"page-changed",
1064 sidebar_thumbnails);
1065 g_signal_connect (priv->
model,
"notify::rotation",
1067 sidebar_thumbnails);
1068 g_signal_connect (priv->
model,
"notify::inverted-colors",
1070 sidebar_thumbnails);
1071 g_signal_connect_swapped (priv->
model,
"notify::fullscreen",
1073 sidebar_thumbnails);
1074 sidebar_thumbnails->priv->start_page = -1;
1075 sidebar_thumbnails->priv->end_page = -1;
1088 if (priv->
model == model)
1091 priv->
model = model;
1092 g_signal_connect (model,
"notify::document",
1105 gtk_tree_model_get (model, iter,
COLUMN_JOB, &job, -1);
1110 g_object_unref (job);
1135 return _(
"Thumbnails");