]> www.fi.muni.cz Git - evince.git/blob - shell/ev-sidebar-layers.c
[dualscreen] fix crash on ctrl+w and fix control window closing
[evince.git] / shell / ev-sidebar-layers.c
1 /* ev-sidebar-layers.c
2  *  this file is part of evince, a gnome document viewer
3  *
4  * Copyright (C) 2008 Carlos Garcia Campos  <carlosgc@gnome.org>
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 <glib/gi18n.h>
24 #include "gimpcellrenderertoggle.h"
25
26 #include "ev-document-layers.h"
27 #include "ev-sidebar-page.h"
28 #include "ev-jobs.h"
29 #include "ev-job-scheduler.h"
30 #include "ev-stock-icons.h"
31 #include "ev-sidebar-layers.h"
32
33 struct _EvSidebarLayersPrivate {
34         GtkTreeView  *tree_view;
35
36         EvDocument   *document;
37         EvJob        *job;
38 };
39
40 enum {
41         PROP_0,
42         PROP_WIDGET
43 };
44
45 enum {
46         LAYERS_VISIBILITY_CHANGED,
47         N_SIGNALS
48 };
49
50 static void ev_sidebar_layers_page_iface_init (EvSidebarPageInterface *iface);
51 static void job_finished_callback             (EvJobLayers            *job,
52                                                EvSidebarLayers        *sidebar_layers);
53
54 static guint signals[N_SIGNALS];
55
56 G_DEFINE_TYPE_EXTENDED (EvSidebarLayers,
57                         ev_sidebar_layers,
58                         GTK_TYPE_VBOX,
59                         0, 
60                         G_IMPLEMENT_INTERFACE (EV_TYPE_SIDEBAR_PAGE, 
61                                                ev_sidebar_layers_page_iface_init))
62
63 #define EV_SIDEBAR_LAYERS_GET_PRIVATE(object) \
64         (G_TYPE_INSTANCE_GET_PRIVATE ((object), EV_TYPE_SIDEBAR_LAYERS, EvSidebarLayersPrivate))
65
66 static void
67 ev_sidebar_layers_dispose (GObject *object)
68 {
69         EvSidebarLayers *sidebar = EV_SIDEBAR_LAYERS (object);
70
71         if (sidebar->priv->job) {
72                 g_signal_handlers_disconnect_by_func (sidebar->priv->job,
73                                                       job_finished_callback,
74                                                       sidebar);
75                 ev_job_cancel (sidebar->priv->job);                                                   
76                 g_object_unref (sidebar->priv->job);
77                 sidebar->priv->job = NULL;
78         }
79
80         if (sidebar->priv->document) {
81                 g_object_unref (sidebar->priv->document);
82                 sidebar->priv->document = NULL;
83         }
84
85         G_OBJECT_CLASS (ev_sidebar_layers_parent_class)->dispose (object);
86 }
87
88 static void
89 ev_sidebar_layers_get_property (GObject    *object,
90                                 guint       prop_id,
91                                 GValue     *value,
92                                 GParamSpec *pspec)
93 {
94         EvSidebarLayers *ev_sidebar_layers;
95   
96         ev_sidebar_layers = EV_SIDEBAR_LAYERS (object);
97
98         switch (prop_id) {
99                 case PROP_WIDGET:
100                         g_value_set_object (value, ev_sidebar_layers->priv->tree_view);
101                         break;
102                 default:
103                         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
104                         break;
105         }
106 }
107
108 static GtkTreeModel *
109 ev_sidebar_layers_create_loading_model (void)
110 {
111         GtkTreeModel *retval;
112         GtkTreeIter   iter;
113         gchar        *markup;
114
115         /* Creates a fake model to indicate that we're loading */
116         retval = (GtkTreeModel *)gtk_list_store_new (EV_DOCUMENT_LAYERS_N_COLUMNS,
117                                                      G_TYPE_STRING,
118                                                      G_TYPE_OBJECT,
119                                                      G_TYPE_BOOLEAN,
120                                                      G_TYPE_BOOLEAN,
121                                                      G_TYPE_BOOLEAN,
122                                                      G_TYPE_INT);
123
124         gtk_list_store_append (GTK_LIST_STORE (retval), &iter);
125         markup = g_strdup_printf ("<span size=\"larger\" style=\"italic\">%s</span>", _("Loading…"));
126         gtk_list_store_set (GTK_LIST_STORE (retval), &iter,
127                             EV_DOCUMENT_LAYERS_COLUMN_TITLE, markup,
128                             EV_DOCUMENT_LAYERS_COLUMN_VISIBLE, FALSE,
129                             EV_DOCUMENT_LAYERS_COLUMN_ENABLED, TRUE,
130                             EV_DOCUMENT_LAYERS_COLUMN_SHOWTOGGLE, FALSE,
131                             EV_DOCUMENT_LAYERS_COLUMN_RBGROUP, -1,
132                             EV_DOCUMENT_LAYERS_COLUMN_LAYER, NULL,
133                             -1);
134         g_free (markup);
135
136         return retval;
137 }
138
139 static gboolean
140 update_kids (GtkTreeModel *model,
141              GtkTreePath  *path,
142              GtkTreeIter  *iter,
143              GtkTreeIter  *parent)
144 {
145         if (gtk_tree_store_is_ancestor (GTK_TREE_STORE (model), parent, iter)) {
146                 gboolean visible;
147
148                 gtk_tree_model_get (model, parent,
149                                     EV_DOCUMENT_LAYERS_COLUMN_VISIBLE, &visible,
150                                     -1);
151                 gtk_tree_store_set (GTK_TREE_STORE (model), iter,
152                                     EV_DOCUMENT_LAYERS_COLUMN_ENABLED, visible,
153                                     -1);
154         }
155
156         return FALSE;
157 }
158
159 static gboolean
160 clear_rb_group (GtkTreeModel *model,
161                 GtkTreePath  *path,
162                 GtkTreeIter  *iter,
163                 gint         *rb_group)
164 {
165         gint group;
166
167         gtk_tree_model_get (model, iter,
168                             EV_DOCUMENT_LAYERS_COLUMN_RBGROUP, &group,
169                             -1);
170
171         if (group == *rb_group) {
172                 gtk_tree_store_set (GTK_TREE_STORE (model), iter,
173                                     EV_DOCUMENT_LAYERS_COLUMN_VISIBLE, FALSE,
174                                     -1);
175         }
176
177         return FALSE;
178 }
179
180 static void
181 ev_sidebar_layers_visibility_changed (GtkCellRendererToggle *cell,
182                                       gchar                 *path_str,
183                                       EvSidebarLayers       *ev_layers)
184 {
185         GtkTreeModel *model;
186         GtkTreePath  *path;
187         GtkTreeIter   iter;
188         gboolean      visible;
189         EvLayer      *layer;
190
191         model = gtk_tree_view_get_model (ev_layers->priv->tree_view);
192         
193         path = gtk_tree_path_new_from_string (path_str);
194         gtk_tree_model_get_iter (model, &iter, path);
195         gtk_tree_model_get (model, &iter,
196                             EV_DOCUMENT_LAYERS_COLUMN_VISIBLE, &visible,
197                             EV_DOCUMENT_LAYERS_COLUMN_LAYER, &layer, 
198                             -1);
199         
200         visible = !visible;
201         if (visible) {
202                 gint rb_group;
203                 
204                 ev_document_layers_show_layer (EV_DOCUMENT_LAYERS (ev_layers->priv->document),
205                                                layer);
206
207                 rb_group = ev_layer_get_rb_group (layer);
208                 if (rb_group) {
209                         gtk_tree_model_foreach (model,
210                                                 (GtkTreeModelForeachFunc)clear_rb_group,
211                                                 &rb_group);
212                 }
213         } else {
214                 ev_document_layers_hide_layer (EV_DOCUMENT_LAYERS (ev_layers->priv->document),
215                                                layer);
216         }
217         
218         gtk_tree_store_set (GTK_TREE_STORE (model), &iter,
219                             EV_DOCUMENT_LAYERS_COLUMN_VISIBLE, visible,
220                             -1);
221
222         if (ev_layer_is_parent (layer)) {
223                 gtk_tree_model_foreach (model,
224                                         (GtkTreeModelForeachFunc)update_kids,
225                                         &iter);
226         }
227         
228         gtk_tree_path_free (path);
229
230         g_signal_emit (ev_layers, signals[LAYERS_VISIBILITY_CHANGED], 0);
231 }
232
233 static GtkTreeView *
234 ev_sidebar_layers_create_tree_view (EvSidebarLayers *ev_layers)
235 {
236         GtkTreeView       *tree_view;
237         GtkTreeViewColumn *column;
238         GtkCellRenderer   *renderer;
239         
240         tree_view = GTK_TREE_VIEW (gtk_tree_view_new ());
241         gtk_tree_view_set_headers_visible (tree_view, FALSE);
242         gtk_tree_selection_set_mode (gtk_tree_view_get_selection (tree_view),
243                                      GTK_SELECTION_NONE);
244
245
246         column = gtk_tree_view_column_new ();
247
248         renderer = gimp_cell_renderer_toggle_new (EV_STOCK_VISIBLE);
249         gtk_tree_view_column_pack_start (column, renderer, FALSE);
250         gtk_tree_view_column_set_attributes (column, renderer,
251                                              "active", EV_DOCUMENT_LAYERS_COLUMN_VISIBLE,
252                                              "activatable", EV_DOCUMENT_LAYERS_COLUMN_ENABLED,
253                                              "visible", EV_DOCUMENT_LAYERS_COLUMN_SHOWTOGGLE,
254                                              "sensitive", EV_DOCUMENT_LAYERS_COLUMN_ENABLED,
255                                              NULL);
256         g_object_set (G_OBJECT (renderer),
257                       "xpad", 0,
258                       "ypad", 0,
259                       NULL);
260         g_signal_connect (renderer, "toggled",
261                           G_CALLBACK (ev_sidebar_layers_visibility_changed),
262                           (gpointer)ev_layers);
263
264         renderer = gtk_cell_renderer_text_new ();
265         gtk_tree_view_column_pack_start (column, renderer, TRUE);
266         gtk_tree_view_column_set_attributes (column, renderer,
267                                              "markup", EV_DOCUMENT_LAYERS_COLUMN_TITLE,
268                                              "sensitive", EV_DOCUMENT_LAYERS_COLUMN_ENABLED,
269                                              NULL);
270         g_object_set (G_OBJECT (renderer), "ellipsize", PANGO_ELLIPSIZE_END, NULL);
271         
272         gtk_tree_view_append_column (tree_view, column);
273
274         return tree_view;
275 }
276
277 static void
278 ev_sidebar_layers_init (EvSidebarLayers *ev_layers)
279 {
280         GtkWidget    *swindow;
281         GtkTreeModel *model;
282         
283         ev_layers->priv = EV_SIDEBAR_LAYERS_GET_PRIVATE (ev_layers);
284
285         swindow = gtk_scrolled_window_new (NULL, NULL);
286         gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (swindow),
287                                         GTK_POLICY_NEVER,
288                                         GTK_POLICY_AUTOMATIC);
289         gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (swindow),
290                                              GTK_SHADOW_IN);
291         /* Data Model */
292         model = ev_sidebar_layers_create_loading_model ();
293
294         /* Layers list */
295         ev_layers->priv->tree_view = ev_sidebar_layers_create_tree_view (ev_layers);
296         gtk_tree_view_set_model (ev_layers->priv->tree_view, model);
297         g_object_unref (model);
298
299         gtk_container_add (GTK_CONTAINER (swindow),
300                            GTK_WIDGET (ev_layers->priv->tree_view));
301
302         gtk_container_add (GTK_CONTAINER (ev_layers), swindow);
303         gtk_widget_show_all (GTK_WIDGET (ev_layers));
304 }
305
306 static void
307 ev_sidebar_layers_class_init (EvSidebarLayersClass *ev_layers_class)
308 {
309         GObjectClass *g_object_class = G_OBJECT_CLASS (ev_layers_class);
310
311         g_object_class->get_property = ev_sidebar_layers_get_property;
312         g_object_class->dispose = ev_sidebar_layers_dispose;
313
314         g_type_class_add_private (g_object_class, sizeof (EvSidebarLayersPrivate));
315
316         g_object_class_override_property (g_object_class, PROP_WIDGET, "main-widget");
317
318         signals[LAYERS_VISIBILITY_CHANGED] =
319                 g_signal_new ("layers_visibility_changed",
320                               G_TYPE_FROM_CLASS (g_object_class),
321                               G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
322                               G_STRUCT_OFFSET (EvSidebarLayersClass, layers_visibility_changed),
323                               NULL, NULL,
324                               g_cclosure_marshal_VOID__VOID,
325                               G_TYPE_NONE, 0, G_TYPE_NONE);
326 }
327
328 GtkWidget *
329 ev_sidebar_layers_new (void)
330 {
331         return GTK_WIDGET (g_object_new (EV_TYPE_SIDEBAR_LAYERS, NULL));
332 }
333
334 static void
335 update_layers_state (GtkTreeModel     *model,
336                      GtkTreeIter      *iter,
337                      EvDocumentLayers *document_layers)
338 {
339         EvLayer    *layer;
340         gboolean    visible;
341         GtkTreeIter child_iter;
342
343         do {
344                 gtk_tree_model_get (model, iter,
345                                     EV_DOCUMENT_LAYERS_COLUMN_VISIBLE, &visible,
346                                     EV_DOCUMENT_LAYERS_COLUMN_LAYER, &layer,
347                                     -1);
348                 if (layer) {
349                         gboolean layer_visible;
350
351                         layer_visible = ev_document_layers_layer_is_visible (document_layers, layer);
352                         if (layer_visible != visible) {
353                                 gtk_tree_store_set (GTK_TREE_STORE (model), iter,
354                                                     EV_DOCUMENT_LAYERS_COLUMN_VISIBLE, layer_visible,
355                                                     -1);
356                         }
357                 }
358
359                 if (gtk_tree_model_iter_children (model, &child_iter, iter))
360                         update_layers_state (model, &child_iter, document_layers);
361         } while (gtk_tree_model_iter_next (model, iter));
362 }
363
364 void
365 ev_sidebar_layers_update_layers_state (EvSidebarLayers *sidebar_layers)
366 {
367         GtkTreeModel     *model;
368         GtkTreeIter       iter;
369         EvDocumentLayers *document_layers;
370
371         document_layers = EV_DOCUMENT_LAYERS (sidebar_layers->priv->document);
372         model = gtk_tree_view_get_model (GTK_TREE_VIEW (sidebar_layers->priv->tree_view));
373         if (gtk_tree_model_get_iter_first (model, &iter))
374                 update_layers_state (model, &iter, document_layers);
375 }
376
377 static void
378 job_finished_callback (EvJobLayers     *job,
379                        EvSidebarLayers *sidebar_layers)
380 {
381         EvSidebarLayersPrivate *priv;
382
383         priv = sidebar_layers->priv;
384
385         gtk_tree_view_set_model (GTK_TREE_VIEW (priv->tree_view), job->model);
386         
387         g_object_unref (job);
388         priv->job = NULL;
389 }
390
391 static void
392 ev_sidebar_layers_document_changed_cb (EvDocumentModel *model,
393                                        GParamSpec      *pspec,
394                                        EvSidebarLayers *sidebar_layers)
395 {
396         EvDocument *document = ev_document_model_get_document (model);
397         EvSidebarLayersPrivate *priv = sidebar_layers->priv;
398
399         if (!EV_IS_DOCUMENT_LAYERS (document))
400                 return;
401
402         if (priv->document) {
403                 gtk_tree_view_set_model (GTK_TREE_VIEW (priv->tree_view), NULL);
404                 g_object_unref (priv->document);
405         }
406
407         priv->document = g_object_ref (document);
408
409         if (priv->job) {
410                 g_signal_handlers_disconnect_by_func (priv->job,
411                                                       job_finished_callback,
412                                                       sidebar_layers);
413                 g_object_unref (priv->job);
414         }
415
416         priv->job = ev_job_layers_new (document);
417         g_signal_connect (priv->job, "finished",
418                           G_CALLBACK (job_finished_callback),
419                           sidebar_layers);
420         /* The priority doesn't matter for this job */
421         ev_job_scheduler_push_job (priv->job, EV_JOB_PRIORITY_NONE);
422 }
423
424 static void
425 ev_sidebar_layers_set_model (EvSidebarPage   *sidebar_page,
426                              EvDocumentModel *model)
427 {
428         g_signal_connect (model, "notify::document",
429                           G_CALLBACK (ev_sidebar_layers_document_changed_cb),
430                           sidebar_page);
431 }
432
433 static gboolean
434 ev_sidebar_layers_support_document (EvSidebarPage *sidebar_page,
435                                     EvDocument    *document)
436 {
437         return (EV_IS_DOCUMENT_LAYERS (document) &&
438                 ev_document_layers_has_layers (EV_DOCUMENT_LAYERS (document)));
439 }
440
441 static const gchar*
442 ev_sidebar_layers_get_label (EvSidebarPage *sidebar_page)
443 {
444         return _("Layers");
445 }
446
447 static void
448 ev_sidebar_layers_page_iface_init (EvSidebarPageInterface *iface)
449 {
450         iface->support_document = ev_sidebar_layers_support_document;
451         iface->set_model = ev_sidebar_layers_set_model;
452         iface->get_label = ev_sidebar_layers_get_label;
453 }
454