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-history.c
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2005 Marco Pesenti Gritti
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2, or (at your option)
7  * any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  */
19 
20 #include "config.h"
21 
22 #include <glib/gi18n.h>
23 #include <string.h>
24 
25 #include "ev-history.h"
26 
27 enum {
30 
32 };
33 
34 #define EV_HISTORY_MAX_LENGTH (32)
35 
36 static guint signals[N_SIGNALS] = {0, };
37 
39  GList *list;
40  GList *current;
41 
44 
45  guint frozen;
46 };
47 
48 G_DEFINE_TYPE (EvHistory, ev_history, G_TYPE_OBJECT)
49 
50 static void ev_history_set_model (EvHistory *history,
51  EvDocumentModel *model);
52 
53 static void
54 clear_list (GList *list)
55 {
56  g_list_free_full (list, (GDestroyNotify) g_object_unref);
57 }
58 
59 static void
61 {
62  clear_list (history->priv->list);
63  history->priv->list = NULL;
64 
65  history->priv->current = NULL;
66 }
67 
68 static void
70 {
71  EvHistoryPrivate *priv = history->priv;
72  GList *l;
73  guint i;
74 
75  g_assert (priv->current->next == NULL);
76 
77  for (i = 0, l = priv->current; i < EV_HISTORY_MAX_LENGTH && l != NULL; i++, l = l->prev)
78  /* empty */;
79 
80  if (l == NULL)
81  return;
82 
83  /* Throw away all history up to @l */
84  l = l->next;
85  l->prev->next = NULL;
86  l->prev = NULL;
87 
88  clear_list (priv->list);
89  priv->list = l;
90 
91  g_assert (g_list_length (priv->list) == EV_HISTORY_MAX_LENGTH);
92 }
93 
94 static void
95 ev_history_finalize (GObject *object)
96 {
97  EvHistory *history = EV_HISTORY (object);
98 
99  ev_history_clear (history);
100  ev_history_set_model (history, NULL);
101 
102  G_OBJECT_CLASS (ev_history_parent_class)->finalize (object);
103 }
104 
105 static void
107 {
108  GObjectClass *object_class = G_OBJECT_CLASS (class);
109 
110  object_class->finalize = ev_history_finalize;
111 
112  signals[CHANGED] =
113  g_signal_new ("changed",
114  G_OBJECT_CLASS_TYPE (object_class),
115  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
116  G_STRUCT_OFFSET (EvHistoryClass, changed),
117  NULL, NULL,
118  g_cclosure_marshal_VOID__VOID,
119  G_TYPE_NONE, 0);
120 
122  g_signal_new ("activate-link",
123  G_OBJECT_CLASS_TYPE (object_class),
124  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
125  G_STRUCT_OFFSET (EvHistoryClass, activate_link),
126  NULL, NULL,
127  g_cclosure_marshal_VOID__OBJECT,
128  G_TYPE_NONE, 1,
129  G_TYPE_OBJECT);
130 
131  g_type_class_add_private (object_class, sizeof (EvHistoryPrivate));
132 }
133 
134 static void
136 {
137  history->priv = G_TYPE_INSTANCE_GET_PRIVATE (history, EV_TYPE_HISTORY, EvHistoryPrivate);
138 }
139 
140 gboolean
142 {
143  return history->priv->frozen > 0;
144 }
145 
146 void
148  EvLink *link)
149 {
150  EvHistoryPrivate *priv;
151 
152  g_return_if_fail (EV_IS_HISTORY (history));
153  g_return_if_fail (EV_IS_LINK (link));
154 
155  if (ev_history_is_frozen (history))
156  return;
157 
158  priv = history->priv;
159 
160  if (priv->current) {
161  /* Truncate forward history at @current */
162  clear_list (priv->current->next);
163  priv->current->next = NULL;
164  }
165 
166  /* Push @link to the list */
167  priv->current = g_list_append (NULL, g_object_ref (link));
168  priv->list = g_list_concat (priv->list, priv->current);
169 
170  ev_history_prune (history);
171 
172  g_signal_emit (history, signals[CHANGED], 0);
173 }
174 
175 static void
177 {
178  g_assert (history->priv->current);
179 
180  ev_history_freeze (history);
181  g_signal_handler_block (history->priv->model, history->priv->page_changed_handler_id);
182  g_signal_emit (history, signals[ACTIVATE_LINK], 0, history->priv->current->data);
183  g_signal_handler_unblock (history->priv->model, history->priv->page_changed_handler_id);
184  ev_history_thaw (history);
185 
186  g_signal_emit (history, signals[CHANGED], 0);
187 }
188 
189 gboolean
191 {
192  EvHistoryPrivate *priv;
193 
194  g_return_val_if_fail (EV_IS_HISTORY (history), FALSE);
195 
196  if (ev_history_is_frozen (history))
197  return FALSE;
198 
199  priv = history->priv;
200  return priv->current && priv->current->prev;
201 }
202 
203 void
205 {
206  EvHistoryPrivate *priv;
207 
208  g_return_if_fail (EV_IS_HISTORY (history));
209 
210  if (!ev_history_can_go_back (history))
211  return;
212 
213  priv = history->priv;
214 
215  /* Move current back one step */
216  priv->current = priv->current->prev;
217 
219 }
220 
221 gboolean
223 {
224  EvHistoryPrivate *priv;
225 
226  g_return_val_if_fail (EV_IS_HISTORY (history), FALSE);
227 
228  if (ev_history_is_frozen (history))
229  return FALSE;
230 
231  priv = history->priv;
232  return priv->current && priv->current->next;
233 }
234 
235 void
237 {
238  EvHistoryPrivate *priv;
239 
240  g_return_if_fail (EV_IS_HISTORY (history));
241 
242  if (!ev_history_can_go_forward (history))
243  return;
244 
245  priv = history->priv;
246 
247  /* Move current forward one step */
248  priv->current = priv->current->next;
249 
251 }
252 
253 static gint
255  EvLink *b)
256 {
257  if (a == b)
258  return 0;
259 
261 }
262 
263 /*
264  * ev_history_go_to_link:
265  * @history: a #EvHistory
266  * @link: a #EvLink
267  *
268  * Goes to the link, if it is in the history.
269  *
270  * Returns: %TRUE if the link was in the history and history isn't frozen; %FALSE otherwise
271  */
272 gboolean
274  EvLink *link)
275 {
276  EvHistoryPrivate *priv;
277  GList *l;
278 
279  g_return_val_if_fail (EV_IS_HISTORY (history), FALSE);
280  g_return_val_if_fail (EV_IS_LINK (link), FALSE);
281 
282  if (ev_history_is_frozen (history))
283  return FALSE;
284 
285  priv = history->priv;
286 
287  l = g_list_find_custom (priv->list, link, (GCompareFunc) compare_link);
288  if (l == NULL)
289  return FALSE;
290 
291  /* Set the link as current */
292  priv->current = l;
293 
295 
296  return TRUE;
297 }
298 
305 GList *
307 {
308  EvHistoryPrivate *priv;
309  GList *list, *l;
310 
311  g_return_val_if_fail (EV_IS_HISTORY (history), NULL);
312 
313  priv = history->priv;
314 
315  if (priv->current == NULL)
316  return NULL;
317 
318  list = NULL;
319  for (l = priv->current->prev; l != NULL; l = l->prev)
320  list = g_list_prepend (list, l->data);
321 
322  return g_list_reverse (list);
323 }
324 
331 GList *
333 {
334  g_return_val_if_fail (EV_IS_HISTORY (history), NULL);
335 
336  return g_list_copy (history->priv->current->next);
337 }
338 
339 void
341 {
342  g_return_if_fail (EV_IS_HISTORY (history));
343 
344  history->priv->frozen++;
345 }
346 
347 void
349 {
350  g_return_if_fail (EV_IS_HISTORY (history));
351  g_return_if_fail (history->priv->frozen > 0);
352 
353  history->priv->frozen--;
354 }
355 
356 static gint
358 {
359  EvLink *link;
360  EvDocument *document;
361  EvLinkDest *dest;
362  EvLinkAction *action;
363 
364  if (!history->priv->current)
365  return -1;
366 
367  link = history->priv->current->data;
368  action = ev_link_get_action (link);
369  if (!action)
370  return -1;
371 
372  dest = ev_link_action_get_dest (action);
373  if (!dest)
374  return -1;
375 
376  switch (ev_link_dest_get_dest_type (dest)) {
378  document = ev_document_model_get_document (history->priv->model);
379  if (!EV_IS_DOCUMENT_LINKS (document))
380  return -1;
381 
385  gint page = -1;
386 
387  document = ev_document_model_get_document (history->priv->model);
390  &page);
391 
392  return page;
393  }
394  default:
395  return ev_link_dest_get_page (dest);
396  }
397 
398  return -1;
399 }
400 
401 static void
403  gint page)
404 {
405  EvDocument *document;
406  EvLinkDest *dest;
407  EvLinkAction *action;
408  EvLink *link;
409  gchar *page_label;
410  gchar *title;
411 
412  if (ev_history_is_frozen (history))
413  return;
414 
415  if (ev_history_get_current_page (history) == page)
416  return;
417 
418  document = ev_document_model_get_document (history->priv->model);
419  if (!document)
420  return;
421 
422  page_label = ev_document_get_page_label (document, page);
423  if (!page_label)
424  return;
425 
426  title = g_strdup_printf (_("Page %s"), page_label);
427  g_free (page_label);
428 
429  dest = ev_link_dest_new_page (page);
430  action = ev_link_action_new_dest (dest);
431  g_object_unref (dest);
432 
433  link = ev_link_new (title, action);
434  g_object_unref (action);
435  g_free (title);
436 
437  ev_history_add_link (history, link);
438  g_object_unref (link);
439 }
440 
441 static void
443  gint old_page,
444  gint new_page,
445  EvHistory *history)
446 {
447  if (ABS (new_page - old_page) > 1)
448  ev_history_add_link_for_page (history, new_page);
449 }
450 
451 static void
453  GParamSpec *pspec,
454  EvHistory *history)
455 {
456  ev_history_clear (history);
458 }
459 
460 static void
462  EvDocumentModel *model)
463 {
464  if (history->priv->model == model)
465  return;
466 
467  if (history->priv->model) {
468  g_object_remove_weak_pointer (G_OBJECT (history->priv->model),
469  (gpointer)&history->priv->model);
470 
471  if (history->priv->page_changed_handler_id) {
472  g_signal_handler_disconnect (history->priv->model,
473  history->priv->page_changed_handler_id);
474  history->priv->page_changed_handler_id = 0;
475  }
476  }
477 
478  history->priv->model = model;
479  if (!model)
480  return;
481 
482  g_object_add_weak_pointer (G_OBJECT (model),
483  (gpointer)&history->priv->model);
484 
485  g_signal_connect (history->priv->model, "notify::document",
486  G_CALLBACK (document_changed_cb),
487  history);
488  history->priv->page_changed_handler_id =
489  g_signal_connect (history->priv->model, "page-changed",
490  G_CALLBACK (page_changed_cb),
491  history);
492 }
493 
494 EvHistory *
496 {
497  EvHistory *history;
498 
499  g_return_val_if_fail (EV_IS_DOCUMENT_MODEL (model), NULL);
500 
501  history = EV_HISTORY (g_object_new (EV_TYPE_HISTORY, NULL));
502  ev_history_set_model (history, model);
503 
504  return history;
505 }