]> www.fi.muni.cz Git - evince.git/blob - libmisc/ev-page-action-widget.c
Move part of the EvPageCache to EvDocument
[evince.git] / libmisc / ev-page-action-widget.c
1 /*
2  *  Copyright (C) 2003, 2004 Marco Pesenti Gritti
3  *  Copyright (C) 2003, 2004 Christian Persch
4  *
5  *  This program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2, or (at your option)
8  *  any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program; if not, write to the Free Software
17  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18  *
19  */
20
21 #include "config.h"
22
23 #include <string.h>
24 #include <glib/gi18n.h>
25 #include <gtk/gtk.h>
26
27 #include <evince-document.h>
28 #include "ev-page-action.h"
29 #include "ev-page-action-widget.h"
30
31 /* Widget we pass back */
32 static void  ev_page_action_widget_init       (EvPageActionWidget      *action_widget);
33 static void  ev_page_action_widget_class_init (EvPageActionWidgetClass *action_widget);
34
35 enum
36 {
37         WIDGET_ACTIVATE_LINK,
38         WIDGET_N_SIGNALS
39 };
40
41 struct _EvPageActionWidget
42 {
43         GtkToolItem parent;
44
45         EvDocument *document;
46         EvPageCache *page_cache;
47
48         GtkWidget *entry;
49         GtkWidget *label;
50         guint signal_id;
51         GtkTreeModel *filter_model;
52         GtkTreeModel *model;
53 };
54
55 static guint widget_signals[WIDGET_N_SIGNALS] = {0, };
56
57 G_DEFINE_TYPE (EvPageActionWidget, ev_page_action_widget, GTK_TYPE_TOOL_ITEM)
58
59 static void
60 update_pages_label (EvPageActionWidget *action_widget,
61                     gint                page)
62 {
63         char *label_text;
64         gint n_pages;
65
66         n_pages = ev_document_get_n_pages (action_widget->document);
67         if (ev_document_has_text_page_labels (action_widget->document)) {
68                 label_text = g_strdup_printf (_("(%d of %d)"), page + 1, n_pages);
69         } else {
70                 label_text = g_strdup_printf (_("of %d"), n_pages);
71         }
72         gtk_label_set_text (GTK_LABEL (action_widget->label), label_text);
73         g_free (label_text);
74 }
75
76 static void
77 page_changed_cb (EvPageCache        *page_cache,
78                  gint                page,
79                  EvPageActionWidget *action_widget)
80 {
81         if (page >= 0) {
82                 gchar *page_label;
83
84                 gtk_entry_set_width_chars (GTK_ENTRY (action_widget->entry),
85                                            CLAMP (ev_document_get_max_label_len (action_widget->document),
86                                                   6, 12));
87
88                 page_label = ev_document_get_page_label (action_widget->document, page);
89                 gtk_entry_set_text (GTK_ENTRY (action_widget->entry), page_label);
90                 gtk_editable_set_position (GTK_EDITABLE (action_widget->entry), -1);
91                 g_free (page_label);
92
93         } else {
94                 gtk_entry_set_text (GTK_ENTRY (action_widget->entry), "");
95         }
96
97         update_pages_label (action_widget, page);
98 }
99
100 static gboolean
101 page_scroll_cb (EvPageActionWidget *action_widget, GdkEventScroll *event)
102 {
103         EvPageCache *page_cache = action_widget->page_cache;
104         gint pageno;
105
106         pageno = ev_page_cache_get_current_page (page_cache);
107         if ((event->direction == GDK_SCROLL_DOWN) &&
108             (pageno < ev_document_get_n_pages (action_widget->document) - 1))
109                 pageno++;
110         if ((event->direction == GDK_SCROLL_UP) && (pageno > 0))
111                 pageno--;
112         ev_page_cache_set_current_page (page_cache, pageno);
113
114         return TRUE;
115 }
116
117 static void
118 activate_cb (EvPageActionWidget *action_widget)
119 {
120         EvPageCache *page_cache;
121         const char *text;
122         gchar *page_label;
123         EvLinkDest *link_dest;
124         EvLinkAction *link_action;
125         EvLink *link;
126         gchar *link_text;
127
128         text = gtk_entry_get_text (GTK_ENTRY (action_widget->entry));
129
130         link_dest = ev_link_dest_new_page_label (text);
131         link_action = ev_link_action_new_dest (link_dest);
132         link_text = g_strdup_printf ("Page: %s", text);
133         link = ev_link_new (link_text, link_action);
134
135         g_signal_emit (action_widget, widget_signals[WIDGET_ACTIVATE_LINK], 0, link);
136
137         g_object_unref (link);
138         g_free (link_text);
139
140         /* rest the entry to the current page if we were unable to
141          * change it */
142         page_cache = action_widget->page_cache;
143         page_label = ev_document_get_page_label (action_widget->document,
144                                                  ev_page_cache_get_current_page (page_cache));
145         gtk_entry_set_text (GTK_ENTRY (action_widget->entry), page_label);
146         gtk_editable_set_position (GTK_EDITABLE (action_widget->entry), -1);
147         g_free (page_label);
148 }
149
150 static void
151 ev_page_action_widget_init (EvPageActionWidget *action_widget)
152 {
153         GtkWidget *hbox;
154         AtkObject *obj;
155
156         hbox = gtk_hbox_new (FALSE, 0);
157         gtk_box_set_spacing (GTK_BOX (hbox), 6);
158
159         action_widget->entry = gtk_entry_new ();
160         gtk_widget_add_events (action_widget->entry,
161                                GDK_BUTTON_MOTION_MASK);
162         gtk_entry_set_width_chars (GTK_ENTRY (action_widget->entry), 5);
163         gtk_entry_set_text (GTK_ENTRY (action_widget->entry), "");
164         g_signal_connect_swapped (action_widget->entry, "scroll-event",
165                                   G_CALLBACK (page_scroll_cb),
166                                   action_widget);
167         g_signal_connect_swapped (action_widget->entry, "activate",
168                                   G_CALLBACK (activate_cb),
169                                   action_widget);
170
171         obj = gtk_widget_get_accessible (action_widget->entry);
172         atk_object_set_name (obj, "page-label-entry");
173
174         gtk_box_pack_start (GTK_BOX (hbox), action_widget->entry,
175                             FALSE, FALSE, 0);
176         gtk_widget_show (action_widget->entry);
177
178         action_widget->label = gtk_label_new (NULL);
179         gtk_box_pack_start (GTK_BOX (hbox), action_widget->label,
180                             FALSE, FALSE, 0);
181         gtk_widget_show (action_widget->label);
182
183         gtk_container_set_border_width (GTK_CONTAINER (action_widget), 6);
184         gtk_container_add (GTK_CONTAINER (action_widget), hbox);
185         gtk_widget_show (hbox);
186
187         gtk_widget_show (GTK_WIDGET (action_widget));
188 }
189
190 void
191 ev_page_action_widget_set_document (EvPageActionWidget *action_widget,
192                                     EvDocument         *document)
193 {
194         EvPageCache *page_cache = ev_page_cache_get (document);
195
196         g_object_ref (document);
197         if (action_widget->document)
198                 g_object_unref (action_widget->document);
199         action_widget->document = document;
200
201         if (action_widget->page_cache != NULL) {
202                 if (action_widget->signal_id > 0) {
203                         g_signal_handler_disconnect (action_widget->page_cache,
204                                                      action_widget->signal_id);
205                         action_widget->signal_id = 0;
206                 }
207                 g_object_remove_weak_pointer (G_OBJECT (action_widget->page_cache),
208                                               (gpointer)&action_widget->page_cache);
209                 action_widget->page_cache = NULL;
210         }
211
212         action_widget->page_cache = page_cache;
213         g_object_add_weak_pointer (G_OBJECT (page_cache),
214                                    (gpointer)&action_widget->page_cache);
215         action_widget->signal_id =
216                 g_signal_connect_object (page_cache, "page-changed",
217                                          G_CALLBACK (page_changed_cb),
218                                          action_widget, 0);
219         page_changed_cb (page_cache,
220                          ev_page_cache_get_current_page (page_cache),
221                          action_widget);
222 }
223
224 static void
225 ev_page_action_widget_finalize (GObject *object)
226 {
227         EvPageActionWidget *action_widget = EV_PAGE_ACTION_WIDGET (object);
228
229         if (action_widget->page_cache != NULL) {
230                 if (action_widget->signal_id > 0) {
231                         g_signal_handler_disconnect (action_widget->page_cache,
232                                                      action_widget->signal_id);
233                         action_widget->signal_id = 0;
234                 }
235                 g_object_remove_weak_pointer (G_OBJECT (action_widget->page_cache),
236                                               (gpointer)&action_widget->page_cache);
237                 action_widget->page_cache = NULL;
238         }
239
240         if (action_widget->document) {
241                 g_object_unref (action_widget->document);
242                 action_widget->document = NULL;
243         }
244
245         G_OBJECT_CLASS (ev_page_action_widget_parent_class)->finalize (object);
246 }
247
248 static void
249 ev_page_action_widget_class_init (EvPageActionWidgetClass *class)
250 {
251         GObjectClass *object_class = G_OBJECT_CLASS (class);
252
253         object_class->finalize = ev_page_action_widget_finalize;
254
255         widget_signals[WIDGET_ACTIVATE_LINK] =
256                 g_signal_new ("activate_link",
257                               G_OBJECT_CLASS_TYPE (object_class),
258                               G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
259                               G_STRUCT_OFFSET (EvPageActionClass, activate_link),
260                               NULL, NULL,
261                               g_cclosure_marshal_VOID__OBJECT,
262                               G_TYPE_NONE, 1,
263                               G_TYPE_OBJECT);
264
265 }
266
267 static gboolean
268 match_selected_cb (GtkEntryCompletion *completion,
269                    GtkTreeModel       *filter_model,
270                    GtkTreeIter        *filter_iter,
271                    EvPageActionWidget *proxy)
272 {
273         EvLink *link;
274         GtkTreeIter *iter;
275
276         gtk_tree_model_get (filter_model, filter_iter,
277                             0, &iter,
278                             -1);
279         gtk_tree_model_get (proxy->model, iter,
280                             EV_DOCUMENT_LINKS_COLUMN_LINK, &link,
281                             -1);
282
283         g_signal_emit (proxy, widget_signals[WIDGET_ACTIVATE_LINK], 0, link);
284
285         if (link)
286                 g_object_unref (link);
287
288         gtk_tree_iter_free (iter);
289         
290         return TRUE;
291 }
292                    
293
294 static void
295 display_completion_text (GtkCellLayout      *cell_layout,
296                          GtkCellRenderer    *renderer,
297                          GtkTreeModel       *filter_model,
298                          GtkTreeIter        *filter_iter,
299                          EvPageActionWidget *proxy)
300 {
301         EvLink *link;
302         GtkTreeIter *iter;
303
304         gtk_tree_model_get (filter_model, filter_iter,
305                             0, &iter,
306                             -1);
307         gtk_tree_model_get (proxy->model, iter,
308                             EV_DOCUMENT_LINKS_COLUMN_LINK, &link,
309                             -1);
310
311         g_object_set (renderer, "text", ev_link_get_title (link), NULL);
312
313         if (link)
314                 g_object_unref (link);
315         
316         gtk_tree_iter_free (iter);
317 }
318
319 static gboolean
320 match_completion (GtkEntryCompletion *completion,
321                   const gchar        *key,
322                   GtkTreeIter        *filter_iter,
323                   EvPageActionWidget *proxy)
324 {
325         EvLink *link;
326         GtkTreeIter *iter;
327         const gchar *text = NULL;
328
329         gtk_tree_model_get (gtk_entry_completion_get_model (completion),
330                             filter_iter,
331                             0, &iter,
332                             -1);
333         gtk_tree_model_get (proxy->model, iter,
334                             EV_DOCUMENT_LINKS_COLUMN_LINK, &link,
335                             -1);
336
337
338         if (link) {
339                 text = ev_link_get_title (link);
340                 g_object_unref (link);
341         }
342
343         gtk_tree_iter_free (iter);
344
345         if (text && key) {
346                 gchar *normalized_text;
347                 gchar *normalized_key;
348                 gchar *case_normalized_text;
349                 gchar *case_normalized_key;
350                 gboolean retval = FALSE;
351
352                 normalized_text = g_utf8_normalize (text, -1, G_NORMALIZE_ALL);
353                 normalized_key = g_utf8_normalize (key, -1, G_NORMALIZE_ALL);
354                 case_normalized_text = g_utf8_casefold (normalized_text, -1);
355                 case_normalized_key = g_utf8_casefold (normalized_key, -1);
356
357                 if (strstr (case_normalized_text, case_normalized_key))
358                         retval = TRUE;
359
360                 g_free (normalized_text);
361                 g_free (normalized_key);
362                 g_free (case_normalized_text);
363                 g_free (case_normalized_key);
364
365                 return retval;
366         }
367
368         return FALSE;
369 }
370
371 /* user data to set on the widget. */
372 #define EPA_FILTER_MODEL_DATA "epa-filter-model"
373
374 static gboolean
375 build_new_tree_cb (GtkTreeModel *model,
376                    GtkTreePath  *path,
377                    GtkTreeIter  *iter,
378                    gpointer      data)
379 {
380         GtkTreeModel *filter_model = GTK_TREE_MODEL (data);
381         EvLink *link;
382         EvLinkAction *action;
383         EvLinkActionType type;
384
385         gtk_tree_model_get (model, iter,
386                             EV_DOCUMENT_LINKS_COLUMN_LINK, &link,
387                             -1);
388
389         if (!link)
390                 return FALSE;
391
392         action = ev_link_get_action (link);
393         if (!action) {
394                 g_object_unref (link);
395                 return FALSE;
396         }
397         
398         type = ev_link_action_get_action_type (action);
399
400         if (type == EV_LINK_ACTION_TYPE_GOTO_DEST) {
401                 GtkTreeIter filter_iter;
402
403                 gtk_list_store_append (GTK_LIST_STORE (filter_model), &filter_iter);
404                 gtk_list_store_set (GTK_LIST_STORE (filter_model), &filter_iter,
405                                     0, iter,
406                                     -1);
407         }
408         
409         g_object_unref (link);
410         
411         return FALSE;
412 }
413
414 static GtkTreeModel *
415 get_filter_model_from_model (GtkTreeModel *model)
416 {
417         GtkTreeModel *filter_model;
418
419         filter_model =
420                 (GtkTreeModel *) g_object_get_data (G_OBJECT (model), EPA_FILTER_MODEL_DATA);
421         if (filter_model == NULL) {
422                 filter_model = (GtkTreeModel *) gtk_list_store_new (1, GTK_TYPE_TREE_ITER);
423
424                 gtk_tree_model_foreach (model,
425                                         build_new_tree_cb,
426                                         filter_model);
427                 g_object_set_data_full (G_OBJECT (model), EPA_FILTER_MODEL_DATA, filter_model, g_object_unref);
428         }
429
430         return filter_model;
431 }
432
433
434 void
435 ev_page_action_widget_update_model (EvPageActionWidget *proxy, GtkTreeModel *model)
436 {
437         GtkTreeModel *filter_model;
438         GtkEntryCompletion *completion;
439         GtkCellRenderer *renderer;
440
441         if (!model)
442                 return;
443
444         /* Magik */
445         proxy->model = model;
446         filter_model = get_filter_model_from_model (model);
447
448         completion = gtk_entry_completion_new ();
449         g_object_set (G_OBJECT (completion),
450                       "popup-set-width", FALSE,
451                       "model", filter_model,
452                       NULL);
453
454         g_signal_connect (completion, "match-selected", G_CALLBACK (match_selected_cb), proxy);
455         gtk_entry_completion_set_match_func (completion,
456                                              (GtkEntryCompletionMatchFunc) match_completion,
457                                              proxy, NULL);
458
459         /* Set up the layout */
460         renderer = (GtkCellRenderer *)
461                 g_object_new (GTK_TYPE_CELL_RENDERER_TEXT,
462                               "ellipsize", PANGO_ELLIPSIZE_END,
463                               "width_chars", 30,
464                               NULL);
465         gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (completion), renderer, TRUE);
466         gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (completion),
467                                             renderer,
468                                             (GtkCellLayoutDataFunc) display_completion_text,
469                                             proxy, NULL);
470         gtk_entry_set_completion (GTK_ENTRY (proxy->entry), completion);
471
472         g_object_unref (completion);
473         g_object_unref (model);
474 }
475
476 void
477 ev_page_action_widget_grab_focus (EvPageActionWidget *proxy)
478 {
479         gtk_widget_grab_focus (proxy->entry);
480 }
481