zathura.c 33.5 KB
Newer Older
1
2
/* See LICENSE file for license and copyright information */

3
#define _BSD_SOURCE
Sebastian Ramacher's avatar
Sebastian Ramacher committed
4
#define _XOPEN_SOURCE 700
5

6
#include <errno.h>
Moritz Lipp's avatar
Moritz Lipp committed
7
#include <stdlib.h>
Pavel Borzenkov's avatar
Pavel Borzenkov committed
8
#include <unistd.h>
9
#include <math.h>
Moritz Lipp's avatar
Moritz Lipp committed
10

11
12
13
14
15
#include <girara/datastructures.h>
#include <girara/utils.h>
#include <girara/session.h>
#include <girara/statusbar.h>
#include <girara/settings.h>
16
#include <glib/gstdio.h>
17
#include <glib/gi18n.h>
18

19
#include "bookmarks.h"
Moritz Lipp's avatar
Moritz Lipp committed
20
21
#include "callbacks.h"
#include "config.h"
22
23
24
25
#ifdef WITH_SQLITE
#include "database-sqlite.h"
#endif
#include "database-plain.h"
26
#include "document.h"
Moritz Lipp's avatar
Moritz Lipp committed
27
#include "shortcuts.h"
Moritz Lipp's avatar
Moritz Lipp committed
28
#include "zathura.h"
Moritz Lipp's avatar
Moritz Lipp committed
29
#include "utils.h"
Moritz Lipp's avatar
Moritz Lipp committed
30
#include "marks.h"
Sebastian Ramacher's avatar
Sebastian Ramacher committed
31
#include "render.h"
Moritz Lipp's avatar
Moritz Lipp committed
32
#include "page.h"
33
#include "page-widget.h"
34
#include "plugin.h"
Moritz Lipp's avatar
Moritz Lipp committed
35

Moritz Lipp's avatar
Moritz Lipp committed
36
typedef struct zathura_document_info_s {
Moritz Lipp's avatar
Update    
Moritz Lipp committed
37
38
39
40
41
  zathura_t* zathura;
  const char* path;
  const char* password;
} zathura_document_info_t;

Moritz Lipp's avatar
Moritz Lipp committed
42
typedef struct page_set_delayed_s {
43
44
45
46
  zathura_t* zathura;
  unsigned int page;
} page_set_delayed_t;

Moritz Lipp's avatar
Moritz Lipp committed
47
typedef struct position_set_delayed_s {
48
  zathura_t* zathura;
Moritz Lipp's avatar
Moritz Lipp committed
49
50
  double position_x;
  double position_y;
51
52
} position_set_delayed_t;

53
static gboolean document_info_open(gpointer data);
54
static gboolean purge_pages(gpointer data);
Moritz Lipp's avatar
Moritz Lipp committed
55

Moritz Lipp's avatar
Moritz Lipp committed
56
/* function implementation */
Moritz Lipp's avatar
Moritz Lipp committed
57
zathura_t*
58
zathura_create(void)
59
{
60
  zathura_t* zathura = g_malloc0(sizeof(zathura_t));
61

62
63
64
  /* global settings */
  zathura->global.recolor            = false;
  zathura->global.update_page_number = true;
65
  zathura->global.search_direction = FORWARD;
66

67
  /* plugins */
Moritz Lipp's avatar
Moritz Lipp committed
68
69
  zathura->plugins.manager = zathura_plugin_manager_new();
  if (zathura->plugins.manager == NULL) {
70
    goto error_out;
Moritz Lipp's avatar
Moritz Lipp committed
71
  }
72

73
74
75
  /* UI */
  if ((zathura->ui.session = girara_session_create()) == NULL) {
    goto error_out;
Sebastian Ramacher's avatar
Sebastian Ramacher committed
76
77
  }

78
79
80
81
82
83
84
85
86
87
  zathura->ui.session->global.data = zathura;

  return zathura;

error_out:

  zathura_free(zathura);

  return NULL;
}
Sebastian Ramacher's avatar
Sebastian Ramacher committed
88

89
90
91
92
93
bool
zathura_init(zathura_t* zathura)
{
  if (zathura == NULL) {
    return false;
94
95
  }

96
  /* create zathura (config/data) directory */
97
98
99
100
101
102
103
  if (g_mkdir_with_parents(zathura->config.config_dir, 0771) == -1) {
    girara_error("Could not create '%s': %s", zathura->config.config_dir, strerror(errno));
  }

  if (g_mkdir_with_parents(zathura->config.data_dir, 0771) == -1) {
    girara_error("Could not create '%s': %s", zathura->config.data_dir, strerror(errno));
  }
104

105
106
107
  /* load plugins */
  zathura_plugin_manager_load(zathura->plugins.manager);

108
109
110
  /* configuration */
  config_load_default(zathura);

111
  /* load global configuration files */
Moritz Lipp's avatar
Moritz Lipp committed
112
113
  char* config_path = girara_get_xdg_path(XDG_CONFIG_DIRS);
  girara_list_t* config_dirs = girara_split_path_array(config_path);
114
115
116
117
118
119
120
121
  ssize_t size = girara_list_size(config_dirs) - 1;
  for (; size >= 0; --size) {
    const char* dir = girara_list_nth(config_dirs, size);
    char* file = g_build_filename(dir, ZATHURA_RC, NULL);
    config_load_file(zathura, file);
    g_free(file);
  }
  girara_list_free(config_dirs);
Moritz Lipp's avatar
Moritz Lipp committed
122
  g_free(config_path);
123
124
125

  config_load_file(zathura, GLOBAL_RC);

126
127
128
  /* load local configuration files */
  char* configuration_file = g_build_filename(zathura->config.config_dir, ZATHURA_RC, NULL);
  config_load_file(zathura, configuration_file);
129
  g_free(configuration_file);
130

131
  /* UI */
Sebastian Ramacher's avatar
Sebastian Ramacher committed
132
  if (girara_session_init(zathura->ui.session, "zathura") == false) {
Moritz Lipp's avatar
Moritz Lipp committed
133
    goto error_free;
134
135
136
  }

  /* girara events */
Moritz Lipp's avatar
Moritz Lipp committed
137
138
  zathura->ui.session->events.buffer_changed  = cb_buffer_changed;
  zathura->ui.session->events.unknown_command = cb_unknown_command;
139

140
  /* page view */
Moritz Lipp's avatar
Moritz Lipp committed
141
142
#if (GTK_MAJOR_VERSION == 3)
  zathura->ui.page_widget = gtk_grid_new();
143
144
  gtk_grid_set_row_homogeneous(GTK_GRID(zathura->ui.page_widget), TRUE);
  gtk_grid_set_column_homogeneous(GTK_GRID(zathura->ui.page_widget), TRUE);
Moritz Lipp's avatar
Moritz Lipp committed
145
#else
146
  zathura->ui.page_widget = gtk_table_new(0, 0, TRUE);
Moritz Lipp's avatar
Moritz Lipp committed
147
#endif
Moritz Lipp's avatar
Moritz Lipp committed
148
  if (zathura->ui.page_widget == NULL) {
149
150
151
    goto error_free;
  }

152
  g_signal_connect(G_OBJECT(zathura->ui.session->gtk.window), "size-allocate", G_CALLBACK(cb_view_resized), zathura);
153

Moritz Lipp's avatar
Moritz Lipp committed
154
155
156
157
158
159
  /* callbacks */
  GtkAdjustment* view_vadjustment = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(zathura->ui.session->gtk.view));
  g_signal_connect(G_OBJECT(view_vadjustment), "value-changed", G_CALLBACK(cb_view_vadjustment_value_changed), zathura);
  GtkAdjustment* view_hadjustment = gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(zathura->ui.session->gtk.view));
  g_signal_connect(G_OBJECT(view_hadjustment), "value-changed", G_CALLBACK(cb_view_vadjustment_value_changed), zathura);

