]> www.fi.muni.cz Git - evince.git/blob - libmisc/ev-page-action-widget.c
build: Remove unused variable to fix a compiler warning
[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         EvDocumentModel *doc_model;
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 ev_page_action_widget_set_current_page (EvPageActionWidget *action_widget,
78                                         gint                page)
79 {
80         if (page >= 0) {
81                 gchar *page_label;
82
83                 gtk_entry_set_width_chars (GTK_ENTRY (action_widget->entry),
84                                            CLAMP (ev_document_get_max_label_len (action_widget->document),
85                                                   6, 12));
86
87                 page_label = ev_document_get_page_label (action_widget->document, page);
88                 gtk_entry_set_text (GTK_ENTRY (action_widget->entry), page_label);
89                 gtk_editable_set_position (GTK_EDITABLE (action_widget->entry), -1);
90                 g_free (page_label);
91
92         } else {
93                 gtk_entry_set_text (GTK_ENTRY (action_widget->entry), "");
94         }
95
96         update_pages_label (action_widget, page);
97 }
98
99 static void
100 page_changed_cb (EvDocumentModel    *model,
101                  gint                old_page,
102                  gint                new_page,
103                  EvPageActionWidget *action_widget)
104 {
105         ev_page_action_widget_set_current_page (action_widget, new_page);
106 }
107
108 static gboolean
109 page_scroll_cb (EvPageActionWidget *action_widget, GdkEventScroll *event)
110 {
111         EvDocumentModel *model = action_widget->doc_model;
112         gint pageno;
113
114         pageno = ev_document_model_get_page (model);
115         if ((event->direction == GDK_SCROLL_DOWN) &&
116             (pageno < ev_document_get_n_pages (action_widget->document) - 1))
117                 pageno++;
118         if ((event->direction == GDK_SCROLL_UP) && (pageno > 0))
119                 pageno--;
120         ev_document_model_set_page (model, pageno);
121
122         return TRUE;
123 }
124
125 static void
126 activate_cb (EvPageActionWidget *action_widget)
127 {
128         EvDocumentModel *model;
129         const char *text;
130         gchar *page_label;
131         EvLinkDest *link_dest;
132         EvLinkAction *link_action;
133         EvLink *link;
134         gchar *link_text;
135
136         text = gtk_entry_get_text (GTK_ENTRY (action_widget->entry));
137
138         link_dest = ev_link_dest_new_page_label (text);
139         link_action = ev_link_action_new_dest (link_dest);
140         link_text = g_strdup_printf ("Page: %s", text);
141         link = ev_link_new (link_text, link_action);
142
143         g_signal_emit (action_widget, widget_signals[WIDGET_ACTIVATE_LINK], 0, link);
144
145         g_object_unref (link);
146         g_free (link_text);
147
148         /* rest the entry to the current page if we were unable to
149          * change it */
150         model = action_widget->doc_model;
151         page_label = ev_document_get_page_label (action_widget->document,
152                                                  ev_document_model_get_page (model));
153         gtk_entry_set_text (GTK_ENTRY (action_widget->entry), page_label);
154         gtk_editable_set_position (GTK_EDITABLE (action_widget->entry), -1);
155         g_free (page_label);
156 }
157
158 static void
159 ev_page_action_widget_init (EvPageActionWidget *action_widget)
160 {
161         GtkWidget *hbox;
162         AtkObject *obj;
163
164         hbox = gtk_hbox_new (FALSE, 0);
165         gtk_box_set_spacing (GTK_BOX (hbox), 6);
166
167         action_widget->entry = gtk_entry_new ();
168         gtk_widget_add_events (action_widget->entry,
169                                GDK_BUTTON_MOTION_MASK);
170         gtk_entry_set_width_chars (GTK_ENTRY (action_widget->entry), 5);
171         gtk_entry_set_text (GTK_ENTRY (action_widget->entry), "");
172         g_signal_connect_swapped (action_widget->entry, "scroll-event",
173                                   G_CALLBACK (page_scroll_cb),
174                                   action_widget);
175         g_signal_connect_swapped (action_widget->entry, "activate",
176                                   G_CALLBACK (activate_cb),
177                                   action_widget);
178
179         obj = gtk_widget_get_accessible (action_widget->entry);
180         atk_object_set_name (obj, "page-label-entry");
181
182         gtk_box_pack_start (GTK_BOX (hbox), action_widget->entry,
183                             FALSE, FALSE, 0);
184         gtk_widget_show (action_widget->entry);
185
186         action_widget->label = gtk_label_new (NULL);
187         gtk_box_pack_start (GTK_BOX (hbox), action_widget->label,
188                             FALSE, FALSE, 0);
189         gtk_widget_show (action_widget->label);
190
191         gtk_container_set_border_width (GTK_CONTAINER (action_widget), 6);
192         gtk_container_add (GTK_CONTAINER (action_widget), hbox);
193         gtk_widget_show (hbox);
194
195         gtk_widget_show (GTK_WIDGET (action_widget));
196 }
197
198 static void
199 ev_page_action_widget_document_changed_cb (EvDocumentModel    *model,
200                                            GParamSpec         *pspec,
201                                            EvPageActionWidget *action_widget)
202 {
203         EvDocument *document = ev_document_model_get_document (model);
204
205         g_object_ref (document);
206         if (action_widget->document)
207                 g_object_unref (action_widget->document);
208         action_widget->document = document;
209
210         if (action_widget->signal_id > 0) {
211                 g_signal_handler_disconnect (action_widget->doc_model,
212                                              action_widget->signal_id);
213                 action_widget->signal_id = 0;
214         }
215         action_widget->signal_id =
216                 g_signal_connect_object (action_widget->doc_model,
217                                          "page-changed",
218                                          G_CALLBACK (page_changed_cb),
219                                          action_widget, 0);
220
221         ev_page_action_widget_set_current_page (action_widget,
222                                                 ev_document_model_get_page (model));
223 }
224
225 void
226 ev_page_action_widget_set_model (EvPageActionWidget *action_widget,
227                                  EvDocumentModel    *model)
228 {
229         if (action_widget->doc_model) {
230                 g_object_remove_weak_pointer (G_OBJECT (action_widget->doc_model),
231                                               (gpointer)&action_widget->doc_model);
232         }
233         action_widget->doc_model = model;
234         g_object_add_weak_pointer (G_OBJECT (model),
235                                    (gpointer)&action_widget->doc_model);
236
237         g_signal_connect (model, "notify::document",
238                           G_CALLBACK (ev_page_action_widget_document_changed_cb),
239                           action_widget);
240 }
241
242 static void
243 ev_page_action_widget_finalize (GObject *object)
244 {
245         EvPageActionWidget *action_widget = EV_PAGE_ACTION_WIDGET (object);
246
247         if (action_widget->doc_model != NULL) {
248                 if (action_widget->signal_id > 0) {
249                         g_signal_handler_disconnect (action_widget->doc_model,
250                                                      action_widget->signal_id);
251                         action_widget->signal_id = 0;
252                 }
253                 g_object_remove_weak_pointer (G_OBJECT (action_widget->doc_model),
254                                               (gpointer)&action_widget->doc_model);
255                 action_widget->doc_model = NULL;
256         }
257
258         if (action_widget->document) {
259                 g_object_unref (action_widget->document);
260                 action_widget->document = NULL;
261         }
262
263         G_OBJECT_CLASS (ev_page_action_widget_parent_class)->finalize (object);
264 }
265
266 static void
267 ev_page_action_widget_class_init (EvPageActionWidgetClass *class)
268 {
269         GObjectClass *object_class = G_OBJECT_CLASS (class);
270
271         object_class->finalize = ev_page_action_widget_finalize;
272
273         widget_signals[WIDGET_ACTIVATE_LINK] =
274                 g_signal_new ("activate_link",
275                               G_OBJECT_CLASS_TYPE (object_class),
276                               G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
277                               G_STRUCT_OFFSET (EvPageActionClass, activate_link),
278                               NULL, NULL,
279                               g_cclosure_marshal_VOID__OBJECT,
280                               G_TYPE_NONE, 1,
281                               G_TYPE_OBJECT);
282
283 }
284
285 static gboolean
286 match_selected_cb (GtkEntryCompletion *completion,
287                    GtkTreeModel       *filter_model,
288                    GtkTreeIter        *filter_iter,
289                    EvPageActionWidget *proxy)
290 {
291         EvLink *link;
292         GtkTreeIter *iter;
293
294         gtk_tree_model_get (filter_model, filter_iter,
295                             0, &iter,
296                             -1);
297         gtk_tree_model_get (proxy->model, iter,
298                             EV_DOCUMENT_LINKS_COLUMN_LINK, &link,
299                             -1);
300
301         g_signal_emit (proxy, widget_signals[WIDGET_ACTIVATE_LINK], 0, link);
302
303         if (link)
304                 g_object_unref (link);
305
306         gtk_tree_iter_free (iter);
307         
308         return TRUE;
309 }
310                    
311
312 static void
313 display_completion_text (GtkCellLayout      *cell_layout,
314                          GtkCellRenderer    *renderer,
315                          GtkTreeModel       *filter_model,
316                          GtkTreeIter        *filter_iter,
317                          EvPageActionWidget *proxy)
318 {
319         EvLink *link;
320         GtkTreeIter *iter;
321
322         gtk_tree_model_get (filter_model, filter_iter,
323                             0, &iter,
324                             -1);
325         gtk_tree_model_get (proxy->model, iter,
326                             EV_DOCUMENT_LINKS_COLUMN_LINK, &link,
327                             -1);
328
329         g_object_set (renderer, "text", ev_link_get_title (link), NULL);
330
331         if (link)
332                 g_object_unref (link);
333         
334         gtk_tree_iter_free (iter);
335 }
336
337 static gboolean
338 match_completion (GtkEntryCompletion *completion,
339                   const gchar        *key,
340                   GtkTreeIter        *filter_iter,
341                   EvPageActionWidget *proxy)
342 {
343         EvLink *link;
344         GtkTreeIter *iter;
345         const gchar *text = NULL;
346
347         gtk_tree_model_get (gtk_entry_completion_get_model (completion),
348                             filter_iter,
349                             0, &iter,
350                             -1);
351         gtk_tree_model_get (proxy->model, iter,
352                             EV_DOCUMENT_LINKS_COLUMN_LINK, &link,
353                             -1);
354
355
356         if (link) {
357                 text = ev_link_get_title (link);
358                 g_object_unref (link);
359         }
360
361         gtk_tree_iter_free (iter);
362
363         if (text && key) {
364                 gchar *normalized_text;
365                 gchar *normalized_key;
366                 gchar *case_normalized_text;
367                 gchar *case_normalized_key;
368                 gboolean retval = FALSE;
369
370                 normalized_text = g_utf8_normalize (text, -1, G_NORMALIZE_ALL);
371                 normalized_key = g_utf8_normalize (key, -1, G_NORMALIZE_ALL);
372                 case_normalized_text = g_utf8_casefold (normalized_text, -1);
373                 case_normalized_key = g_utf8_casefold (normalized_key, -1);
374
375                 if (strstr (case_normalized_text, case_normalized_key))
376                         retval = TRUE;
377
378                 g_free (normalized_text);
379                 g_free (normalized_key);
380                 g_free (case_normalized_text);
381                 g_free (case_normalized_key);
382
383                 return retval;
384         }
385
386         return FALSE;
387 }
388
389 /* user data to set on the widget. */
390 #define EPA_FILTER_MODEL_DATA "epa-filter-model"
391
392 static gboolean
393 build_new_tree_cb (GtkTreeModel *model,
394                    GtkTreePath  *path,
395                    GtkTreeIter  *iter,
396                    gpointer      data)
397 {
398         GtkTreeModel *filter_model = GTK_TREE_MODEL (data);
399         EvLink *link;
400         EvLinkAction *action;
401         EvLinkActionType type;
402
403         gtk_tree_model_get (model, iter,
404                             EV_DOCUMENT_LINKS_COLUMN_LINK, &link,
405                             -1);
406
407         if (!link)
408                 return FALSE;
409
410         action = ev_link_get_action (link);
411         if (!action) {
412                 g_object_unref (link);
413                 return FALSE;
414         }
415         
416         type = ev_link_action_get_action_type (action);
417
418         if (type == EV_LINK_ACTION_TYPE_GOTO_DEST) {
419                 GtkTreeIter filter_iter;
420
421                 gtk_list_store_append (GTK_LIST_STORE (filter_model), &filter_iter);
422                 gtk_list_store_set (GTK_LIST_STORE (filter_model), &filter_iter,
423                                     0, iter,
424                                     -1);
425         }
426         
427         g_object_unref (link);
428         
429         return FALSE;
430 }
431
432 static GtkTreeModel *
433 get_filter_model_from_model (GtkTreeModel *model)
434 {
435         GtkTreeModel *filter_model;
436
437         filter_model =
438                 (GtkTreeModel *) g_object_get_data (G_OBJECT (model), EPA_FILTER_MODEL_DATA);
439         if (filter_model == NULL) {
440                 filter_model = (GtkTreeModel *) gtk_list_store_new (1, GTK_TYPE_TREE_ITER);
441
442                 gtk_tree_model_foreach (model,
443                                         build_new_tree_cb,
444                                         filter_model);
445                 g_object_set_data_full (G_OBJECT (model), EPA_FILTER_MODEL_DATA, filter_model, g_object_unref);
446         }
447
448         return filter_model;
449 }
450
451
452 void
453 ev_page_action_widget_update_links_model (EvPageActionWidget *proxy, GtkTreeModel *model)
454 {
455         GtkTreeModel *filter_model;
456         GtkEntryCompletion *completion;
457         GtkCellRenderer *renderer;
458
459         if (!model)
460                 return;
461
462         /* Magik */
463         proxy->model = model;
464         filter_model = get_filter_model_from_model (model);
465
466         completion = gtk_entry_completion_new ();
467         g_object_set (G_OBJECT (completion),
468                       "popup-set-width", FALSE,
469                       "model", filter_model,
470                       NULL);
471
472         g_signal_connect (completion, "match-selected", G_CALLBACK (match_selected_cb), proxy);
473         gtk_entry_completion_set_match_func (completion,
474                                              (GtkEntryCompletionMatchFunc) match_completion,
475                                              proxy, NULL);
476
477         /* Set up the layout */
478         renderer = (GtkCellRenderer *)
479                 g_object_new (GTK_TYPE_CELL_RENDERER_TEXT,
480                               "ellipsize", PANGO_ELLIPSIZE_END,
481                               "width_chars", 30,
482                               NULL);
483         gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (completion), renderer, TRUE);
484         gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (completion),
485                                             renderer,
486                                             (GtkCellLayoutDataFunc) display_completion_text,
487                                             proxy, NULL);
488         gtk_entry_set_completion (GTK_ENTRY (proxy->entry), completion);
489
490         g_object_unref (completion);
491 }
492
493 void
494 ev_page_action_widget_grab_focus (EvPageActionWidget *proxy)
495 {
496         gtk_widget_grab_focus (proxy->entry);
497 }
498