zathura
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros
zathura.c
Go to the documentation of this file.
1 /* See LICENSE file for license and copyright information */
2 
3 #define _BSD_SOURCE
4 #define _XOPEN_SOURCE 700
5 
6 #include <errno.h>
7 #include <stdlib.h>
8 #include <unistd.h>
9 #include <math.h>
10 
11 #include <girara/datastructures.h>
12 #include <girara/utils.h>
13 #include <girara/session.h>
14 #include <girara/statusbar.h>
15 #include <girara/settings.h>
16 #include <girara/shortcuts.h>
17 #include <glib/gstdio.h>
18 #include <glib/gi18n.h>
19 
20 #include "bookmarks.h"
21 #include "callbacks.h"
22 #include "config.h"
23 #ifdef WITH_SQLITE
24 #include "database-sqlite.h"
25 #endif
26 #include "database-plain.h"
27 #include "document.h"
28 #include "shortcuts.h"
29 #include "zathura.h"
30 #include "utils.h"
31 #include "marks.h"
32 #include "render.h"
33 #include "page.h"
34 #include "page-widget.h"
35 #include "plugin.h"
36 #include "adjustment.h"
37 #include "dbus-interface.h"
38 
39 typedef struct zathura_document_info_s {
40  zathura_t* zathura;
41  const char* path;
42  const char* password;
44  const char* mode;
46 
47 
48 static gboolean document_info_open(gpointer data);
49 static void zathura_jumplist_reset_current(zathura_t* zathura);
50 static void zathura_jumplist_append_jump(zathura_t* zathura);
51 static void zathura_jumplist_save(zathura_t* zathura);
52 
53 /* function implementation */
54 zathura_t*
56 {
57  zathura_t* zathura = g_try_malloc0(sizeof(zathura_t));
58  if (zathura == NULL) {
59  return NULL;
60  }
61 
62  /* global settings */
63  zathura->global.search_direction = FORWARD;
64 
65  /* plugins */
66  zathura->plugins.manager = zathura_plugin_manager_new();
67  if (zathura->plugins.manager == NULL) {
68  goto error_out;
69  }
70 
71  /* UI */
72  if ((zathura->ui.session = girara_session_create()) == NULL) {
73  goto error_out;
74  }
75 
76  zathura->ui.session->global.data = zathura;
77 
78  return zathura;
79 
80 error_out:
81 
82  zathura_free(zathura);
83 
84  return NULL;
85 }
86 
87 bool
88 zathura_init(zathura_t* zathura)
89 {
90  if (zathura == NULL) {
91  return false;
92  }
93 
94  /* create zathura (config/data) directory */
95  if (g_mkdir_with_parents(zathura->config.config_dir, 0771) == -1) {
96  girara_error("Could not create '%s': %s", zathura->config.config_dir, strerror(errno));
97  }
98 
99  if (g_mkdir_with_parents(zathura->config.data_dir, 0771) == -1) {
100  girara_error("Could not create '%s': %s", zathura->config.data_dir, strerror(errno));
101  }
102 
103  /* load plugins */
104  zathura_plugin_manager_load(zathura->plugins.manager);
105 
106  /* configuration */
107  config_load_default(zathura);
108  config_load_files(zathura);
109 
110  /* UI */
111  if (girara_session_init(zathura->ui.session, "zathura") == false) {
112  goto error_free;
113  }
114 
115  /* girara events */
116  zathura->ui.session->events.buffer_changed = cb_buffer_changed;
117  zathura->ui.session->events.unknown_command = cb_unknown_command;
118 
119  /* zathura signals */
120  zathura->signals.refresh_view = g_signal_new("refresh-view",
121  GTK_TYPE_WIDGET,
122  G_SIGNAL_RUN_LAST,
123  0,
124  NULL,
125  NULL,
126  g_cclosure_marshal_generic,
127  G_TYPE_NONE,
128  1,
129  G_TYPE_POINTER);
130 
131  g_signal_connect(G_OBJECT(zathura->ui.session->gtk.view), "refresh-view",
132  G_CALLBACK(cb_refresh_view), zathura);
133 
134  /* page view */
135  zathura->ui.page_widget = gtk_grid_new();
136  gtk_grid_set_row_homogeneous(GTK_GRID(zathura->ui.page_widget), TRUE);
137  gtk_grid_set_column_homogeneous(GTK_GRID(zathura->ui.page_widget), TRUE);
138  if (zathura->ui.page_widget == NULL) {
139  goto error_free;
140  }
141 
142  g_signal_connect(G_OBJECT(zathura->ui.session->gtk.window), "size-allocate", G_CALLBACK(cb_view_resized), zathura);
143 
144  GtkAdjustment* hadjustment = gtk_scrolled_window_get_hadjustment(
145  GTK_SCROLLED_WINDOW(zathura->ui.session->gtk.view));
146 
147  /* Connect hadjustment signals */
148  g_signal_connect(G_OBJECT(hadjustment), "value-changed",
149  G_CALLBACK(cb_view_hadjustment_value_changed), zathura);
150  g_signal_connect(G_OBJECT(hadjustment), "changed",
151  G_CALLBACK(cb_view_hadjustment_changed), zathura);
152 
153  GtkAdjustment* vadjustment = gtk_scrolled_window_get_vadjustment(
154  GTK_SCROLLED_WINDOW(zathura->ui.session->gtk.view));
155 
156  /* Connect vadjustment signals */
157  g_signal_connect(G_OBJECT(vadjustment), "value-changed",
158  G_CALLBACK(cb_view_vadjustment_value_changed), zathura);
159  g_signal_connect(G_OBJECT(vadjustment), "changed",
160  G_CALLBACK(cb_view_vadjustment_changed), zathura);
161 
162  /* page view alignment */
163  zathura->ui.page_widget_alignment = gtk_alignment_new(0.5, 0.5, 0, 0);
164  if (zathura->ui.page_widget_alignment == NULL) {
165  goto error_free;
166  }
167  gtk_container_add(GTK_CONTAINER(zathura->ui.page_widget_alignment), zathura->ui.page_widget);
168 
169  gtk_widget_set_hexpand_set(zathura->ui.page_widget_alignment, TRUE);
170  gtk_widget_set_hexpand(zathura->ui.page_widget_alignment, FALSE);
171  gtk_widget_set_vexpand_set(zathura->ui.page_widget_alignment, TRUE);
172  gtk_widget_set_vexpand(zathura->ui.page_widget_alignment, FALSE);
173 
174  gtk_widget_show(zathura->ui.page_widget);
175 
176  /* statusbar */
177  zathura->ui.statusbar.file = girara_statusbar_item_add(zathura->ui.session, TRUE, TRUE, TRUE, NULL);
178  if (zathura->ui.statusbar.file == NULL) {
179  goto error_free;
180  }
181 
182  zathura->ui.statusbar.buffer = girara_statusbar_item_add(zathura->ui.session, FALSE, FALSE, FALSE, NULL);
183  if (zathura->ui.statusbar.buffer == NULL) {
184  goto error_free;
185  }
186 
187  zathura->ui.statusbar.page_number = girara_statusbar_item_add(zathura->ui.session, FALSE, FALSE, FALSE, NULL);
188  if (zathura->ui.statusbar.page_number == NULL) {
189  goto error_free;
190  }
191 
192  girara_statusbar_item_set_text(zathura->ui.session, zathura->ui.statusbar.file, _("[No name]"));
193 
194  /* signals */
195  g_signal_connect(G_OBJECT(zathura->ui.session->gtk.window), "destroy", G_CALLBACK(cb_destroy), zathura);
196 
197  /* database */
198  char* database = NULL;
199  girara_setting_get(zathura->ui.session, "database", &database);
200 
201  if (g_strcmp0(database, "plain") == 0) {
202  girara_debug("Using plain database backend.");
203  zathura->database = zathura_plaindatabase_new(zathura->config.data_dir);
204 #ifdef WITH_SQLITE
205  } else if (g_strcmp0(database, "sqlite") == 0) {
206  girara_debug("Using sqlite database backend.");
207  char* tmp = g_build_filename(zathura->config.data_dir, "bookmarks.sqlite", NULL);
208  zathura->database = zathura_sqldatabase_new(tmp);
209  g_free(tmp);
210 #endif
211  } else {
212  girara_error("Database backend '%s' is not supported.", database);
213  }
214  g_free(database);
215 
216  if (zathura->database == NULL) {
217  girara_error("Unable to initialize database. Bookmarks won't be available.");
218  } else {
219  g_object_set(zathura->ui.session->command_history, "io", zathura->database, NULL);
220  }
221 
222  /* bookmarks */
223  zathura->bookmarks.bookmarks = girara_sorted_list_new2((girara_compare_function_t) zathura_bookmarks_compare,
224  (girara_free_function_t) zathura_bookmark_free);
225 
226  /* jumplist */
227  int jumplist_size = 20;
228  girara_setting_get(zathura->ui.session, "jumplist-size", &jumplist_size);
229 
230  zathura->jumplist.max_size = jumplist_size < 0 ? 0 : jumplist_size;
231  zathura->jumplist.list = NULL;
232  zathura->jumplist.size = 0;
233  zathura->jumplist.cur = NULL;
234 
235  /* Start D-Bus service */
236  bool dbus = true;
237  girara_setting_get(zathura->ui.session, "dbus-service", &dbus);
238  if (dbus == true) {
239  zathura->dbus = zathura_dbus_new(zathura);
240  }
241 
242  return true;
243 
244 error_free:
245 
246  if (zathura->ui.page_widget != NULL) {
247  g_object_unref(zathura->ui.page_widget);
248  }
249 
250  if (zathura->ui.page_widget_alignment != NULL) {
251  g_object_unref(zathura->ui.page_widget_alignment);
252  }
253 
254  return false;
255 }
256 
257 void
258 zathura_free(zathura_t* zathura)
259 {
260  if (zathura == NULL) {
261  return;
262  }
263 
264  document_close(zathura, false);
265 
266  /* stop D-Bus */
267  if (zathura->dbus != NULL) {
268  g_object_unref(zathura->dbus);
269  zathura->dbus = NULL;
270  }
271 
272  if (zathura->ui.session != NULL) {
273  girara_session_destroy(zathura->ui.session);
274  }
275 
276  /* stdin support */
277  if (zathura->stdin_support.file != NULL) {
278  g_unlink(zathura->stdin_support.file);
279  g_free(zathura->stdin_support.file);
280  }
281 
282  /* bookmarks */
283  girara_list_free(zathura->bookmarks.bookmarks);
284 
285  /* database */
286  if (zathura->database != NULL) {
287  g_object_unref(G_OBJECT(zathura->database));
288  }
289 
290  /* free print settings */
291  if (zathura->print.settings != NULL) {
292  g_object_unref(zathura->print.settings);
293  }
294 
295  if (zathura->print.page_setup != NULL) {
296  g_object_unref(zathura->print.page_setup);
297  }
298 
299  /* free registered plugins */
300  zathura_plugin_manager_free(zathura->plugins.manager);
301 
302  /* free config variables */
303  g_free(zathura->config.config_dir);
304  g_free(zathura->config.data_dir);
305 
306  /* free jumplist */
307  if (zathura->jumplist.list != NULL) {
308  girara_list_free(zathura->jumplist.list);
309  }
310 
311  if (zathura->jumplist.cur != NULL) {
312  girara_list_iterator_free(zathura->jumplist.cur);
313  }
314 
315  g_free(zathura);
316 }
317 
318 void
319 zathura_set_xid(zathura_t* zathura, Window xid)
320 {
321  g_return_if_fail(zathura != NULL);
322 
323  zathura->ui.session->gtk.embed = xid;
324 }
325 
326 void
327 zathura_set_config_dir(zathura_t* zathura, const char* dir)
328 {
329  g_return_if_fail(zathura != NULL);
330 
331  if (dir != NULL) {
332  zathura->config.config_dir = g_strdup(dir);
333  } else {
334  gchar* path = girara_get_xdg_path(XDG_CONFIG);
335  zathura->config.config_dir = g_build_filename(path, "zathura", NULL);
336  g_free(path);
337  }
338 }
339 
340 void
341 zathura_set_data_dir(zathura_t* zathura, const char* dir)
342 {
343  if (dir != NULL) {
344  zathura->config.data_dir = g_strdup(dir);
345  } else {
346  gchar* path = girara_get_xdg_path(XDG_DATA);
347  zathura->config.data_dir = g_build_filename(path, "zathura", NULL);
348  g_free(path);
349  }
350 
351  g_return_if_fail(zathura != NULL);
352 }
353 
354 void
355 zathura_set_plugin_dir(zathura_t* zathura, const char* dir)
356 {
357  g_return_if_fail(zathura != NULL);
358  g_return_if_fail(zathura->plugins.manager != NULL);
359 
360  if (dir != NULL) {
361  girara_list_t* paths = girara_split_path_array(dir);
362  GIRARA_LIST_FOREACH(paths, char*, iter, path)
363  zathura_plugin_manager_add_dir(zathura->plugins.manager, path);
364  GIRARA_LIST_FOREACH_END(paths, char*, iter, path);
365  girara_list_free(paths);
366  } else {
367 #ifdef ZATHURA_PLUGINDIR
368  girara_list_t* paths = girara_split_path_array(ZATHURA_PLUGINDIR);
369  GIRARA_LIST_FOREACH(paths, char*, iter, path)
370  zathura_plugin_manager_add_dir(zathura->plugins.manager, path);
371  GIRARA_LIST_FOREACH_END(paths, char*, iter, path);
372  girara_list_free(paths);
373 #endif
374  }
375 
376 }
377 
378 void
379 zathura_set_synctex_editor_command(zathura_t* zathura, const char* command)
380 {
381  g_return_if_fail(zathura != NULL);
382 
383  if (zathura->synctex.editor != NULL) {
384  g_free(zathura->synctex.editor);
385  }
386 
387  if (command != NULL) {
388  zathura->synctex.editor = g_strdup(command);
389  } else {
390  zathura->synctex.editor = NULL;
391  }
392 }
393 
394 void
395 zathura_set_synctex(zathura_t* zathura, bool value)
396 {
397  g_return_if_fail(zathura != NULL);
398  g_return_if_fail(zathura->ui.session != NULL);
399 
400  girara_setting_set(zathura->ui.session, "synctex", &value);
401 }
402 
403 void
404 zathura_set_argv(zathura_t* zathura, char** argv)
405 {
406  g_return_if_fail(zathura != NULL);
407 
408  zathura->global.arguments = argv;
409 }
410 
411 static gchar*
412 prepare_document_open_from_stdin(zathura_t* zathura)
413 {
414  g_return_val_if_fail(zathura, NULL);
415 
416  GError* error = NULL;
417  gchar* file = NULL;
418  gint handle = g_file_open_tmp("zathura.stdin.XXXXXX", &file, &error);
419  if (handle == -1) {
420  if (error != NULL) {
421  girara_error("Can not create temporary file: %s", error->message);
422  g_error_free(error);
423  }
424  return NULL;
425  }
426 
427  // read from stdin and dump to temporary file
428  int stdinfno = fileno(stdin);
429  if (stdinfno == -1) {
430  girara_error("Can not read from stdin.");
431  close(handle);
432  g_unlink(file);
433  g_free(file);
434  return NULL;
435  }
436 
437  char buffer[BUFSIZ];
438  ssize_t count = 0;
439  while ((count = read(stdinfno, buffer, BUFSIZ)) > 0) {
440  if (write(handle, buffer, count) != count) {
441  girara_error("Can not write to temporary file: %s", file);
442  close(handle);
443  g_unlink(file);
444  g_free(file);
445  return NULL;
446  }
447  }
448 
449  close(handle);
450 
451  if (count != 0) {
452  girara_error("Can not read from stdin.");
453  g_unlink(file);
454  g_free(file);
455  return NULL;
456  }
457 
458  return file;
459 }
460 
461 static gboolean
462 document_info_open(gpointer data)
463 {
464  zathura_document_info_t* document_info = data;
465  g_return_val_if_fail(document_info != NULL, FALSE);
466 
467  if (document_info->zathura != NULL && document_info->path != NULL) {
468  char* file = NULL;
469  if (g_strcmp0(document_info->path, "-") == 0) {
470  file = prepare_document_open_from_stdin(document_info->zathura);
471  if (file == NULL) {
472  girara_notify(document_info->zathura->ui.session, GIRARA_ERROR,
473  _("Could not read file from stdin and write it to a temporary file."));
474  } else {
475  document_info->zathura->stdin_support.file = g_strdup(file);
476  }
477  } else {
478  file = g_strdup(document_info->path);
479  }
480 
481  if (file != NULL) {
482  document_open(document_info->zathura, file, document_info->password,
483  document_info->page_number);
484  g_free(file);
485 
486  if (document_info->mode != NULL) {
487  if (g_strcmp0(document_info->mode, "presentation") == 0) {
488  sc_toggle_presentation(document_info->zathura->ui.session, NULL, NULL,
489  0);
490  } else if (g_strcmp0(document_info->mode, "fullscreen") == 0) {
491  sc_toggle_fullscreen(document_info->zathura->ui.session, NULL, NULL,
492  0);
493  } else {
494  girara_error("Unknown mode: %s", document_info->mode);
495  }
496  }
497  }
498  }
499 
500  g_free(document_info);
501  return FALSE;
502 }
503 
504 bool
505 document_open(zathura_t* zathura, const char* path, const char* password,
506  int page_number)
507 {
508  if (zathura == NULL || zathura->plugins.manager == NULL || path == NULL) {
509  goto error_out;
510  }
511 
512  gchar* file_uri = NULL;
514  zathura_document_t* document = zathura_document_open(zathura->plugins.manager, path, password, &error);
515 
516  if (document == NULL) {
517  if (error == ZATHURA_ERROR_INVALID_PASSWORD) {
518  zathura_password_dialog_info_t* password_dialog_info = malloc(sizeof(zathura_password_dialog_info_t));
519  if (password_dialog_info != NULL) {
520  password_dialog_info->zathura = zathura;
521 
522  if (path != NULL) {
523  password_dialog_info->path = g_strdup(path);
524  girara_dialog(zathura->ui.session, "Enter password:", true, NULL,
525  (girara_callback_inputbar_activate_t) cb_password_dialog, password_dialog_info);
526  goto error_out;
527  } else {
528  free(password_dialog_info);
529  }
530  }
531  goto error_out;
532  }
533  if (error == ZATHURA_ERROR_OK ) {
534  girara_notify(zathura->ui.session, GIRARA_ERROR, _("Unsupported file type. Please install the necessary plugin."));
535  }
536  goto error_out;
537  }
538 
539  const char* file_path = zathura_document_get_path(document);
540  unsigned int number_of_pages = zathura_document_get_number_of_pages(document);
541 
542  if (number_of_pages == 0) {
543  girara_notify(zathura->ui.session, GIRARA_WARNING,
544  _("Document does not contain any pages"));
545  goto error_free;
546  }
547 
548  /* read history file */
549  zathura_fileinfo_t file_info = {
550  .current_page = 0,
551  .page_offset = 0,
552  .scale = 1,
553  .rotation = 0,
554  .pages_per_row = 0,
555  .first_page_column = 0,
556  .position_x = 0,
557  .position_y = 0
558  };
559  const bool known_file = zathura_db_get_fileinfo(zathura->database, file_path, &file_info);
560 
561  /* set page offset */
562  zathura_document_set_page_offset(document, file_info.page_offset);
563 
564  /* check for valid scale value */
565  if (file_info.scale <= FLT_EPSILON) {
566  girara_warning("document info: '%s' has non positive scale", file_path);
567  zathura_document_set_scale(document, 1);
568  } else {
569  zathura_document_set_scale(document, file_info.scale);
570  }
571 
572  /* check current page number */
573  /* if it wasn't specified on the command-line, get it from file_info */
574  if (page_number == ZATHURA_PAGE_NUMBER_UNSPECIFIED)
575  page_number = file_info.current_page;
576  if (page_number < 0)
577  page_number += number_of_pages;
578  if ((unsigned)page_number > number_of_pages) {
579  girara_warning("document info: '%s' has an invalid page number", file_path);
581  } else {
582  zathura_document_set_current_page_number(document, page_number);
583  }
584 
585  /* check for valid rotation */
586  if (file_info.rotation % 90 != 0) {
587  girara_warning("document info: '%s' has an invalid rotation", file_path);
588  zathura_document_set_rotation(document, 0);
589  } else {
590  zathura_document_set_rotation(document, file_info.rotation % 360);
591  }
592 
593  /* jump to first page if setting enabled */
594  bool always_first_page = false;
595  girara_setting_get(zathura->ui.session, "open-first-page", &always_first_page);
596  if (always_first_page == true) {
598  }
599 
600  /* apply open adjustment */
601  char* adjust_open = "best-fit";
602  if (known_file == false && girara_setting_get(zathura->ui.session, "adjust-open", &(adjust_open)) == true) {
603  if (g_strcmp0(adjust_open, "best-fit") == 0) {
605  } else if (g_strcmp0(adjust_open, "width") == 0) {
607  } else {
609  }
610  g_free(adjust_open);
611  } else {
613  }
614 
615  /* initialize bisect state */
616  zathura->bisect.start = 0;
617  zathura->bisect.last_jump = zathura_document_get_current_page_number(document);
618  zathura->bisect.end = number_of_pages - 1;
619 
620  /* update statusbar */
621  bool basename_only = false;
622  girara_setting_get(zathura->ui.session, "statusbar-basename", &basename_only);
623  if (basename_only == false) {
624  girara_statusbar_item_set_text(zathura->ui.session, zathura->ui.statusbar.file, file_path);
625  } else {
626  girara_statusbar_item_set_text(zathura->ui.session, zathura->ui.statusbar.file, zathura_document_get_basename(document));
627  }
628 
629  /* install file monitor */
630  file_uri = g_filename_to_uri(file_path, NULL, NULL);
631  if (file_uri == NULL) {
632  goto error_free;
633  }
634 
635  if (zathura->file_monitor.file == NULL) {
636  zathura->file_monitor.file = g_file_new_for_uri(file_uri);
637  if (zathura->file_monitor.file == NULL) {
638  goto error_free;
639  }
640  }
641 
642  if (zathura->file_monitor.monitor == NULL) {
643  zathura->file_monitor.monitor = g_file_monitor_file(zathura->file_monitor.file, G_FILE_MONITOR_NONE, NULL, NULL);
644  if (zathura->file_monitor.monitor == NULL) {
645  goto error_free;
646  }
647  g_signal_connect(G_OBJECT(zathura->file_monitor.monitor), "changed", G_CALLBACK(cb_file_monitor), zathura->ui.session);
648  }
649 
650  if (zathura->file_monitor.file_path == NULL) {
651  zathura->file_monitor.file_path = g_strdup(file_path);
652  if (zathura->file_monitor.file_path == NULL) {
653  goto error_free;
654  }
655  }
656 
657  if (password != NULL) {
658  g_free(zathura->file_monitor.password);
659  zathura->file_monitor.password = g_strdup(password);
660  if (zathura->file_monitor.password == NULL) {
661  goto error_free;
662  }
663  }
664 
665  /* create marks list */
666  zathura->global.marks = girara_list_new2((girara_free_function_t) mark_free);
667  if (zathura->global.marks == NULL) {
668  goto error_free;
669  }
670 
671  zathura->document = document;
672 
673  /* page cache size */
674  int cache_size = 0;
675  girara_setting_get(zathura->ui.session, "page-cache-size", &cache_size);
676  if (cache_size <= 0) {
677  girara_warning("page-cache-size is not positive, using %d instead",
679  cache_size = ZATHURA_PAGE_CACHE_DEFAULT_SIZE;
680  }
681 
682  /* threads */
683  zathura->sync.render_thread = zathura_renderer_new(cache_size);
684 
685  if (zathura->sync.render_thread == NULL) {
686  goto error_free;
687  }
688 
689  /* set up recolor info in ZathuraRenderer */
690  char* recolor_dark = NULL;
691  char* recolor_light = NULL;
692  girara_setting_get(zathura->ui.session, "recolor-darkcolor", &recolor_dark);
693  girara_setting_get(zathura->ui.session, "recolor-lightcolor", &recolor_light);
694  zathura_renderer_set_recolor_colors_str(zathura->sync.render_thread,
695  recolor_light, recolor_dark);
696  g_free(recolor_dark);
697  g_free(recolor_light);
698 
699  bool recolor = false;
700  girara_setting_get(zathura->ui.session, "recolor", &recolor);
701  zathura_renderer_enable_recolor(zathura->sync.render_thread, recolor);
702  girara_setting_get(zathura->ui.session, "recolor-keephue", &recolor);
703  zathura_renderer_enable_recolor_hue(zathura->sync.render_thread, recolor);
704 
705  /* get view port size */
706  GtkAdjustment* hadjustment = gtk_scrolled_window_get_hadjustment(
707  GTK_SCROLLED_WINDOW(zathura->ui.session->gtk.view));
708  GtkAdjustment* vadjustment = gtk_scrolled_window_get_vadjustment(
709  GTK_SCROLLED_WINDOW(zathura->ui.session->gtk.view));
710 
711  const unsigned int view_width = (unsigned int)floor(gtk_adjustment_get_page_size(hadjustment));
712  zathura_document_set_viewport_width(zathura->document, view_width);
713  const unsigned int view_height = (unsigned int)floor(gtk_adjustment_get_page_size(vadjustment));
714  zathura_document_set_viewport_height(zathura->document, view_height);
715 
716  /* create blank pages */
717  zathura->pages = calloc(number_of_pages, sizeof(GtkWidget*));
718  if (zathura->pages == NULL) {
719  goto error_free;
720  }
721 
722  for (unsigned int page_id = 0; page_id < number_of_pages; page_id++) {
723  zathura_page_t* page = zathura_document_get_page(document, page_id);
724  if (page == NULL) {
725  goto error_free;
726  }
727 
728  GtkWidget* page_widget = zathura_page_widget_new(zathura, page);
729  if (page_widget == NULL) {
730  goto error_free;
731  }
732 
733  g_object_ref(page_widget);
734  zathura->pages[page_id] = page_widget;
735 
736  g_signal_connect(G_OBJECT(page_widget), "text-selected",
737  G_CALLBACK(cb_page_widget_text_selected), zathura);
738  g_signal_connect(G_OBJECT(page_widget), "image-selected",
739  G_CALLBACK(cb_page_widget_image_selected), zathura);
740  }
741 
742  /* view mode */
743  unsigned int pages_per_row = 1;
744  unsigned int first_page_column = 1;
745  unsigned int page_padding = 1;
746 
747  girara_setting_get(zathura->ui.session, "page-padding", &page_padding);
748 
749  if (file_info.pages_per_row > 0) {
750  pages_per_row = file_info.pages_per_row;
751  } else {
752  girara_setting_get(zathura->ui.session, "pages-per-row", &pages_per_row);
753  }
754 
755  if (file_info.first_page_column > 0) {
756  first_page_column = file_info.first_page_column;
757  } else {
758  girara_setting_get(zathura->ui.session, "first-page-column", &first_page_column);
759  }
760 
761  girara_setting_set(zathura->ui.session, "pages-per-row", &pages_per_row);
762  girara_setting_set(zathura->ui.session, "first-page-column", &first_page_column);
763 
764  page_widget_set_mode(zathura, page_padding, pages_per_row, first_page_column);
765  zathura_document_set_page_layout(zathura->document, page_padding, pages_per_row, first_page_column);
766 
767  girara_set_view(zathura->ui.session, zathura->ui.page_widget_alignment);
768 
769 
770  /* bookmarks */
771  zathura_bookmarks_load(zathura, file_path);
772 
773  /* jumplist */
774  if (zathura_jumplist_load(zathura, file_path) == false) {
775  zathura->jumplist.list = girara_list_new2(g_free);
776  }
777 
778  /* update title */
779  basename_only = false;
780  girara_setting_get(zathura->ui.session, "window-title-basename", &basename_only);
781  if (basename_only == false) {
782  girara_set_window_title(zathura->ui.session, file_path);
783  } else {
784  girara_set_window_title(zathura->ui.session, zathura_document_get_basename(document));
785  }
786 
787  g_free(file_uri);
788 
789  /* adjust_view */
790  adjust_view(zathura);
791  for (unsigned int page_id = 0; page_id < number_of_pages; page_id++) {
792  /* set widget size */
793  zathura_page_t* page = zathura_document_get_page(document, page_id);
794  unsigned int page_height = 0;
795  unsigned int page_width = 0;
796 
797  /* adjust_view calls render_all in some cases and render_all calls
798  * gtk_widget_set_size_request. To be sure that it's really called, do it
799  * here once again. */
800  const double height = zathura_page_get_height(page);
801  const double width = zathura_page_get_width(page);
802  page_calc_height_width(zathura->document, height, width, &page_height, &page_width, true);
803  gtk_widget_set_size_request(zathura->pages[page_id], page_width, page_height);
804 
805  /* show widget */
806  gtk_widget_show(zathura->pages[page_id]);
807  }
808 
809  /* Set page */
811 
812  /* Set position (only if restoring from history file) */
813  if (file_info.current_page == zathura_document_get_current_page_number(document) &&
814  (file_info.position_x != 0 || file_info.position_y != 0)) {
815  position_set(zathura, file_info.position_x, file_info.position_y);
816  }
817 
818  return true;
819 
820 error_free:
821 
822  if (file_uri != NULL) {
823  g_free(file_uri);
824  }
825 
826  zathura_document_free(document);
827 
828 error_out:
829 
830  return false;
831 }
832 
833 void
834 document_open_idle(zathura_t* zathura, const char* path, const char* password,
835  int page_number, const char* mode)
836 {
837  if (zathura == NULL || path == NULL) {
838  return;
839  }
840 
841  zathura_document_info_t* document_info = g_try_malloc0(sizeof(zathura_document_info_t));
842  if (document_info == NULL) {
843  return;
844  }
845 
846  document_info->zathura = zathura;
847  document_info->path = path;
848  document_info->password = password;
849  document_info->page_number = page_number;
850  document_info->mode = mode;
851 
852  gdk_threads_add_idle(document_info_open, document_info);
853 }
854 
855 bool
856 document_save(zathura_t* zathura, const char* path, bool overwrite)
857 {
858  g_return_val_if_fail(zathura, false);
859  g_return_val_if_fail(zathura->document, false);
860  g_return_val_if_fail(path, false);
861 
862  gchar* file_path = girara_fix_path(path);
863  /* use current basename if path points to a directory */
864  if (g_file_test(file_path, G_FILE_TEST_IS_DIR) == TRUE) {
865  char* basename = g_path_get_basename(zathura_document_get_path(zathura->document));
866  char* tmp = file_path;
867  file_path = g_strconcat(file_path, "/", basename, NULL);
868  g_free(tmp);
869  g_free(basename);
870  }
871 
872  if ((overwrite == false) && g_file_test(file_path, G_FILE_TEST_EXISTS)) {
873  girara_error("File already exists: %s. Use :write! to overwrite it.", file_path);
874  g_free(file_path);
875  return false;
876  }
877 
878  zathura_error_t error = zathura_document_save_as(zathura->document, file_path);
879  g_free(file_path);
880 
881  return (error == ZATHURA_ERROR_OK) ? true : false;
882 }
883 
884 static void
885 remove_page_from_table(GtkWidget* page, gpointer permanent)
886 {
887  if ((bool)permanent == false) {
888  g_object_ref(G_OBJECT(page));
889  }
890 
891  gtk_container_remove(GTK_CONTAINER(gtk_widget_get_parent(page)), page);
892 }
893 
894 bool
895 document_close(zathura_t* zathura, bool keep_monitor)
896 {
897  if (zathura == NULL || zathura->document == NULL) {
898  return false;
899  }
900 
901  /* stop rendering */
902  zathura_renderer_stop(zathura->sync.render_thread);
903 
904  /* remove monitor */
905  if (keep_monitor == false) {
906  if (zathura->file_monitor.monitor != NULL) {
907  g_file_monitor_cancel(zathura->file_monitor.monitor);
908  g_object_unref(zathura->file_monitor.monitor);
909  zathura->file_monitor.monitor = NULL;
910  }
911 
912  if (zathura->file_monitor.file != NULL) {
913  g_object_unref(zathura->file_monitor.file);
914  zathura->file_monitor.file = NULL;
915  }
916 
917  if (zathura->file_monitor.file_path != NULL) {
918  g_free(zathura->file_monitor.file_path);
919  zathura->file_monitor.file_path = NULL;
920  }
921 
922  if (zathura->file_monitor.password != NULL) {
923  g_free(zathura->file_monitor.password);
924  zathura->file_monitor.password = NULL;
925  }
926  }
927 
928  /* remove marks */
929  if (zathura->global.marks != NULL) {
930  girara_list_free(zathura->global.marks);
931  zathura->global.marks = NULL;
932  }
933 
934  /* store file information */
935  const char* path = zathura_document_get_path(zathura->document);
936 
937  zathura_fileinfo_t file_info = { 0, 0, 1, 0, 1, 1, 0, 0 };
938  file_info.current_page = zathura_document_get_current_page_number(zathura->document);
939  file_info.page_offset = zathura_document_get_page_offset(zathura->document);
940  file_info.scale = zathura_document_get_scale(zathura->document);
941  file_info.rotation = zathura_document_get_rotation(zathura->document);
942 
943  girara_setting_get(zathura->ui.session, "pages-per-row", &(file_info.pages_per_row));
944  girara_setting_get(zathura->ui.session, "first-page-column", &(file_info.first_page_column));
945 
946  /* get position */
947  file_info.position_x = zathura_document_get_position_x(zathura->document);
948  file_info.position_y = zathura_document_get_position_y(zathura->document);
949 
950  /* save file info */
951  zathura_db_set_fileinfo(zathura->database, path, &file_info);
952 
953  /* save jumplist */
954  zathura_db_save_jumplist(zathura->database, path, zathura->jumplist.list);
955  girara_list_iterator_free(zathura->jumplist.cur);
956  zathura->jumplist.cur = NULL;
957  girara_list_free(zathura->jumplist.list);
958  zathura->jumplist.list = NULL;
959  zathura->jumplist.size = 0;
960 
961  /* release render thread */
962  g_object_unref(zathura->sync.render_thread);
963  zathura->sync.render_thread = NULL;
964 
965  /* remove widgets */
966  gtk_container_foreach(GTK_CONTAINER(zathura->ui.page_widget), remove_page_from_table, (gpointer) true);
967  for (unsigned int i = 0; i < zathura_document_get_number_of_pages(zathura->document); i++) {
968  g_object_unref(zathura->pages[i]);
969  }
970  free(zathura->pages);
971  zathura->pages = NULL;
972 
973  /* remove document */
974  zathura_document_free(zathura->document);
975  zathura->document = NULL;
976 
977  /* remove index */
978  if (zathura->ui.index != NULL) {
979  g_object_ref_sink(zathura->ui.index);
980  zathura->ui.index = NULL;
981  }
982 
983  gtk_widget_hide(zathura->ui.page_widget);
984 
986 
987  if (zathura->ui.session != NULL && zathura->ui.statusbar.file != NULL) {
988  girara_statusbar_item_set_text(zathura->ui.session, zathura->ui.statusbar.file, _("[No name]"));
989  }
990 
991  /* update title */
992  girara_set_window_title(zathura->ui.session, "zathura");
993 
994  return true;
995 }
996 
997 bool
998 page_set(zathura_t* zathura, unsigned int page_id)
999 {
1000  if (zathura == NULL || zathura->document == NULL) {
1001  goto error_out;
1002  }
1003 
1004  zathura_page_t* page = zathura_document_get_page(zathura->document, page_id);
1005  if (page == NULL) {
1006  goto error_out;
1007  }
1008 
1009  zathura_document_set_current_page_number(zathura->document, page_id);
1010 
1011  /* negative position means auto */
1012  return position_set(zathura, -1, -1);
1013 
1014 error_out:
1015  return false;
1016 }
1017 
1018 void
1019 statusbar_page_number_update(zathura_t* zathura)
1020 {
1021  if (zathura == NULL || zathura->ui.statusbar.page_number == NULL) {
1022  return;
1023  }
1024 
1025  unsigned int number_of_pages = zathura_document_get_number_of_pages(zathura->document);
1026  unsigned int current_page_number = zathura_document_get_current_page_number(zathura->document);
1027 
1028  if (zathura->document != NULL) {
1029  char* page_number_text = g_strdup_printf("[%d/%d]", current_page_number + 1, number_of_pages);
1030  girara_statusbar_item_set_text(zathura->ui.session, zathura->ui.statusbar.page_number, page_number_text);
1031 
1032  bool page_number_in_window_title = false;
1033  girara_setting_get(zathura->ui.session, "window-title-page", &page_number_in_window_title);
1034 
1035  if (page_number_in_window_title == true) {
1036  bool basename_only = false;
1037  girara_setting_get(zathura->ui.session, "window-title-basename", &basename_only);
1038  char* title = g_strdup_printf("%s %s",
1039  (basename_only == true)
1040  ? zathura_document_get_basename(zathura->document)
1041  : zathura_document_get_path(zathura->document),
1042  page_number_text);
1043  girara_set_window_title(zathura->ui.session, title);
1044  g_free(title);
1045  }
1046 
1047  g_free(page_number_text);
1048  } else {
1049  girara_statusbar_item_set_text(zathura->ui.session, zathura->ui.statusbar.page_number, "");
1050  }
1051 }
1052 
1053 void
1054 page_widget_set_mode(zathura_t* zathura, unsigned int page_padding,
1055  unsigned int pages_per_row, unsigned int first_page_column)
1056 {
1057  /* show at least one page */
1058  if (pages_per_row == 0) {
1059  pages_per_row = 1;
1060  }
1061 
1062  /* ensure: 0 < first_page_column <= pages_per_row */
1063  if (first_page_column < 1) {
1064  first_page_column = 1;
1065  }
1066  if (first_page_column > pages_per_row) {
1067  first_page_column = ((first_page_column - 1) % pages_per_row) + 1;
1068  }
1069 
1070  if (zathura->document == NULL) {
1071  return;
1072  }
1073 
1074  gtk_container_foreach(GTK_CONTAINER(zathura->ui.page_widget), remove_page_from_table, (gpointer)0);
1075 
1076  unsigned int number_of_pages = zathura_document_get_number_of_pages(zathura->document);
1077 
1078  gtk_grid_set_row_spacing(GTK_GRID(zathura->ui.page_widget), page_padding);
1079  gtk_grid_set_column_spacing(GTK_GRID(zathura->ui.page_widget), page_padding);
1080 
1081  for (unsigned int i = 0; i < number_of_pages; i++) {
1082  int x = (i + first_page_column - 1) % pages_per_row;
1083  int y = (i + first_page_column - 1) / pages_per_row;
1084 
1085  GtkWidget* page_widget = zathura->pages[i];
1086 
1087  GtkWidget* align = gtk_alignment_new(0.5, 0.5, 0, 0);
1088  GtkWidget* parent = gtk_widget_get_parent(page_widget);
1089  if (parent)
1090  {
1091  gtk_container_remove(GTK_CONTAINER(parent), page_widget);
1092  g_object_unref(parent);
1093  }
1094 
1095  gtk_container_add(GTK_CONTAINER(align), page_widget);
1096  gtk_grid_attach(GTK_GRID(zathura->ui.page_widget), align, x, y, 1, 1);
1097  }
1098 
1099  gtk_widget_show_all(zathura->ui.page_widget);
1100 }
1101 
1102 bool
1103 position_set(zathura_t* zathura, double position_x, double position_y)
1104 {
1105  if (zathura == NULL || zathura->document == NULL) {
1106  return false;
1107  }
1108 
1109  double comppos_x, comppos_y;
1110  unsigned int page_id = zathura_document_get_current_page_number(zathura->document);
1111 
1112  /* xalign = 0.5: center horizontally (with the page, not the document) */
1113  /* yalign = 0.0: align page an viewport edges at the top */
1114  page_number_to_position(zathura->document, page_id, 0.5, 0.0, &comppos_x, &comppos_y);
1115 
1116  /* automatic horizontal adjustment */
1117  zathura_adjust_mode_t adjust_mode = zathura_document_get_adjust_mode(zathura->document);
1118 
1119  /* negative position_x mean: use the computed value */
1120  if (position_x < 0) {
1121  position_x = comppos_x;
1122  bool zoom_center = false;
1123  girara_setting_get(zathura->ui.session, "zoom-center", &zoom_center);
1124 
1125  /* center horizontally */
1126  if (adjust_mode == ZATHURA_ADJUST_BESTFIT ||
1127  adjust_mode == ZATHURA_ADJUST_WIDTH ||
1128  zoom_center) {
1129  position_x = 0.5;
1130  }
1131  }
1132 
1133  if (position_y < 0) {
1134  position_y = comppos_y;
1135  }
1136 
1137  /* set the position */
1138  zathura_document_set_position_x(zathura->document, position_x);
1139  zathura_document_set_position_y(zathura->document, position_y);
1140 
1141  /* trigger a 'change' event for both adjustments */
1142  refresh_view(zathura);
1143 
1144  return true;
1145 }
1146 
1147 
1148 void
1149 refresh_view(zathura_t* zathura) {
1150  g_return_if_fail(zathura != NULL);
1151 
1152  /* emit a custom refresh-view signal */
1153  g_signal_emit(zathura->ui.session->gtk.view, zathura->signals.refresh_view,
1154  0, zathura);
1155 }
1156 
1157 
1158 bool
1159 adjust_view(zathura_t* zathura) {
1160  g_return_val_if_fail(zathura != NULL, false);
1161 
1162  if (zathura->ui.page_widget == NULL || zathura->document == NULL) {
1163  goto error_ret;
1164  }
1165 
1166  zathura_adjust_mode_t adjust_mode = zathura_document_get_adjust_mode(zathura->document);
1167  if (adjust_mode == ZATHURA_ADJUST_NONE) {
1168  /* there is nothing todo */
1169  goto error_ret;
1170  }
1171 
1172  unsigned int cell_height = 0, cell_width = 0;
1173  unsigned int document_height = 0, document_width = 0;
1174  unsigned int view_height = 0, view_width = 0;
1175 
1176  zathura_document_get_cell_size(zathura->document, &cell_height, &cell_width);
1177  zathura_document_get_document_size(zathura->document, &document_height, &document_width);
1178  zathura_document_get_viewport_size(zathura->document, &view_height, &view_width);
1179 
1180  if (view_height == 0 || view_width == 0 || cell_height == 0 || cell_width == 0) {
1181  goto error_ret;
1182  }
1183 
1184  double page_ratio = (double)cell_height / (double)document_width;
1185  double view_ratio = (double)view_height / (double)view_width;
1186  double scale = zathura_document_get_scale(zathura->document);
1187  double newscale = scale;
1188 
1189  if (adjust_mode == ZATHURA_ADJUST_WIDTH ||
1190  (adjust_mode == ZATHURA_ADJUST_BESTFIT && page_ratio < view_ratio)) {
1191  newscale = scale * (double)view_width / (double)document_width;
1192 
1193  } else if (adjust_mode == ZATHURA_ADJUST_BESTFIT) {
1194  newscale = scale * (double)view_height / (double)cell_height;
1195 
1196  } else {
1197  goto error_ret;
1198  }
1199 
1200  /* save new scale and recompute cell size */
1201  zathura_document_set_scale(zathura->document, newscale);
1202  unsigned int new_cell_height = 0, new_cell_width = 0;
1203  zathura_document_get_cell_size(zathura->document, &new_cell_height, &new_cell_width);
1204 
1205  /* if the change in scale changes page cell dimensions by at least one pixel, render */
1206  if (abs(new_cell_width - cell_width) > 1 ||
1207  abs(new_cell_height - cell_height) > 1) {
1208  render_all(zathura);
1209  refresh_view(zathura);
1210 
1211  /* otherwise set the old scale and leave */
1212  } else {
1213  zathura_document_set_scale(zathura->document, scale);
1214  }
1215 
1216 error_ret:
1217  return false;
1218 }
1219 
1220 bool
1222 {
1223  return girara_list_iterator_has_previous(zathura->jumplist.cur);
1224 }
1225 
1226 bool
1227 zathura_jumplist_has_next(zathura_t* zathura)
1228 {
1229  return girara_list_iterator_has_next(zathura->jumplist.cur);
1230 }
1231 
1233 zathura_jumplist_current(zathura_t* zathura)
1234 {
1235  if (zathura->jumplist.cur != NULL) {
1236  return girara_list_iterator_data(zathura->jumplist.cur);
1237  } else {
1238  return NULL;
1239  }
1240 }
1241 
1242 void
1243 zathura_jumplist_forward(zathura_t* zathura)
1244 {
1245  if (girara_list_iterator_has_next(zathura->jumplist.cur)) {
1246  girara_list_iterator_next(zathura->jumplist.cur);
1247  }
1248 }
1249 
1250 void
1251 zathura_jumplist_backward(zathura_t* zathura)
1252 {
1253  if (girara_list_iterator_has_previous(zathura->jumplist.cur)) {
1254  girara_list_iterator_previous(zathura->jumplist.cur);
1255  }
1256 }
1257 
1258 static void
1259 zathura_jumplist_reset_current(zathura_t* zathura)
1260 {
1261  g_return_if_fail(zathura != NULL && zathura->jumplist.cur != NULL);
1262 
1263  while (true) {
1264  if (girara_list_iterator_has_next(zathura->jumplist.cur) == false) {
1265  return;
1266  }
1267 
1268  girara_list_iterator_next(zathura->jumplist.cur);
1269  }
1270 }
1271 
1272 static void
1273 zathura_jumplist_append_jump(zathura_t* zathura)
1274 {
1275  g_return_if_fail(zathura != NULL && zathura->jumplist.list != NULL);
1276 
1277  zathura_jump_t* jump = g_try_malloc0(sizeof(zathura_jump_t));
1278  if (jump == NULL) {
1279  return;
1280  }
1281 
1282  jump->page = 0;
1283  jump->x = 0.0;
1284  jump->y = 0.0;
1285  girara_list_append(zathura->jumplist.list, jump);
1286 
1287  if (zathura->jumplist.size == 0) {
1288  zathura->jumplist.cur = girara_list_iterator(zathura->jumplist.list);
1289  }
1290 
1291  ++zathura->jumplist.size;
1292  zathura_jumplist_trim(zathura);
1293 }
1294 
1295 void
1296 zathura_jumplist_trim(zathura_t* zathura)
1297 {
1298  g_return_if_fail(zathura != NULL && zathura->jumplist.list != NULL && zathura->jumplist.size != 0);
1299 
1300  girara_list_iterator_t* cur = girara_list_iterator(zathura->jumplist.list);
1301 
1302  while (zathura->jumplist.size > zathura->jumplist.max_size) {
1303  if (girara_list_iterator_data(cur) == girara_list_iterator_data(zathura->jumplist.cur)) {
1304  girara_list_iterator_free(zathura->jumplist.cur);
1305  zathura->jumplist.cur = NULL;
1306  }
1307 
1308  girara_list_iterator_remove(cur);
1309  --zathura->jumplist.size;
1310  }
1311 
1312  if (zathura->jumplist.size == 0 || (zathura->jumplist.size != 0 && zathura->jumplist.cur != NULL)) {
1313  girara_list_iterator_free(cur);
1314  } else {
1315  zathura->jumplist.cur = cur;
1316  }
1317 }
1318 
1319 void
1320 zathura_jumplist_add(zathura_t* zathura)
1321 {
1322  g_return_if_fail(zathura != NULL && zathura->document != NULL && zathura->jumplist.list != NULL);
1323 
1324  unsigned int pagenum = zathura_document_get_current_page_number(zathura->document);
1325  double x = zathura_document_get_position_x(zathura->document);
1326  double y = zathura_document_get_position_y(zathura->document);
1327 
1328  if (zathura->jumplist.size != 0) {
1329  zathura_jumplist_reset_current(zathura);
1330 
1331  zathura_jump_t* cur = zathura_jumplist_current(zathura);
1332 
1333  if (cur != NULL) {
1334  if (cur->page == pagenum && cur->x == x && cur->y == y) {
1335  return;
1336  }
1337  }
1338  }
1339 
1340  zathura_jumplist_append_jump(zathura);
1341  zathura_jumplist_reset_current(zathura);
1342  zathura_jumplist_save(zathura);
1343 }
1344 
1345 bool
1346 zathura_jumplist_load(zathura_t* zathura, const char* file)
1347 {
1348  g_return_val_if_fail(zathura != NULL && zathura->database != NULL && file != NULL, false);
1349 
1350  zathura->jumplist.list = zathura_db_load_jumplist(zathura->database, file);
1351 
1352  if (zathura->jumplist.list == NULL) {
1353  girara_error("Failed to load the jumplist from the database");
1354 
1355  return false;
1356  }
1357 
1358  zathura->jumplist.size = girara_list_size(zathura->jumplist.list);
1359 
1360  if (zathura->jumplist.size != 0) {
1361  zathura->jumplist.cur = girara_list_iterator(zathura->jumplist.list);
1362  zathura_jumplist_reset_current(zathura);
1363  zathura_jumplist_trim(zathura);
1364  girara_debug("Loaded the jumplist from the database");
1365  } else {
1366  girara_debug("No jumplist for this file in the database yet");
1367  }
1368 
1369  return true;
1370 }
1371 
1372 static void
1373 zathura_jumplist_save(zathura_t* zathura)
1374 {
1375  g_return_if_fail(zathura != NULL && zathura->document != NULL);
1376 
1377  zathura_jump_t* cur = zathura_jumplist_current(zathura);
1378 
1379  unsigned int pagenum = zathura_document_get_current_page_number(zathura->document);
1380 
1381  if (cur) {
1382  cur->page = pagenum;
1383  cur->x = zathura_document_get_position_x(zathura->document);
1384  cur->y = zathura_document_get_position_y(zathura->document);
1385  }
1386 }