160
  /* page view alignment */
161
  zathura->ui.page_widget_alignment = gtk_alignment_new(0.5, 0.5, 0, 0);
Moritz Lipp's avatar
Moritz Lipp committed
162
  if (zathura->ui.page_widget_alignment == NULL) {
163
164
    goto error_free;
  }
165
  gtk_container_add(GTK_CONTAINER(zathura->ui.page_widget_alignment), zathura->ui.page_widget);
166

167
168
169
170
171
172
173
174
#if (GTK_MAJOR_VERSION == 3)
  gtk_widget_set_hexpand_set(zathura->ui.page_widget_alignment, TRUE);
  gtk_widget_set_hexpand(zathura->ui.page_widget_alignment, FALSE);
  gtk_widget_set_vexpand_set(zathura->ui.page_widget_alignment, TRUE);
  gtk_widget_set_vexpand(zathura->ui.page_widget_alignment, FALSE);
#endif


175
  gtk_widget_show(zathura->ui.page_widget);
Moritz Lipp's avatar
Moritz Lipp committed
176

177
  /* statusbar */
Moritz Lipp's avatar
Moritz Lipp committed
178
179
  zathura->ui.statusbar.file = girara_statusbar_item_add(zathura->ui.session, TRUE, TRUE, TRUE, NULL);
  if (zathura->ui.statusbar.file == NULL) {
Moritz Lipp's avatar
Moritz Lipp committed
180
    goto error_free;
Moritz Lipp's avatar
Moritz Lipp committed
181
182
  }

Moritz Lipp's avatar
Moritz Lipp committed
183
184
  zathura->ui.statusbar.buffer = girara_statusbar_item_add(zathura->ui.session, FALSE, FALSE, FALSE, NULL);
  if (zathura->ui.statusbar.buffer == NULL) {
Moritz Lipp's avatar
Moritz Lipp committed
185
    goto error_free;
Moritz Lipp's avatar
Moritz Lipp committed
186
187
  }

Moritz Lipp's avatar
Moritz Lipp committed
188
  zathura->ui.statusbar.page_number = girara_statusbar_item_add(zathura->ui.session, FALSE, FALSE, FALSE, NULL);
Moritz Lipp's avatar
Moritz Lipp committed
189
  if (zathura->ui.statusbar.page_number == NULL) {
Moritz Lipp's avatar
Moritz Lipp committed
190
    goto error_free;
Moritz Lipp's avatar
Moritz Lipp committed
191
192
  }

Sebastian Ramacher's avatar
Sebastian Ramacher committed
193
  girara_statusbar_item_set_text(zathura->ui.session, zathura->ui.statusbar.file, _("[No name]"));
Moritz Lipp's avatar
Moritz Lipp committed
194

Moritz Lipp's avatar
Moritz Lipp committed
195
  /* signals */
196
  g_signal_connect(G_OBJECT(zathura->ui.session->gtk.window), "destroy", G_CALLBACK(cb_destroy), zathura);
197

198
199
200
  /* set page padding */
  int page_padding = 1;
  girara_setting_get(zathura->ui.session, "page-padding", &page_padding);
Moritz Lipp's avatar
Moritz Lipp committed
201

Moritz Lipp's avatar
Moritz Lipp committed
202
203
204
205
#if (GTK_MAJOR_VERSION == 3)
  gtk_grid_set_row_spacing(GTK_GRID(zathura->ui.page_widget), page_padding);
  gtk_grid_set_column_spacing(GTK_GRID(zathura->ui.page_widget), page_padding);
#else
206
207
  gtk_table_set_row_spacings(GTK_TABLE(zathura->ui.page_widget), page_padding);
  gtk_table_set_col_spacings(GTK_TABLE(zathura->ui.page_widget), page_padding);
Moritz Lipp's avatar
Moritz Lipp committed
208
#endif
Moritz Lipp's avatar
Moritz Lipp committed
209

210
  /* database */
211
212
213
214
  char* database = NULL;
  girara_setting_get(zathura->ui.session, "database", &database);

  if (g_strcmp0(database, "plain") == 0) {
215
    girara_debug("Using plain database backend.");
216
217
218
    zathura->database = zathura_plaindatabase_new(zathura->config.data_dir);
#ifdef WITH_SQLITE
  } else if (g_strcmp0(database, "sqlite") == 0) {
219
    girara_debug("Using sqlite database backend.");
220
221
222
223
224
225
226
227
228
    char* tmp = g_build_filename(zathura->config.data_dir, "bookmarks.sqlite", NULL);
    zathura->database = zathura_sqldatabase_new(tmp);
    g_free(tmp);
#endif
  } else {
    girara_error("Database backend '%s' is not supported.", database);
  }
  g_free(database);

