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-page-action-widget.c
Go to the documentation of this file.
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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-widget.h"
29 
30 /* Widget we pass back */
31 static void ev_page_action_widget_init (EvPageActionWidget *action_widget);
33 
34 enum
35 {
38 };
39 
41 {
42  GtkToolItem parent;
43 
46 
47  GtkWidget *entry;
48  GtkWidget *label;
49  guint signal_id;
50  GtkTreeModel *filter_model;
51  GtkTreeModel *model;
52 };
53 
54 static guint widget_signals[WIDGET_N_SIGNALS] = {0, };
55 
56 G_DEFINE_TYPE (EvPageActionWidget, ev_page_action_widget, GTK_TYPE_TOOL_ITEM)
57 
58 static gboolean
60  gint page)
61 {
62  gchar *page_label;
63  gboolean retval;
64 
65  if (!ev_document_has_text_page_labels (action_widget->document))
66  return FALSE;
67 
68  page_label = g_strdup_printf ("%d", page + 1);
69  retval = g_strcmp0 (page_label, gtk_entry_get_text (GTK_ENTRY (action_widget->entry))) != 0;
70  g_free (page_label);
71 
72  return retval;
73 }
74 
75 static void
77  gint page)
78 {
79  char *label_text;
80  gint n_pages;
81 
82  n_pages = ev_document_get_n_pages (action_widget->document);
83  if (show_page_number_in_pages_label (action_widget, page))
84  label_text = g_strdup_printf (_("(%d of %d)"), page + 1, n_pages);
85  else
86  label_text = g_strdup_printf (_("of %d"), n_pages);
87  gtk_entry_set_text (GTK_ENTRY (action_widget->label), label_text);
88  g_free (label_text);
89 }
90 
91 static void
93  gint page)
94 {
95  if (page >= 0) {
96  gchar *page_label;
97 
98  page_label = ev_document_get_page_label (action_widget->document, page);
99  gtk_entry_set_text (GTK_ENTRY (action_widget->entry), page_label);
100  gtk_editable_set_position (GTK_EDITABLE (action_widget->entry), -1);
101  g_free (page_label);
102  } else {
103  gtk_entry_set_text (GTK_ENTRY (action_widget->entry), "");
104  }
105 
106  update_pages_label (action_widget, page);
107 }
108 
109 static void
111 {
112  gchar *max_label;
113  gint n_pages;
114  gint max_label_len;
115  gchar *max_page_label;
116  gchar *max_page_numeric_label;
117 
118  n_pages = ev_document_get_n_pages (action_widget->document);
119 
120  max_page_label = ev_document_get_page_label (action_widget->document, n_pages - 1);
121  max_page_numeric_label = g_strdup_printf ("%d", n_pages);
122  if (ev_document_has_text_page_labels (action_widget->document) != 0) {
123  max_label = g_strdup_printf (_("(%d of %d)"), n_pages, n_pages);
124  /* Do not take into account the parentheses for the size computation */
125  max_label_len = g_utf8_strlen (max_label, -1) - 2;
126  } else {
127  max_label = g_strdup_printf (_("of %d"), n_pages);
128  max_label_len = g_utf8_strlen (max_label, -1);
129  }
130  g_free (max_page_label);
131 
132  gtk_entry_set_width_chars (GTK_ENTRY (action_widget->label), max_label_len);
133  g_free (max_label);
134 
135  max_label_len = ev_document_get_max_label_len (action_widget->document);
136  gtk_entry_set_width_chars (GTK_ENTRY (action_widget->entry),
137  CLAMP (max_label_len, strlen (max_page_numeric_label) + 1, 12));
138  g_free (max_page_numeric_label);
139 }
140 
141 static void
143  gint old_page,
144  gint new_page,
145  EvPageActionWidget *action_widget)
146 {
147  ev_page_action_widget_set_current_page (action_widget, new_page);
148 }
149 
150 static gboolean
151 page_scroll_cb (EvPageActionWidget *action_widget, GdkEventScroll *event)
152 {
153  EvDocumentModel *model = action_widget->doc_model;
154  gint pageno;
155 
156  pageno = ev_document_model_get_page (model);
157  if ((event->direction == GDK_SCROLL_DOWN) &&
158  (pageno < ev_document_get_n_pages (action_widget->document) - 1))
159  pageno++;
160  if ((event->direction == GDK_SCROLL_UP) && (pageno > 0))
161  pageno--;
162  ev_document_model_set_page (model, pageno);
163 
164  return TRUE;
165 }
166 
167 static void
169 {
170  EvDocumentModel *model;
171  const char *text;
172  EvLinkDest *link_dest;
173  EvLinkAction *link_action;
174  EvLink *link;
175  gchar *link_text;
176  gint current_page;
177 
178  model = action_widget->doc_model;
179  current_page = ev_document_model_get_page (model);
180 
181  text = gtk_entry_get_text (GTK_ENTRY (action_widget->entry));
182 
183  link_dest = ev_link_dest_new_page_label (text);
184  link_action = ev_link_action_new_dest (link_dest);
185  link_text = g_strdup_printf (_("Page %s"), text);
186  link = ev_link_new (link_text, link_action);
187 
188  g_signal_emit (action_widget, widget_signals[WIDGET_ACTIVATE_LINK], 0, link);
189 
190  g_object_unref (link_dest);
191  g_object_unref (link_action);
192  g_object_unref (link);
193  g_free (link_text);
194 
195  if (current_page == ev_document_model_get_page (model))
196  ev_page_action_widget_set_current_page (action_widget, current_page);
197 }
198 
199 static gboolean
201 {
203  ev_document_model_get_page (action_widget->doc_model));
204  return FALSE;
205 }
206 
207 static void
209 {
210  GtkWidget *hbox;
211  AtkObject *obj;
212  GtkStyleContext *style_context;
213 
214  hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
215 
216  style_context = gtk_widget_get_style_context (hbox);
217  gtk_style_context_add_class (style_context, GTK_STYLE_CLASS_RAISED);
218  gtk_style_context_add_class (style_context, GTK_STYLE_CLASS_LINKED);
219 
220  action_widget->entry = gtk_entry_new ();
221  gtk_widget_add_events (action_widget->entry,
222  GDK_BUTTON_MOTION_MASK);
223  gtk_entry_set_width_chars (GTK_ENTRY (action_widget->entry), 5);
224  gtk_entry_set_text (GTK_ENTRY (action_widget->entry), "");
225  g_signal_connect_swapped (action_widget->entry, "scroll-event",
226  G_CALLBACK (page_scroll_cb),
227  action_widget);
228  g_signal_connect_swapped (action_widget->entry, "activate",
229  G_CALLBACK (activate_cb),
230  action_widget);
231  g_signal_connect_swapped (action_widget->entry, "focus-out-event",
232  G_CALLBACK (focus_out_cb),
233  action_widget);
234 
235  obj = gtk_widget_get_accessible (action_widget->entry);
236  atk_object_set_name (obj, "page-label-entry");
237 
238  gtk_box_pack_start (GTK_BOX (hbox), action_widget->entry,
239  FALSE, FALSE, 0);
240  gtk_widget_show (action_widget->entry);
241 
242  action_widget->label = gtk_entry_new ();
243  gtk_widget_set_sensitive (action_widget->label, FALSE);
244  gtk_entry_set_width_chars (GTK_ENTRY (action_widget->label), 5);
245  gtk_box_pack_start (GTK_BOX (hbox), action_widget->label,
246  FALSE, FALSE, 0);
247  gtk_widget_show (action_widget->label);
248 
249  gtk_container_add (GTK_CONTAINER (action_widget), hbox);
250  gtk_widget_show (hbox);
251 
252  gtk_widget_set_sensitive (GTK_WIDGET (action_widget), FALSE);
253 }
254 
255 static void
257  EvDocument *document)
258 {
259  if (document) {
260  g_object_ref (document);
261  gtk_widget_set_sensitive (GTK_WIDGET (action_widget), ev_document_get_n_pages (document) > 0);
262  }
263 
264  if (action_widget->signal_id > 0) {
265  g_signal_handler_disconnect (action_widget->doc_model,
266  action_widget->signal_id);
267  action_widget->signal_id = 0;
268  }
269 
270  if (action_widget->document)
271  g_object_unref (action_widget->document);
272  action_widget->document = document;
273  if (!action_widget->document)
274  return;
275 
276  action_widget->signal_id =
277  g_signal_connect (action_widget->doc_model,
278  "page-changed",
279  G_CALLBACK (page_changed_cb),
280  action_widget);
281 
283  ev_document_model_get_page (action_widget->doc_model));
285 }
286 
287 static void
289  GParamSpec *pspec,
290  EvPageActionWidget *action_widget)
291 {
293 }
294 
295 void
297  EvDocumentModel *model)
298 {
299  if (action_widget->doc_model) {
300  g_object_remove_weak_pointer (G_OBJECT (action_widget->doc_model),
301  (gpointer)&action_widget->doc_model);
302  }
303  action_widget->doc_model = model;
304  g_object_add_weak_pointer (G_OBJECT (model),
305  (gpointer)&action_widget->doc_model);
306 
308  g_signal_connect (model, "notify::document",
310  action_widget);
311 }
312 
313 static void
315 {
316  EvPageActionWidget *action_widget = EV_PAGE_ACTION_WIDGET (object);
317 
318  if (action_widget->doc_model != NULL) {
319  if (action_widget->signal_id > 0) {
320  g_signal_handler_disconnect (action_widget->doc_model,
321  action_widget->signal_id);
322  action_widget->signal_id = 0;
323  }
324  g_object_remove_weak_pointer (G_OBJECT (action_widget->doc_model),
325  (gpointer)&action_widget->doc_model);
326  action_widget->doc_model = NULL;
327  }
328 
329  ev_page_action_widget_set_document (action_widget, NULL);
330 
331  G_OBJECT_CLASS (ev_page_action_widget_parent_class)->finalize (object);
332 }
333 
334 static void
336  gint *minimum_width,
337  gint *natural_width)
338 {
339  GtkWidget *child;
340 
341  *minimum_width = *natural_width = 0;
342 
343  child = gtk_bin_get_child (GTK_BIN (widget));
344  if (!child || !gtk_widget_get_visible (child))
345  return;
346 
347  gtk_widget_get_preferred_width (child, minimum_width, natural_width);
348  *natural_width = *minimum_width;
349 }
350 
351 static void
353 {
354  GObjectClass *object_class = G_OBJECT_CLASS (klass);
355  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
356 
357  object_class->finalize = ev_page_action_widget_finalize;
358  widget_class->get_preferred_width = ev_page_action_widget_get_preferred_width;
359 
361  g_signal_new ("activate_link",
362  G_OBJECT_CLASS_TYPE (object_class),
363  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
364  G_STRUCT_OFFSET (EvPageActionWidgetClass, activate_link),
365  NULL, NULL,
366  g_cclosure_marshal_VOID__OBJECT,
367  G_TYPE_NONE, 1,
368  G_TYPE_OBJECT);
369 
370 }
371 
372 static gboolean
373 match_selected_cb (GtkEntryCompletion *completion,
374  GtkTreeModel *filter_model,
375  GtkTreeIter *filter_iter,
376  EvPageActionWidget *proxy)
377 {
378  EvLink *link;
379  GtkTreeIter *iter;
380 
381  gtk_tree_model_get (filter_model, filter_iter,
382  0, &iter,
383  -1);
384  gtk_tree_model_get (proxy->model, iter,
386  -1);
387 
388  g_signal_emit (proxy, widget_signals[WIDGET_ACTIVATE_LINK], 0, link);
389 
390  if (link)
391  g_object_unref (link);
392 
393  gtk_tree_iter_free (iter);
394 
395  return TRUE;
396 }
397 
398 
399 static void
400 display_completion_text (GtkCellLayout *cell_layout,
401  GtkCellRenderer *renderer,
402  GtkTreeModel *filter_model,
403  GtkTreeIter *filter_iter,
404  EvPageActionWidget *proxy)
405 {
406  EvLink *link;
407  GtkTreeIter *iter;
408 
409  gtk_tree_model_get (filter_model, filter_iter,
410  0, &iter,
411  -1);
412  gtk_tree_model_get (proxy->model, iter,
414  -1);
415 
416  g_object_set (renderer, "text", ev_link_get_title (link), NULL);
417 
418  if (link)
419  g_object_unref (link);
420 
421  gtk_tree_iter_free (iter);
422 }
423 
424 static gboolean
425 match_completion (GtkEntryCompletion *completion,
426  const gchar *key,
427  GtkTreeIter *filter_iter,
428  EvPageActionWidget *proxy)
429 {
430  EvLink *link;
431  GtkTreeIter *iter;
432  const gchar *text = NULL;
433 
434  gtk_tree_model_get (gtk_entry_completion_get_model (completion),
435  filter_iter,
436  0, &iter,
437  -1);
438  gtk_tree_model_get (proxy->model, iter,
440  -1);
441 
442 
443  if (link) {
444  text = ev_link_get_title (link);
445  g_object_unref (link);
446  }
447 
448  gtk_tree_iter_free (iter);
449 
450  if (text && key) {
451  gchar *normalized_text;
452  gchar *normalized_key;
453  gchar *case_normalized_text;
454  gchar *case_normalized_key;
455  gboolean retval = FALSE;
456 
457  normalized_text = g_utf8_normalize (text, -1, G_NORMALIZE_ALL);
458  normalized_key = g_utf8_normalize (key, -1, G_NORMALIZE_ALL);
459  case_normalized_text = g_utf8_casefold (normalized_text, -1);
460  case_normalized_key = g_utf8_casefold (normalized_key, -1);
461 
462  if (strstr (case_normalized_text, case_normalized_key))
463  retval = TRUE;
464 
465  g_free (normalized_text);
466  g_free (normalized_key);
467  g_free (case_normalized_text);
468  g_free (case_normalized_key);
469 
470  return retval;
471  }
472 
473  return FALSE;
474 }
475 
476 /* user data to set on the widget. */
477 #define EPA_FILTER_MODEL_DATA "epa-filter-model"
478 
479 static gboolean
480 build_new_tree_cb (GtkTreeModel *model,
481  GtkTreePath *path,
482  GtkTreeIter *iter,
483  gpointer data)
484 {
485  GtkTreeModel *filter_model = GTK_TREE_MODEL (data);
486  EvLink *link;
487  EvLinkAction *action;
488  EvLinkActionType type;
489 
490  gtk_tree_model_get (model, iter,
492  -1);
493 
494  if (!link)
495  return FALSE;
496 
497  action = ev_link_get_action (link);
498  if (!action) {
499  g_object_unref (link);
500  return FALSE;
501  }
502 
503  type = ev_link_action_get_action_type (action);
504 
505  if (type == EV_LINK_ACTION_TYPE_GOTO_DEST) {
506  GtkTreeIter filter_iter;
507 
508  gtk_list_store_append (GTK_LIST_STORE (filter_model), &filter_iter);
509  gtk_list_store_set (GTK_LIST_STORE (filter_model), &filter_iter,
510  0, iter,
511  -1);
512  }
513 
514  g_object_unref (link);
515 
516  return FALSE;
517 }
518 
519 static GtkTreeModel *
520 get_filter_model_from_model (GtkTreeModel *model)
521 {
522  GtkTreeModel *filter_model;
523 
524  filter_model =
525  (GtkTreeModel *) g_object_get_data (G_OBJECT (model), EPA_FILTER_MODEL_DATA);
526  if (filter_model == NULL) {
527  filter_model = (GtkTreeModel *) gtk_list_store_new (1, GTK_TYPE_TREE_ITER);
528 
529  gtk_tree_model_foreach (model,
531  filter_model);
532  g_object_set_data_full (G_OBJECT (model), EPA_FILTER_MODEL_DATA, filter_model, g_object_unref);
533  }
534 
535  return filter_model;
536 }
537 
538 
539 void
541 {
542  GtkTreeModel *filter_model;
543  GtkEntryCompletion *completion;
544  GtkCellRenderer *renderer;
545 
546  if (!model || model == proxy->model)
547  return;
548 
549  /* Magik */
550  proxy->model = model;
551  filter_model = get_filter_model_from_model (model);
552 
553  completion = gtk_entry_completion_new ();
554  g_object_set (G_OBJECT (completion),
555  "popup-set-width", FALSE,
556  "model", filter_model,
557  NULL);
558 
559  g_signal_connect (completion, "match-selected", G_CALLBACK (match_selected_cb), proxy);
560  gtk_entry_completion_set_match_func (completion,
561  (GtkEntryCompletionMatchFunc) match_completion,
562  proxy, NULL);
563 
564  /* Set up the layout */
565  renderer = (GtkCellRenderer *)
566  g_object_new (GTK_TYPE_CELL_RENDERER_TEXT,
567  "ellipsize", PANGO_ELLIPSIZE_END,
568  "width_chars", 30,
569  NULL);
570  gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (completion), renderer, TRUE);
571  gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (completion),
572  renderer,
573  (GtkCellLayoutDataFunc) display_completion_text,
574  proxy, NULL);
575  gtk_entry_set_completion (GTK_ENTRY (proxy->entry), completion);
576 
577  g_object_unref (completion);
578 }
579 
580 void
582 {
583  gtk_widget_grab_focus (proxy->entry);
584 }
585