229
  if (zathura->database == NULL) {
Sebastian Ramacher's avatar
Sebastian Ramacher committed
230
    girara_error("Unable to initialize database. Bookmarks won't be available.");
231
232
  }

233
  /* bookmarks */
234
  zathura->bookmarks.bookmarks = girara_sorted_list_new2((girara_compare_function_t) zathura_bookmarks_compare,
Moritz Lipp's avatar
Moritz Lipp committed
235
                                 (girara_free_function_t) zathura_bookmark_free);
236

237
238
239
240
241
  /* add even to purge old pages */
  int interval = 30;
  girara_setting_get(zathura->ui.session, "page-store-interval", &interval);
  g_timeout_add_seconds(interval, purge_pages, zathura);

242
243
244
245
246
247
248
249
250
251
  /* jumplist */

  zathura->jumplist.max_size = 20;
  girara_setting_get(zathura->ui.session, "jumplist-size", &(zathura->jumplist.max_size));

  zathura->jumplist.list = girara_list_new2(g_free);
  zathura->jumplist.size = 0;
  zathura->jumplist.cur = NULL;
  zathura_jumplist_append_jump(zathura);
  zathura->jumplist.cur = girara_list_iterator(zathura->jumplist.list);
252
  return true;
Moritz Lipp's avatar
Moritz Lipp committed
253
254
255

error_free:

Moritz Lipp's avatar
Moritz Lipp committed
256
  if (zathura->ui.page_widget != NULL) {
257
    g_object_unref(zathura->ui.page_widget);
258
259
  }

Moritz Lipp's avatar
Moritz Lipp committed
260
  if (zathura->ui.page_widget_alignment != NULL) {
261
    g_object_unref(zathura->ui.page_widget_alignment);
262
  }
Moritz Lipp's avatar
Moritz Lipp committed
263

264
  return false;
Moritz Lipp's avatar
Moritz Lipp committed
265
266
267
268
269
270
271
272
273
}

void
zathura_free(zathura_t* zathura)
{
  if (zathura == NULL) {
    return;
  }

Moritz Lipp's avatar
Moritz Lipp committed
274
  document_close(zathura, false);
275

Moritz Lipp's avatar
Moritz Lipp committed
276
277
278
279
  if (zathura->ui.session != NULL) {
    girara_session_destroy(zathura->ui.session);
  }

280
281
282
283
284
285
  /* stdin support */
  if (zathura->stdin_support.file != NULL) {
    g_unlink(zathura->stdin_support.file);
    g_free(zathura->stdin_support.file);
  }

286
287
288
  /* bookmarks */
  girara_list_free(zathura->bookmarks.bookmarks);

289
290
291
  /* database */
  zathura_db_free(zathura->database);

292
  /* free print settings */
Moritz Lipp's avatar
Moritz Lipp committed
293
  if (zathura->print.settings != NULL) {
294
295
296
297
298
299
    g_object_unref(zathura->print.settings);
  }

  if (zathura->print.page_setup != NULL) {
    g_object_unref(zathura->print.page_setup);
  }
300

Moritz Lipp's avatar
Moritz Lipp committed
301
  /* free registered plugins */
Moritz Lipp's avatar
Moritz Lipp committed
302
  zathura_plugin_manager_free(zathura->plugins.manager);
303
304
305
306

  /* free config variables */
  g_free(zathura->config.config_dir);
  g_free(zathura->config.data_dir);
Moritz Lipp's avatar
Moritz Lipp committed
307

308
309
310
311
312
313
314
315
316
  /* free jumplist */
  if (zathura->jumplist.list != NULL) {
    girara_list_free(zathura->jumplist.list);
  }

  if (zathura->jumplist.cur != NULL) {
    girara_list_iterator_free(zathura->jumplist.cur);
  }

317
  g_free(zathura);
318
319
}

320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
void
#if (GTK_MAJOR_VERSION == 2)
zathura_set_xid(zathura_t* zathura, GdkNativeWindow xid)
#else
zathura_set_xid(zathura_t* zathura, Window xid)
#endif
{
  g_return_if_fail(zathura != NULL);

  zathura->ui.session->gtk.embed = xid;
}

void
zathura_set_config_dir(zathura_t* zathura, const char* dir)
{
  g_return_if_fail(zathura != NULL);

  if (dir != NULL) {
    zathura->config.config_dir = g_strdup(dir);
  } else {
    gchar* path = girara_get_xdg_path(XDG_CONFIG);
    zathura->config.config_dir = g_build_filename(path, "zathura", NULL);
    g_free(path);
  }
}

void
zathura_set_data_dir(zathura_t* zathura, const char* dir)
{
  if (dir != NULL) {
    zathura->config.data_dir = g_strdup(dir);
  } else {
    gchar* path = girara_get_xdg_path(XDG_DATA);
    zathura->config.data_dir = g_build_filename(path, "zathura", NULL);
    g_free(path);
  }

  g_return_if_fail(zathura != NULL);
}

void
zathura_set_plugin_dir(zathura_t* zathura, const char* dir)
{
  g_return_if_fail(zathura != NULL);
  g_return_if_fail(zathura->plugins.manager != NULL);

  if (dir != NULL) {
    girara_list_t* paths = girara_split_path_array(dir);
    GIRARA_LIST_FOREACH(paths, char*, iter, path)
Moritz Lipp's avatar
Moritz Lipp committed
369
    zathura_plugin_manager_add_dir(zathura->plugins.manager, path);
370
371
372
373
374
375
    GIRARA_LIST_FOREACH_END(paths, char*, iter, path);
    girara_list_free(paths);
  } else {
#ifdef ZATHURA_PLUGINDIR
    girara_list_t* paths = girara_split_path_array(ZATHURA_PLUGINDIR);
    GIRARA_LIST_FOREACH(paths, char*, iter, path)
Moritz Lipp's avatar
Moritz Lipp committed
376
    zathura_plugin_manager_add_dir(zathura->plugins.manager, path);
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
    GIRARA_LIST_FOREACH_END(paths, char*, iter, path);
    girara_list_free(paths);
#endif
  }

}

void
zathura_set_synctex_editor_command(zathura_t* zathura, const char* command)
{
  g_return_if_fail(zathura != NULL);

  if (zathura->synctex.editor != NULL) {
    g_free(zathura->synctex.editor);
  }

  if (command != NULL) {
    zathura->synctex.editor = g_strdup(command);
  } else {
    zathura->synctex.editor = NULL;
  }
}

Moritz Lipp's avatar
Moritz Lipp committed
400
void
401
zathura_set_synctex(zathura_t* zathura, bool value)
Moritz Lipp's avatar
Moritz Lipp committed
402
403
404
405
406
407
408
{
  g_return_if_fail(zathura != NULL);
  g_return_if_fail(zathura->ui.session != NULL);

  girara_setting_set(zathura->ui.session, "synctex", &value);
}

409
410
411
412
413
414
415
416
void
zathura_set_argv(zathura_t* zathura, char** argv)
{
  g_return_if_fail(zathura != NULL);

  zathura->global.arguments = argv;
}

417
418
419
420
421
422
423
424
static gchar*
prepare_document_open_from_stdin(zathura_t* zathura)
{
  g_return_val_if_fail(zathura, NULL);

  GError* error = NULL;
  gchar* file = NULL;
  gint handle = g_file_open_tmp("zathura.stdin.XXXXXX", &file, &error);
Moritz Lipp's avatar
Moritz Lipp committed
425
  if (handle == -1) {
426
427
428
429
    if (error != NULL) {
      girara_error("Can not create temporary file: %s", error->message);
      g_error_free(error);
    }
430
431
432
433
434
    return NULL;
  }

  // read from stdin and dump to temporary file
  int stdinfno = fileno(stdin);
Moritz Lipp's avatar
Moritz Lipp committed
435
  if (stdinfno == -1) {
436
437
438
439
440
441
442
443
444
    girara_error("Can not read from stdin.");
    close(handle);
    g_unlink(file);
    g_free(file);
    return NULL;
  }

  char buffer[BUFSIZ];
  ssize_t count = 0;
Moritz Lipp's avatar
Moritz Lipp committed
445
446
  while ((count = read(stdinfno, buffer, BUFSIZ)) > 0) {
    if (write(handle, buffer, count) != count) {
447
448
449
450
451
452
453
      girara_error("Can not write to temporary file: %s", file);
      close(handle);
      g_unlink(file);
      g_free(file);
      return NULL;
    }
  }
Moritz Lipp's avatar
Moritz Lipp committed
454

455
456
  close(handle);

Moritz Lipp's avatar
Moritz Lipp committed
457
  if (count != 0) {
458
459
460
461
462
463
464
465
466
    girara_error("Can not read from stdin.");
    g_unlink(file);
    g_free(file);
    return NULL;
  }

  return file;
}

467
static gboolean
Moritz Lipp's avatar
Update    
Moritz Lipp committed
468
469
470
471
472
document_info_open(gpointer data)
{
  zathura_document_info_t* document_info = data;
  g_return_val_if_fail(document_info != NULL, FALSE);

473
  if (document_info->zathura != NULL && document_info->path != NULL) {
474
475
476
477
478
    char* file = NULL;
    if (g_strcmp0(document_info->path, "-") == 0) {
      file = prepare_document_open_from_stdin(document_info->zathura);
      if (file == NULL) {
        girara_notify(document_info->zathura->ui.session, GIRARA_ERROR,
Moritz Lipp's avatar
Moritz Lipp committed
479
                      "Could not read file from stdin and write it to a temporary file.");
480
481
      } else {
        document_info->zathura->stdin_support.file = g_strdup(file);
482
483
484
485
486
487
488
489
490
      }
    } else {
      file = g_strdup(document_info->path);
    }

    if (file != NULL) {
      document_open(document_info->zathura, file, document_info->password);
      g_free(file);
    }
Moritz Lipp's avatar
Update    
Moritz Lipp committed
491
492
  }

493
  g_free(document_info);
Moritz Lipp's avatar
Update    
Moritz Lipp committed
494
  return FALSE;
495
496
}

Moritz Lipp's avatar
Moritz Lipp committed
497
bool
Moritz Lipp's avatar
Moritz Lipp committed
498
document_open(zathura_t* zathura, const char* path, const char* password)
Moritz Lipp's avatar
Moritz Lipp committed
499
{
500
  if (zathura == NULL || zathura->plugins.manager == NULL || path == NULL) {
501
    goto error_out;
Moritz Lipp's avatar
Moritz Lipp committed
502
503
  }

504
  gchar* file_uri = NULL;
505
506
  zathura_error_t error = ZATHURA_ERROR_OK;
  zathura_document_t* document = zathura_document_open(zathura->plugins.manager, path, password, &error);
Moritz Lipp's avatar
Moritz Lipp committed
507

Moritz Lipp's avatar
Moritz Lipp committed
508
  if (document == NULL) {
509
510
511
512
513
514
515
516
    if (error == ZATHURA_ERROR_INVALID_PASSWORD) {
      zathura_password_dialog_info_t* password_dialog_info = malloc(sizeof(zathura_password_dialog_info_t));
      if (password_dialog_info != NULL) {
        password_dialog_info->zathura = zathura;

        if (path != NULL) {
          password_dialog_info->path = g_strdup(path);
          girara_dialog(zathura->ui.session, "Enter password:", true, NULL,
Moritz Lipp's avatar
Moritz Lipp committed
517
                        (girara_callback_inputbar_activate_t) cb_password_dialog, password_dialog_info);
518
519
520
521
522
523
524
          goto error_out;
        } else {
          free(password_dialog_info);
        }
      }
      goto error_out;
    }
525
    goto error_out;
Moritz Lipp's avatar
Moritz Lipp committed
526
527
  }

528
529
  const char* file_path        = zathura_document_get_path(document);
  unsigned int number_of_pages = zathura_document_get_number_of_pages(document);
530

531
532
533
534
535
536
  if (number_of_pages == 0) {
    girara_notify(zathura->ui.session, GIRARA_WARNING,
        _("Document does not contain any pages"));
    goto error_free;
  }

537
  /* read history file */
538
  zathura_fileinfo_t file_info = { 0, 0, 1, 0, 0, 0, 0, 0 };
539
  bool known_file = zathura_db_get_fileinfo(zathura->database, file_path, &file_info);
540

Moritz Lipp's avatar
Moritz Lipp committed
541
  /* set page offset */
542
  zathura_document_set_page_offset(document, file_info.page_offset);
543

544
  /* check for valid scale value */
545
  if (file_info.scale <= FLT_EPSILON) {
546
547
    girara_warning("document info: '%s' has non positive scale", file_path);
    zathura_document_set_scale(document, 1);
548
  } else {
549
    zathura_document_set_scale(document, file_info.scale);
550
551
552
  }

  /* check current page number */
553
  if (file_info.current_page > number_of_pages) {
554
555
    girara_warning("document info: '%s' has an invalid page number", file_path);
    zathura_document_set_current_page_number(document, 0);
556
  } else {
557
    zathura_document_set_current_page_number(document, file_info.current_page);
558
559
560
  }

  /* check for valid rotation */
561
  if (file_info.rotation % 90 != 0) {
562
563
564
    girara_warning("document info: '%s' has an invalid rotation", file_path);
    zathura_document_set_rotation(document, 0);
  } else {
565
    zathura_document_set_rotation(document, file_info.rotation % 360);
566
567
568
569
570
571
572
573
574
575
576
  }

  /* jump to first page if setting enabled */
  bool always_first_page = false;
  girara_setting_get(zathura->ui.session, "open-first-page", &always_first_page);
  if (always_first_page == true) {
    zathura_document_set_current_page_number(document, 0);
  }

  /* apply open adjustment */
  char* adjust_open = "best-fit";
577
  if (known_file == false && girara_setting_get(zathura->ui.session, "adjust-open", &(adjust_open)) == true) {
578
579
580
581
582
583
584
585
    if (g_strcmp0(adjust_open, "best-fit") == 0) {
      zathura_document_set_adjust_mode(document, ZATHURA_ADJUST_BESTFIT);
    } else if (g_strcmp0(adjust_open, "width") == 0) {
      zathura_document_set_adjust_mode(document, ZATHURA_ADJUST_WIDTH);
    } else {
      zathura_document_set_adjust_mode(document, ZATHURA_ADJUST_NONE);
    }
    g_free(adjust_open);
586
587
  } else {
    zathura_document_set_adjust_mode(document, ZATHURA_ADJUST_NONE);
588
589
590
  }

  /* update statusbar */
591
592
593
594
595
596
597
598
599
  bool basename_only = false;
  girara_setting_get(zathura->ui.session, "statusbar-basename", &basename_only);
  if (basename_only == false) {
    girara_statusbar_item_set_text(zathura->ui.session, zathura->ui.statusbar.file, file_path);
  } else {
    char* tmp = g_path_get_basename(file_path);
    girara_statusbar_item_set_text(zathura->ui.session, zathura->ui.statusbar.file, tmp);
    g_free(tmp);
  }
600

Moritz Lipp's avatar
Moritz Lipp committed
601
  /* install file monitor */
602
  file_uri = g_filename_to_uri(file_path, NULL, NULL);
Moritz Lipp's avatar
Moritz Lipp committed
603
604
605
606
607
  if (file_uri == NULL) {
    goto error_free;
  }

  if (zathura->file_monitor.file == NULL) {
608
609
610
611
    zathura->file_monitor.file = g_file_new_for_uri(file_uri);
    if (zathura->file_monitor.file == NULL) {
      goto error_free;
    }
Moritz Lipp's avatar
Moritz Lipp committed
612
613
614
  }

  if (zathura->file_monitor.monitor == NULL) {
615
616
617
618
619
    zathura->file_monitor.monitor = g_file_monitor_file(zathura->file_monitor.file, G_FILE_MONITOR_NONE, NULL, NULL);
    if (zathura->file_monitor.monitor == NULL) {
      goto error_free;
    }
    g_signal_connect(G_OBJECT(zathura->file_monitor.monitor), "changed", G_CALLBACK(cb_file_monitor), zathura->ui.session);
Moritz Lipp's avatar
Moritz Lipp committed
620
621
622
  }

  if (zathura->file_monitor.file_path == NULL) {
623
    zathura->file_monitor.file_path = g_strdup(file_path);
624
625
626
    if (zathura->file_monitor.file_path == NULL) {
      goto error_free;
    }
Moritz Lipp's avatar
Moritz Lipp committed
627
628
  }

629
  if (password != NULL) {
630
    g_free(zathura->file_monitor.password);
631
    zathura->file_monitor.password = g_strdup(password);
Moritz Lipp's avatar
Moritz Lipp committed
632
633
634
635
636
    if (zathura->file_monitor.password == NULL) {
      goto error_free;
    }
  }

Moritz Lipp's avatar
Moritz Lipp committed
637
638
639
640
641
642
  /* create marks list */
  zathura->global.marks = girara_list_new2((girara_free_function_t) mark_free);
  if (zathura->global.marks == NULL) {
    goto error_free;
  }

Moritz Lipp's avatar
Moritz Lipp committed
643
  zathura->document = document;
Moritz Lipp's avatar
Moritz Lipp committed
644

645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
  /* create blank pages */
  zathura->pages = calloc(number_of_pages, sizeof(GtkWidget*));
  if (zathura->pages == NULL) {
    goto error_free;
  }

  for (unsigned int page_id = 0; page_id < number_of_pages; page_id++) {
    zathura_page_t* page = zathura_document_get_page(document, page_id);
    if (page == NULL) {
      goto error_free;
    }

    GtkWidget* page_widget = zathura_page_widget_new(zathura, page);
    if (page_widget == NULL) {
      goto error_free;
    }

    zathura->pages[page_id] = page_widget;

    /* set widget size */
    unsigned int page_height = 0;
    unsigned int page_width  = 0;
    page_calc_height_width(page, &page_height, &page_width, true);

    gtk_widget_set_size_request(page_widget, page_width, page_height);
  }

672
  /* view mode */
673
  int pages_per_row = 1;
674
  int first_page_column = 1;
675
676
677
678
679
  if (file_info.pages_per_row > 0) {
    pages_per_row = file_info.pages_per_row;
  } else {
    girara_setting_get(zathura->ui.session, "pages-per-row", &pages_per_row);
  }
Moritz Lipp's avatar
Moritz Lipp committed
680

681
682
683
684
685
686
  if (file_info.first_page_column > 0) {
    first_page_column = file_info.first_page_column;
  } else {
    girara_setting_get(zathura->ui.session, "first-page-column", &first_page_column);
  }

Moritz Lipp's avatar
Moritz Lipp committed
687
  girara_setting_set(zathura->ui.session, "pages-per-row", &pages_per_row);
688
689
  girara_setting_set(zathura->ui.session, "first-page-column", &first_page_column);
  page_widget_set_mode(zathura, pages_per_row, first_page_column);
Moritz Lipp's avatar
Moritz Lipp committed
690

691
  girara_set_view(zathura->ui.session, zathura->ui.page_widget_alignment);
Moritz Lipp's avatar
Moritz Lipp committed
692

Moritz Lipp's avatar
Moritz Lipp committed
693
  /* threads */
Moritz Lipp's avatar
Moritz Lipp committed
694
  zathura->sync.render_thread = render_init(zathura);
Moritz Lipp's avatar
Moritz Lipp committed
695

Moritz Lipp's avatar
Moritz Lipp committed
696
  if (zathura->sync.render_thread == NULL) {
Moritz Lipp's avatar
Moritz Lipp committed
697
698
699
    goto error_free;
  }

700
  for (unsigned int page_id = 0; page_id < number_of_pages; page_id++) {
701
    gtk_widget_realize(zathura->pages[page_id]);
702
  }
Moritz Lipp's avatar
Moritz Lipp committed
703

704
  /* bookmarks */
705
  zathura_bookmarks_load(zathura, file_path);
706

Moritz Lipp's avatar
Moritz Lipp committed
707
  /* update title */
708
  basename_only = false;
709
710
711
712
713
714
715
716
  girara_setting_get(zathura->ui.session, "window-title-basename", &basename_only);
  if (basename_only == false) {
    girara_set_window_title(zathura->ui.session, file_path);
  } else {
    char* tmp = g_path_get_basename(file_path);
    girara_set_window_title(zathura->ui.session, tmp);
    g_free(tmp);
  }
Moritz Lipp's avatar
Moritz Lipp committed
717

718
  g_free(file_uri);
Moritz Lipp's avatar
Moritz Lipp committed
719

720
  /* adjust window */
721
  girara_argument_t argument = { zathura_document_get_adjust_mode(document), NULL };
722
723
  sc_adjust_window(zathura->ui.session, &argument, NULL, 0);

724
725
  /* set position */
  if (file_info.position_x != 0 || file_info.position_y != 0) {
Moritz Lipp's avatar
Moritz Lipp committed
726
    position_set_delayed(zathura, file_info.position_x, file_info.position_y);
727
728
729
730
731
  } else {
    page_set_delayed(zathura, zathura_document_get_current_page_number(document));
    cb_view_vadjustment_value_changed(NULL, zathura);
  }

Moritz Lipp's avatar
Moritz Lipp committed
732
  return true;
733
734
735

error_free:

Moritz Lipp's avatar
Moritz Lipp committed
736
737
738
739
  if (file_uri != NULL) {
    g_free(file_uri);
  }

740
741
742
743
744
  zathura_document_free(document);

error_out:

  return false;
Moritz Lipp's avatar
Moritz Lipp committed
745
746
}

747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
void
document_open_idle(zathura_t* zathura, const char* path, const char* password)
{
  if (zathura == NULL || path == NULL) {
    return;
  }

  zathura_document_info_t* document_info = g_malloc0(sizeof(zathura_document_info_t));

  document_info->zathura  = zathura;
  document_info->path     = path;
  document_info->password = password;

  gdk_threads_add_idle(document_info_open, document_info);
}

763
764
765
766
767
768
769
770
bool
document_save(zathura_t* zathura, const char* path, bool overwrite)
{
  g_return_val_if_fail(zathura, false);
  g_return_val_if_fail(zathura->document, false);
  g_return_val_if_fail(path, false);

  gchar* file_path = girara_fix_path(path);
771
772
773
774
775
776
777
778
779
  /* use current basename if path points to a directory  */
  if (g_file_test(file_path, G_FILE_TEST_IS_DIR) == TRUE) {
    char* basename = g_path_get_basename(zathura_document_get_path(zathura->document));
    char* tmp = file_path;
    file_path = g_strconcat(file_path, "/", basename, NULL);
    g_free(tmp);
    g_free(basename);
  }

Moritz Lipp's avatar
Moritz Lipp committed
780
  if ((overwrite == false) && g_file_test(file_path, G_FILE_TEST_EXISTS)) {
781
    girara_error("File already exists: %s. Use :write! to overwrite it.", file_path);
782
    g_free(file_path);
783
784
785
    return false;
  }

786
  zathura_error_t error = zathura_document_save_as(zathura->document, file_path);
787
  g_free(file_path);
788

789
  return (error == ZATHURA_ERROR_OK) ? true : false;
790
791
}

Pavel Borzenkov's avatar
Pavel Borzenkov committed
792
793
794
static void
remove_page_from_table(GtkWidget* page, gpointer permanent)
{
Moritz Lipp's avatar
Moritz Lipp committed
795
  if (permanent == false) {
Pavel Borzenkov's avatar
Pavel Borzenkov committed
796
797
798
    g_object_ref(G_OBJECT(page));
  }

799
  gtk_container_remove(GTK_CONTAINER(gtk_widget_get_parent(page)), page);
Pavel Borzenkov's avatar
Pavel Borzenkov committed
800
801
}

Moritz Lipp's avatar
Moritz Lipp committed
802
bool
Moritz Lipp's avatar
Moritz Lipp committed
803
document_close(zathura_t* zathura, bool keep_monitor)
Moritz Lipp's avatar
Moritz Lipp committed
804
{
Moritz Lipp's avatar
Moritz Lipp committed
805
  if (zathura == NULL || zathura->document == NULL) {
Moritz Lipp's avatar
Moritz Lipp committed
806
807
808
    return false;
  }

Moritz Lipp's avatar
Moritz Lipp committed
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
  /* remove monitor */
  if (keep_monitor == false) {
    if (zathura->file_monitor.monitor != NULL) {
      g_file_monitor_cancel(zathura->file_monitor.monitor);
      g_object_unref(zathura->file_monitor.monitor);
      zathura->file_monitor.monitor = NULL;
    }

    if (zathura->file_monitor.file != NULL) {
      g_object_unref(zathura->file_monitor.file);
      zathura->file_monitor.file = NULL;
    }

    if (zathura->file_monitor.file_path != NULL) {
      g_free(zathura->file_monitor.file_path);
      zathura->file_monitor.file_path = NULL;
    }

    if (zathura->file_monitor.password != NULL) {
      g_free(zathura->file_monitor.password);
      zathura->file_monitor.password = NULL;
    }
  }

Moritz Lipp's avatar
Moritz Lipp committed
833
834
835
836
837
838
  /* remove marks */
  if (zathura->global.marks != NULL) {
    girara_list_free(zathura->global.marks);
    zathura->global.marks = NULL;
  }

839
840
  /* store file information */
  const char* path = zathura_document_get_path(zathura->document);
841

842
  zathura_fileinfo_t file_info = { 0, 0, 1, 0, 1, 1, 0, 0 };
843
844
845
846
847
  file_info.current_page = zathura_document_get_current_page_number(zathura->document);
  file_info.page_offset  = zathura_document_get_page_offset(zathura->document);
  file_info.scale        = zathura_document_get_scale(zathura->document);
  file_info.rotation     = zathura_document_get_rotation(zathura->document);

848
  girara_setting_get(zathura->ui.session, "pages-per-row", &(file_info.pages_per_row));
849
  girara_setting_get(zathura->ui.session, "first-page-column", &(file_info.first_page_column));
850
851
852
853
854
855
856
857
858
859

  /* get position */
  GtkScrolledWindow *window = GTK_SCROLLED_WINDOW(zathura->ui.session->gtk.view);
  GtkAdjustment* vadjustment = gtk_scrolled_window_get_vadjustment(window);
  GtkAdjustment* hadjustment = gtk_scrolled_window_get_hadjustment(window);

  file_info.position_x = gtk_adjustment_get_value(hadjustment);
  file_info.position_y = gtk_adjustment_get_value(vadjustment);

  /* save file info */
860
  zathura_db_set_fileinfo(zathura->database, path, &file_info);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
861

862
  /* release render thread */
Pavel Borzenkov's avatar
Pavel Borzenkov committed
863
864
865
  render_free(zathura->sync.render_thread);
  zathura->sync.render_thread = NULL;

866
  /* remove widgets */
867
  gtk_container_foreach(GTK_CONTAINER(zathura->ui.page_widget), remove_page_from_table, (gpointer) 1);
Moritz Lipp's avatar
Moritz Lipp committed
868
869
870
  for (unsigned int i = 0; i < zathura_document_get_number_of_pages(zathura->document); i++) {
    g_object_unref(zathura->pages[i]);
  }
871
872
  free(zathura->pages);
  zathura->pages = NULL;
Moritz Lipp's avatar
Moritz Lipp committed
873

874
  /* remove document */
Moritz Lipp's avatar
Moritz Lipp committed
875
  zathura_document_free(zathura->document);
Pavel Borzenkov's avatar
Pavel Borzenkov committed
876
877
  zathura->document = NULL;

878
879
880
881
882
883
  /* remove index */
  if (zathura->ui.index != NULL) {
    g_object_ref_sink(zathura->ui.index);
    zathura->ui.index = NULL;
  }

884
  gtk_widget_hide(zathura->ui.page_widget);
885
886
887
888

  statusbar_page_number_update(zathura);

  if (zathura->ui.session != NULL && zathura->ui.statusbar.file != NULL) {
Sebastian Ramacher's avatar
Sebastian Ramacher committed
889
    girara_statusbar_item_set_text(zathura->ui.session, zathura->ui.statusbar.file, _("[No name]"));
890
  }
Moritz Lipp's avatar
Moritz Lipp committed
891

Moritz Lipp's avatar
Moritz Lipp committed
892
893
894
  /* update title */
  girara_set_window_title(zathura->ui.session, "zathura");

Moritz Lipp's avatar
Moritz Lipp committed
895
896
897
  return true;
}

898
static gboolean
Sebastian Ramacher's avatar
Sebastian Ramacher committed
899
900
page_set_delayed_impl(gpointer data)
{
901
902
903
904
905
906
907
908
909
910
  page_set_delayed_t* p = data;
  page_set(p->zathura, p->page);

  g_free(p);
  return FALSE;
}

bool
page_set_delayed(zathura_t* zathura, unsigned int page_id)
{
911
912
  if (zathura == NULL || zathura->document == NULL ||
      (page_id >= zathura_document_get_number_of_pages(zathura->document))) {
913
914
915
916
917
918
    return false;
  }

  page_set_delayed_t* p = g_malloc(sizeof(page_set_delayed_t));
  p->zathura = zathura;
  p->page = page_id;
919
  gdk_threads_add_idle(page_set_delayed_impl, p);
920
921
922
  return true;
}

Moritz Lipp's avatar
Moritz Lipp committed
923
bool
Moritz Lipp's avatar
Moritz Lipp committed
924
page_set(zathura_t* zathura, unsigned int page_id)
Moritz Lipp's avatar
Moritz Lipp committed
925
{
926
  if (zathura == NULL || zathura->document == NULL) {
Moritz Lipp's avatar
Moritz Lipp committed
927
    goto error_out;
Moritz Lipp's avatar
Moritz Lipp committed
928
929
  }

930
  /* render page */
931
  zathura_page_t* page = zathura_document_get_page(zathura->document, page_id);
Moritz Lipp's avatar
Moritz Lipp committed
932

Moritz Lipp's avatar
Moritz Lipp committed
933
  if (page == NULL) {
934
935
936
    goto error_out;
  }

937
  zathura_document_set_current_page_number(zathura->document, page_id);
938
  zathura->global.update_page_number = false;
939

940
  page_offset_t offset;
941
  page_calculate_offset(zathura, page, &offset);
Moritz Lipp's avatar
Moritz Lipp committed
942

Moritz Lipp's avatar
Moritz Lipp committed
943
  GtkAdjustment* view_vadjustment = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(zathura->ui.session->gtk.view));
Moritz Lipp's avatar
Moritz Lipp committed
944
  GtkAdjustment* view_hadjustment = gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(zathura->ui.session->gtk.view));
945
946
  set_adjustment(view_hadjustment, offset.x);
  set_adjustment(view_vadjustment, offset.y);
947

Moritz Lipp's avatar
Moritz Lipp committed
948
949
  statusbar_page_number_update(zathura);

Moritz Lipp's avatar
Moritz Lipp committed
950
  return true;
Moritz Lipp's avatar
Moritz Lipp committed
951
952
953
954

error_out:

  return false;
Moritz Lipp's avatar
Moritz Lipp committed
955
956
}

957
958
959
960
961
962
963
void
statusbar_page_number_update(zathura_t* zathura)
{
  if (zathura == NULL || zathura->ui.statusbar.page_number == NULL) {
    return;
  }

964
965
966
  unsigned int number_of_pages     = zathura_document_get_number_of_pages(zathura->document);
  unsigned int current_page_number = zathura_document_get_current_page_number(zathura->document);

967
  if (zathura->document != NULL) {
968
    char* page_number_text = g_strdup_printf("[%d/%d]", current_page_number + 1, number_of_pages);
969
970
971
972
973
    girara_statusbar_item_set_text(zathura->ui.session, zathura->ui.statusbar.page_number, page_number_text);
    g_free(page_number_text);
  } else {
    girara_statusbar_item_set_text(zathura->ui.session, zathura->ui.statusbar.page_number, "");
  }
974
975
}

976
void
977
page_widget_set_mode(zathura_t* zathura, unsigned int pages_per_row, unsigned int first_page_column)
978
{
Moritz Lipp's avatar
Moritz Lipp committed
979
980
981
982
983
  /* show at least one page */
  if (pages_per_row == 0) {
    pages_per_row = 1;
  }

Moritz Lipp's avatar
Moritz Lipp committed
984
985
986
987
  /* ensure: 0 < first_page_column <= pages_per_row */
  if (first_page_column < 1) {
    first_page_column = 1;
  }
988

Moritz Lipp's avatar
Moritz Lipp committed
989
990
991
  if (first_page_column > pages_per_row) {
    first_page_column = ((first_page_column - 1) % pages_per_row) + 1;
  }
992

993
994
995
996
  if (zathura->document == NULL) {
    return;
  }

997
  gtk_container_foreach(GTK_CONTAINER(zathura->ui.page_widget), remove_page_from_table, (gpointer)0);
998

999
  unsigned int number_of_pages     = zathura_document_get_number_of_pages(zathura->document);
Moritz Lipp's avatar
Moritz Lipp committed
1000
1001
#if (GTK_MAJOR_VERSION == 3)
#else
1002
  gtk_table_resize(GTK_TABLE(zathura->ui.page_widget), ceil((number_of_pages + first_page_column - 1) / pages_per_row), pages_per_row);
Moritz Lipp's avatar
Moritz Lipp committed
1003
1004
1005
#endif

  for (unsigned int i = 0; i < number_of_pages; i++) {
1006
1007
    int x = (i + first_page_column - 1) % pages_per_row;
    int y = (i + first_page_column - 1) / pages_per_row;
Moritz Lipp's avatar
Moritz Lipp committed
1008

1009
    zathura_page_t* page   = zathura_document_get_page(zathura->document, i);
1010
    GtkWidget* page_widget = zathura_page_get_widget(zathura, page);
Moritz Lipp's avatar
Moritz Lipp committed
1011
1012
1013
#if (GTK_MAJOR_VERSION == 3)
    gtk_grid_attach(GTK_GRID(zathura->ui.page_widget), page_widget, x, y, 1, 1);
#else
Moritz Lipp's avatar
Moritz Lipp committed
1014
    gtk_table_attach(GTK_TABLE(zathura->ui.page_widget), page_widget, x, x + 1, y, y + 1, GTK_SHRINK, GTK_SHRINK, 0, 0);
Moritz Lipp's avatar
Moritz Lipp committed
1015
#endif
1016
1017
  }

1018
  gtk_widget_show_all(zathura->ui.page_widget);
1019
}
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034

static
gboolean purge_pages(gpointer data)
{
  zathura_t* zathura = data;
  if (zathura == NULL || zathura->document == NULL) {
    return TRUE;
  }

  int threshold = 0;
  girara_setting_get(zathura->ui.session, "page-store-threshold", &threshold);
  if (threshold <= 0) {
    return TRUE;
  }

Sebastian Ramacher's avatar
Sebastian Ramacher committed
1035
  girara_debug("purging pages ...");
1036
1037
1038
  unsigned int number_of_pages = zathura_document_get_number_of_pages(zathura->document);
  for (unsigned int page_id = 0; page_id < number_of_pages; page_id++) {
    zathura_page_t* page   = zathura_document_get_page(zathura->document, page_id);
1039
    GtkWidget* page_widget = zathura_page_get_widget(zathura, page);
Moritz Lipp's avatar
Moritz Lipp committed
1040
    zathura_page_widget_purge_unused(ZATHURA_PAGE(page_widget), threshold);
1041
1042
1043
  }
  return TRUE;
}
1044
1045

static gboolean
Moritz Lipp's avatar
Moritz Lipp committed
1046
position_set_delayed_impl(gpointer data)
1047
1048
1049
1050
1051
1052
1053
{
  position_set_delayed_t* p = (position_set_delayed_t*) data;

  GtkScrolledWindow *window = GTK_SCROLLED_WINDOW(p->zathura->ui.session->gtk.view);
  GtkAdjustment* vadjustment = gtk_scrolled_window_get_vadjustment(window);
  GtkAdjustment* hadjustment = gtk_scrolled_window_get_hadjustment(window);

1054
1055
  set_adjustment(hadjustment, p->position_x);
  set_adjustment(vadjustment, p->position_y);
1056