zathura.c 42.3 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 <girara/shortcuts.h>
17
#include <glib/gstdio.h>
18
#include <glib/gi18n.h>
19

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

Moritz Lipp's avatar
Moritz Lipp committed
38
typedef struct zathura_document_info_s {
Moritz Lipp's avatar
Update    
Moritz Lipp committed
39
40
41
  zathura_t* zathura;
  const char* path;
  const char* password;
42
  int page_number;
Moritz Lipp's avatar
Update    
Moritz Lipp committed
43
44
} zathura_document_info_t;

Moritz Lipp's avatar
Moritz Lipp committed
45
typedef struct page_set_delayed_s {
46
47
48
49
  zathura_t* zathura;
  unsigned int page;
} page_set_delayed_t;

Moritz Lipp's avatar
Moritz Lipp committed
50
typedef struct position_set_delayed_s {
51
  zathura_t* zathura;
Moritz Lipp's avatar
Moritz Lipp committed
52
53
  double position_x;
  double position_y;
54
55
} position_set_delayed_t;

56
static gboolean document_info_open(gpointer data);
57
58
59
60
static bool zathura_page_cache_is_cached(zathura_t* zathura, unsigned int page_index);
static ssize_t zathura_page_cache_lru_invalidate(zathura_t* zathura);
static void zathura_page_cache_invalidate_all(zathura_t* zathura);
static bool zathura_page_cache_is_full(zathura_t* zathura, bool* result);
61
62
63
static void zathura_jumplist_reset_current(zathura_t* zathura);
static void zathura_jumplist_append_jump(zathura_t* zathura);
static void zathura_jumplist_save(zathura_t* zathura);
Moritz Lipp's avatar
Moritz Lipp committed
64

Moritz Lipp's avatar
Moritz Lipp committed
65
/* function implementation */
Moritz Lipp's avatar
Moritz Lipp committed
66
zathura_t*
67
zathura_create(void)
68
{
69
  zathura_t* zathura = g_malloc0(sizeof(zathura_t));
70

71
72
73
  /* global settings */
  zathura->global.recolor            = false;
  zathura->global.update_page_number = true;
74
  zathura->global.search_direction = FORWARD;
75

76
  /* plugins */
Moritz Lipp's avatar
Moritz Lipp committed
77
78
  zathura->plugins.manager = zathura_plugin_manager_new();
  if (zathura->plugins.manager == NULL) {
79
    goto error_out;
Moritz Lipp's avatar
Moritz Lipp committed
80
  }
81

82
83
84
  /* UI */
  if ((zathura->ui.session = girara_session_create()) == NULL) {
    goto error_out;
Sebastian Ramacher's avatar
Sebastian Ramacher committed
85
86
  }

87
88
89
90
91
92
93
94
95
96
  zathura->ui.session->global.data = zathura;

  return zathura;

error_out:

  zathura_free(zathura);

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

98
99
100
101
102
bool
zathura_init(zathura_t* zathura)
{
  if (zathura == NULL) {
    return false;
103
104
  }

105
  /* create zathura (config/data) directory */
106
107
108
109
110
111
112
  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));
  }
113

114
115
116
  /* load plugins */
  zathura_plugin_manager_load(zathura->plugins.manager);

117
118
119
  /* configuration */
  config_load_default(zathura);

120
  /* load global configuration files */
Moritz Lipp's avatar
Moritz Lipp committed
121
122
  char* config_path = girara_get_xdg_path(XDG_CONFIG_DIRS);
  girara_list_t* config_dirs = girara_split_path_array(config_path);
123
124
125
126
127
128
129
130
  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
131
  g_free(config_path);
132
133
134

  config_load_file(zathura, GLOBAL_RC);

135
136
137
  /* load local configuration files */
  char* configuration_file = g_build_filename(zathura->config.config_dir, ZATHURA_RC, NULL);
  config_load_file(zathura, configuration_file);
138
  g_free(configuration_file);
139

140
  /* UI */
Sebastian Ramacher's avatar
Sebastian Ramacher committed
141
  if (girara_session_init(zathura->ui.session, "zathura") == false) {
Moritz Lipp's avatar
Moritz Lipp committed
142
    goto error_free;
143
144
145
  }

  /* girara events */
Moritz Lipp's avatar
Moritz Lipp committed
146
147
  zathura->ui.session->events.buffer_changed  = cb_buffer_changed;
  zathura->ui.session->events.unknown_command = cb_unknown_command;
148

149
  /* page view */
Moritz Lipp's avatar
Moritz Lipp committed
150
151
#if (GTK_MAJOR_VERSION == 3)
  zathura->ui.page_widget = gtk_grid_new();
152
153
  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
154
#else
155
  zathura->ui.page_widget = gtk_table_new(0, 0, TRUE);
Moritz Lipp's avatar
Moritz Lipp committed
156
#endif
Moritz Lipp's avatar
Moritz Lipp committed
157
  if (zathura->ui.page_widget == NULL) {
158
159
160
    goto error_free;
  }

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

163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
  /* Setup hadjustment tracker */
  GtkAdjustment* hadjustment = gtk_scrolled_window_get_hadjustment(
      GTK_SCROLLED_WINDOW(zathura->ui.session->gtk.view));
  zathura->ui.hadjustment = zathura_adjustment_clone(hadjustment);
  g_object_ref_sink(zathura->ui.hadjustment);

  /* Connect hadjustment signals */
  g_signal_connect(G_OBJECT(hadjustment), "value-changed",
      G_CALLBACK(cb_view_vadjustment_value_changed), zathura);
  g_signal_connect(G_OBJECT(hadjustment), "value-changed",
      G_CALLBACK(cb_adjustment_track_value), zathura->ui.hadjustment);
  g_signal_connect(G_OBJECT(hadjustment), "changed",
      G_CALLBACK(cb_view_hadjustment_changed), zathura);
  g_signal_connect(G_OBJECT(hadjustment), "changed",
      G_CALLBACK(cb_adjustment_track_bounds), zathura->ui.hadjustment);

  /* Setup vadjustment tracker */
  GtkAdjustment* vadjustment = gtk_scrolled_window_get_vadjustment(
      GTK_SCROLLED_WINDOW(zathura->ui.session->gtk.view));
  zathura->ui.vadjustment = zathura_adjustment_clone(vadjustment);
  g_object_ref_sink(zathura->ui.vadjustment);

  /* Connect vadjustment signals */
  g_signal_connect(G_OBJECT(vadjustment), "value-changed",
      G_CALLBACK(cb_view_vadjustment_value_changed), zathura);
  g_signal_connect(G_OBJECT(vadjustment), "value-changed",
      G_CALLBACK(cb_adjustment_track_value), zathura->ui.vadjustment);
  g_signal_connect(G_OBJECT(vadjustment), "changed",
      G_CALLBACK(cb_view_vadjustment_changed), zathura);
  g_signal_connect(G_OBJECT(vadjustment), "changed",
      G_CALLBACK(cb_adjustment_track_bounds), zathura->ui.vadjustment);
Moritz Lipp's avatar
Moritz Lipp committed
194

195
  /* page view alignment */
196
  zathura->ui.page_widget_alignment = gtk_alignment_new(0.5, 0.5, 0, 0);
Moritz Lipp's avatar
Moritz Lipp committed
197
  if (zathura->ui.page_widget_alignment == NULL) {
198
199
    goto error_free;
  }
200
  gtk_container_add(GTK_CONTAINER(zathura->ui.page_widget_alignment), zathura->ui.page_widget);
201

202
203
204
205
206
207
208
209
#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


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

212
  /* statusbar */
Moritz Lipp's avatar
Moritz Lipp committed
213
214
  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
215
    goto error_free;
Moritz Lipp's avatar
Moritz Lipp committed
216
217
  }

Moritz Lipp's avatar
Moritz Lipp committed
218
219
  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
220
    goto error_free;
Moritz Lipp's avatar
Moritz Lipp committed
221
222
  }

Moritz Lipp's avatar
Moritz Lipp committed
223
  zathura->ui.statusbar.page_number = girara_statusbar_item_add(zathura->ui.session, FALSE, FALSE, FALSE, NULL);
Moritz Lipp's avatar
Moritz Lipp committed
224
  if (zathura->ui.statusbar.page_number == NULL) {
Moritz Lipp's avatar
Moritz Lipp committed
225
    goto error_free;
Moritz Lipp's avatar
Moritz Lipp committed
226
227
  }

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

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

233
234
235
  /* set page padding */
  int page_padding = 1;
  girara_setting_get(zathura->ui.session, "page-padding", &page_padding);
Moritz Lipp's avatar
Moritz Lipp committed
236

Moritz Lipp's avatar
Moritz Lipp committed
237
238
239
240
#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
241
242
  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
243
#endif
Moritz Lipp's avatar
Moritz Lipp committed
244

245
  /* database */
246
247
248
249
  char* database = NULL;
  girara_setting_get(zathura->ui.session, "database", &database);

  if (g_strcmp0(database, "plain") == 0) {
250
    girara_debug("Using plain database backend.");
251
252
253
    zathura->database = zathura_plaindatabase_new(zathura->config.data_dir);
#ifdef WITH_SQLITE
  } else if (g_strcmp0(database, "sqlite") == 0) {
254
    girara_debug("Using sqlite database backend.");
255
256
257
258
259
260
261
262
263
    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);

264
  if (zathura->database == NULL) {
Sebastian Ramacher's avatar
Sebastian Ramacher committed
265
    girara_error("Unable to initialize database. Bookmarks won't be available.");
266
  } else {
267
    g_object_set(zathura->ui.session->command_history, "io", zathura->database, NULL);
268
269
  }

270
  /* bookmarks */
271
  zathura->bookmarks.bookmarks = girara_sorted_list_new2((girara_compare_function_t) zathura_bookmarks_compare,
Moritz Lipp's avatar
Moritz Lipp committed
272
                                 (girara_free_function_t) zathura_bookmark_free);
273

274
275
  /* jumplist */

276
277
  int jumplist_size = 20;
  girara_setting_get(zathura->ui.session, "jumplist-size", &jumplist_size);
278

279
280
  zathura->jumplist.max_size = jumplist_size < 0 ? 0 : jumplist_size;
  zathura->jumplist.list = NULL;
281
282
  zathura->jumplist.size = 0;
  zathura->jumplist.cur = NULL;
283
284
285

  /* page cache */

286
287
288
  int cache_size = 0;
  girara_setting_get(zathura->ui.session, "page-cache-size", &cache_size);
  if (cache_size <= 0) {
289
290
    girara_warning("page-cache-size is not positive, using %d instead", ZATHURA_PAGE_CACHE_DEFAULT_SIZE);
    zathura->page_cache.size = ZATHURA_PAGE_CACHE_DEFAULT_SIZE;
291
292
  } else {
    zathura->page_cache.size = cache_size;
293
294
  }

295
296
297
  zathura->page_cache.cache = g_malloc(zathura->page_cache.size * sizeof(int));
  zathura_page_cache_invalidate_all(zathura);

298
  return true;
Moritz Lipp's avatar
Moritz Lipp committed
299
300
301

error_free:

Moritz Lipp's avatar
Moritz Lipp committed
302
  if (zathura->ui.page_widget != NULL) {
303
    g_object_unref(zathura->ui.page_widget);
304
305
  }

Moritz Lipp's avatar
Moritz Lipp committed
306
  if (zathura->ui.page_widget_alignment != NULL) {
307
    g_object_unref(zathura->ui.page_widget_alignment);
308
  }
Moritz Lipp's avatar
Moritz Lipp committed
309

310
  return false;
Moritz Lipp's avatar
Moritz Lipp committed
311
312
313
314
315
316
317
318
319
}

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

Moritz Lipp's avatar
Moritz Lipp committed
320
  document_close(zathura, false);
321

Moritz Lipp's avatar
Moritz Lipp committed
322
323
324
325
  if (zathura->ui.session != NULL) {
    girara_session_destroy(zathura->ui.session);
  }

326
327
328
329
330
331
332
  if (zathura->ui.hadjustment != NULL) {
    g_object_unref(G_OBJECT(zathura->ui.hadjustment));
  }
  if (zathura->ui.vadjustment != NULL) {
    g_object_unref(G_OBJECT(zathura->ui.vadjustment));
  }

333
334
335
336
337
338
  /* stdin support */
  if (zathura->stdin_support.file != NULL) {
    g_unlink(zathura->stdin_support.file);
    g_free(zathura->stdin_support.file);
  }

339
340
341
  /* bookmarks */
  girara_list_free(zathura->bookmarks.bookmarks);

342
  /* database */
Sebastian Ramacher's avatar
Sebastian Ramacher committed
343
344
345
  if (zathura->database != NULL) {
    g_object_unref(G_OBJECT(zathura->database));
  }
346

347
  /* free print settings */
Moritz Lipp's avatar
Moritz Lipp committed
348
  if (zathura->print.settings != NULL) {
349
350
351
352
353
354
    g_object_unref(zathura->print.settings);
  }

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

Moritz Lipp's avatar
Moritz Lipp committed
356
  /* free registered plugins */
Moritz Lipp's avatar
Moritz Lipp committed
357
  zathura_plugin_manager_free(zathura->plugins.manager);
358
359
360
361

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

363
364
365
366
367
368
369
370
371
  /* 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);
  }

372
373
  g_free(zathura->page_cache.cache);

374
  g_free(zathura);
375
376
}

377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
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
426
    zathura_plugin_manager_add_dir(zathura->plugins.manager, path);
427
428
429
430
431
432
    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
433
    zathura_plugin_manager_add_dir(zathura->plugins.manager, path);
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
    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
457
void
458
zathura_set_synctex(zathura_t* zathura, bool value)
Moritz Lipp's avatar
Moritz Lipp committed
459
460
461
462
463
464
465
{
  g_return_if_fail(zathura != NULL);
  g_return_if_fail(zathura->ui.session != NULL);

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

466
467
468
469
470
471
472
473
void
zathura_set_argv(zathura_t* zathura, char** argv)
{
  g_return_if_fail(zathura != NULL);

  zathura->global.arguments = argv;
}

474
475
476
477
478
479
480
481
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
482
  if (handle == -1) {
483
484
485
486
    if (error != NULL) {
      girara_error("Can not create temporary file: %s", error->message);
      g_error_free(error);
    }
487
488
489
490
491
    return NULL;
  }

  // read from stdin and dump to temporary file
  int stdinfno = fileno(stdin);
Moritz Lipp's avatar
Moritz Lipp committed
492
  if (stdinfno == -1) {
493
494
495
496
497
498
499
500
501
    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
502
503
  while ((count = read(stdinfno, buffer, BUFSIZ)) > 0) {
    if (write(handle, buffer, count) != count) {
504
505
506
507
508
509
510
      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
511

512
513
  close(handle);

Moritz Lipp's avatar
Moritz Lipp committed
514
  if (count != 0) {
515
516
517
518
519
520
521
522
523
    girara_error("Can not read from stdin.");
    g_unlink(file);
    g_free(file);
    return NULL;
  }

  return file;
}

524
static gboolean
Moritz Lipp's avatar
Update    
Moritz Lipp committed
525
526
527
528
529
document_info_open(gpointer data)
{
  zathura_document_info_t* document_info = data;
  g_return_val_if_fail(document_info != NULL, FALSE);

530
  if (document_info->zathura != NULL && document_info->path != NULL) {
531
532
533
534
535
    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
536
                      "Could not read file from stdin and write it to a temporary file.");
537
538
      } else {
        document_info->zathura->stdin_support.file = g_strdup(file);
539
540
541
542
543
544
      }
    } else {
      file = g_strdup(document_info->path);
    }

    if (file != NULL) {
545
546
      document_open(document_info->zathura, file, document_info->password,
                    document_info->page_number);
547
548
      g_free(file);
    }
Moritz Lipp's avatar
Update    
Moritz Lipp committed
549
550
  }

551
  g_free(document_info);
Moritz Lipp's avatar
Update    
Moritz Lipp committed
552
  return FALSE;
553
554
}

Moritz Lipp's avatar
Moritz Lipp committed
555
bool
556
557
document_open(zathura_t* zathura, const char* path, const char* password,
              int page_number)
Moritz Lipp's avatar
Moritz Lipp committed
558
{
559
  if (zathura == NULL || zathura->plugins.manager == NULL || path == NULL) {
560
    goto error_out;
Moritz Lipp's avatar
Moritz Lipp committed
561
562
  }

563
  gchar* file_uri = NULL;
564
565
  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
566

Moritz Lipp's avatar
Moritz Lipp committed
567
  if (document == NULL) {
568
569
570
571
572
573
574
575
    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
576
                        (girara_callback_inputbar_activate_t) cb_password_dialog, password_dialog_info);
577
578
579
580
581
582
583
          goto error_out;
        } else {
          free(password_dialog_info);
        }
      }
      goto error_out;
    }
Moritz Lipp's avatar
Moritz Lipp committed
584
585
586
    if (error == ZATHURA_ERROR_OK ) {
      girara_notify(zathura->ui.session, GIRARA_ERROR, _("Unsupported file type. Please install the necessary plugin."));
    }
587
    goto error_out;
Moritz Lipp's avatar
Moritz Lipp committed
588
589
  }

590
591
  const char* file_path        = zathura_document_get_path(document);
  unsigned int number_of_pages = zathura_document_get_number_of_pages(document);
592

593
594
595
596
597
598
  if (number_of_pages == 0) {
    girara_notify(zathura->ui.session, GIRARA_WARNING,
        _("Document does not contain any pages"));
    goto error_free;
  }

599
  /* read history file */
600
  zathura_fileinfo_t file_info = { 0, 0, 1, 0, 0, 0, 0, 0 };
601
  bool known_file = zathura_db_get_fileinfo(zathura->database, file_path, &file_info);
602

Moritz Lipp's avatar
Moritz Lipp committed
603
  /* set page offset */
604
  zathura_document_set_page_offset(document, file_info.page_offset);
605

606
  /* check for valid scale value */
607
  if (file_info.scale <= FLT_EPSILON) {
608
609
    girara_warning("document info: '%s' has non positive scale", file_path);
    zathura_document_set_scale(document, 1);
610
  } else {
611
    zathura_document_set_scale(document, file_info.scale);
612
613
614
  }

  /* check current page number */
615
616
617
618
619
620
  /* if it wasn't specified on the command-line, get it from file_info */
  if (page_number == ZATHURA_PAGE_NUMBER_UNSPECIFIED)
    page_number = file_info.current_page;
  if (page_number < 0)
    page_number += number_of_pages;
  if ((unsigned)page_number > number_of_pages) {
621
622
    girara_warning("document info: '%s' has an invalid page number", file_path);
    zathura_document_set_current_page_number(document, 0);
623
  } else {
624
    zathura_document_set_current_page_number(document, page_number);
625
626
627
  }

  /* check for valid rotation */
628
  if (file_info.rotation % 90 != 0) {
629
630
631
    girara_warning("document info: '%s' has an invalid rotation", file_path);
    zathura_document_set_rotation(document, 0);
  } else {
632
    zathura_document_set_rotation(document, file_info.rotation % 360);
633
634
635
636
637
638
639
640
641
642
643
  }

  /* 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";
644
  if (known_file == false && girara_setting_get(zathura->ui.session, "adjust-open", &(adjust_open)) == true) {
645
646
647
648
649
650
651
652
    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);
653
654
  } else {
    zathura_document_set_adjust_mode(document, ZATHURA_ADJUST_NONE);
655
656
657
  }

  /* update statusbar */
658
659
660
661
662
663
664
665
666
  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);
  }
667

Moritz Lipp's avatar
Moritz Lipp committed
668
  /* install file monitor */
669
  file_uri = g_filename_to_uri(file_path, NULL, NULL);
Moritz Lipp's avatar
Moritz Lipp committed
670
671
672
673
674
  if (file_uri == NULL) {
    goto error_free;
  }

  if (zathura->file_monitor.file == NULL) {
675
676
677
678
    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
679
680
681
  }

  if (zathura->file_monitor.monitor == NULL) {
682
683
684
685
686
    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
687
688
689
  }

  if (zathura->file_monitor.file_path == NULL) {
690
    zathura->file_monitor.file_path = g_strdup(file_path);
691
692
693
    if (zathura->file_monitor.file_path == NULL) {
      goto error_free;
    }
Moritz Lipp's avatar
Moritz Lipp committed
694
695
  }

696
  if (password != NULL) {
697
    g_free(zathura->file_monitor.password);
698
    zathura->file_monitor.password = g_strdup(password);
Moritz Lipp's avatar
Moritz Lipp committed
699
700
701
702
703
    if (zathura->file_monitor.password == NULL) {
      goto error_free;
    }
  }

Moritz Lipp's avatar
Moritz Lipp committed
704
705
706
707
708
709
  /* 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
710
  zathura->document = document;
Moritz Lipp's avatar
Moritz Lipp committed
711

712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
  /* 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);
  }

739
  /* view mode */
740
  int pages_per_row = 1;
741
  int first_page_column = 1;
742
743
744
745
746
  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
747

748
749
750
751
752
753
  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
754
  girara_setting_set(zathura->ui.session, "pages-per-row", &pages_per_row);
755
756
  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
757

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

Moritz Lipp's avatar
Moritz Lipp committed
760
  /* threads */
Moritz Lipp's avatar
Moritz Lipp committed
761
  zathura->sync.render_thread = render_init(zathura);
Moritz Lipp's avatar
Moritz Lipp committed
762

Moritz Lipp's avatar
Moritz Lipp committed
763
  if (zathura->sync.render_thread == NULL) {
Moritz Lipp's avatar
Moritz Lipp committed
764
765
766
    goto error_free;
  }

767
  for (unsigned int page_id = 0; page_id < number_of_pages; page_id++) {
768
    gtk_widget_realize(zathura->pages[page_id]);
769
  }
Moritz Lipp's avatar
Moritz Lipp committed
770

771
  /* bookmarks */
772
  zathura_bookmarks_load(zathura, file_path);
773

774
775
776
777
778
  /* jumplist */
  if (zathura_jumplist_load(zathura, file_path) == false) {
    zathura->jumplist.list = girara_list_new2(g_free);
  }

Moritz Lipp's avatar
Moritz Lipp committed
779
  /* update title */
780
  basename_only = false;
781
782
783
784
785
786
787
788
  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
789

790
  g_free(file_uri);
Moritz Lipp's avatar
Moritz Lipp committed
791

792
  /* adjust window */
793
  girara_argument_t argument = { zathura_document_get_adjust_mode(document), NULL };
794
795
  sc_adjust_window(zathura->ui.session, &argument, NULL, 0);

796
797
  /* set position */
  if (file_info.position_x != 0 || file_info.position_y != 0) {
Moritz Lipp's avatar
Moritz Lipp committed
798
    position_set_delayed(zathura, file_info.position_x, file_info.position_y);
799
800
801
802
803
  } else {
    page_set_delayed(zathura, zathura_document_get_current_page_number(document));
    cb_view_vadjustment_value_changed(NULL, zathura);
  }

804
805
806
  /* Invalidate all current entries in the page cache */
  zathura_page_cache_invalidate_all(zathura);

Moritz Lipp's avatar
Moritz Lipp committed
807
  return true;
808
809
810

error_free:

Moritz Lipp's avatar
Moritz Lipp committed
811
812
813
814
  if (file_uri != NULL) {
    g_free(file_uri);
  }

815
816
817
818
819
  zathura_document_free(document);

error_out:

  return false;
Moritz Lipp's avatar
Moritz Lipp committed
820
821
}

822
void
823
824
document_open_idle(zathura_t* zathura, const char* path, const char* password,
                   int page_number)
825
826
827
828
829
830
831
{
  if (zathura == NULL || path == NULL) {
    return;
  }

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

832
833
834
835
  document_info->zathura     = zathura;
  document_info->path        = path;
  document_info->password    = password;
  document_info->page_number = page_number;
836
837
838
839

  gdk_threads_add_idle(document_info_open, document_info);
}

840
841
842
843
844
845
846
847
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);
848
849
850
851
852
853
854
855
856
  /* 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
857
  if ((overwrite == false) && g_file_test(file_path, G_FILE_TEST_EXISTS)) {
858
    girara_error("File already exists: %s. Use :write! to overwrite it.", file_path);
859
    g_free(file_path);
860
861
862
    return false;
  }

863
  zathura_error_t error = zathura_document_save_as(zathura->document, file_path);
864
  g_free(file_path);
865

866
  return (error == ZATHURA_ERROR_OK) ? true : false;
867
868
}

Pavel Borzenkov's avatar
Pavel Borzenkov committed
869
870
871
static void
remove_page_from_table(GtkWidget* page, gpointer permanent)
{
Moritz Lipp's avatar
Moritz Lipp committed
872
  if (permanent == false) {
Pavel Borzenkov's avatar
Pavel Borzenkov committed
873
874
875
    g_object_ref(G_OBJECT(page));
  }

876
  gtk_container_remove(GTK_CONTAINER(gtk_widget_get_parent(page)), page);
Pavel Borzenkov's avatar
Pavel Borzenkov committed
877
878
}

Moritz Lipp's avatar
Moritz Lipp committed
879
bool
Moritz Lipp's avatar
Moritz Lipp committed
880
document_close(zathura_t* zathura, bool keep_monitor)
Moritz Lipp's avatar
Moritz Lipp committed
881
{
Moritz Lipp's avatar
Moritz Lipp committed
882
  if (zathura == NULL || zathura->document == NULL) {
Moritz Lipp's avatar
Moritz Lipp committed
883
884
885
    return false;
  }

Moritz Lipp's avatar
Moritz Lipp committed
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
  /* 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
910
911
912
913
914
915
  /* remove marks */
  if (zathura->global.marks != NULL) {
    girara_list_free(zathura->global.marks);
    zathura->global.marks = NULL;
  }

916
917
  /* store file information */
  const char* path = zathura_document_get_path(zathura->document);
918

919
  zathura_fileinfo_t file_info = { 0, 0, 1, 0, 1, 1, 0, 0 };
920
921
922
923
924
  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);

925
  girara_setting_get(zathura->ui.session, "pages-per-row", &(file_info.pages_per_row));
926
  girara_setting_get(zathura->ui.session, "first-page-column", &(file_info.first_page_column));
927
928
929
930
931
932
933
934
935
936

  /* 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 */
937
  zathura_db_set_fileinfo(zathura->database, path, &file_info);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
938

939
940
941
942
943
944
945
946
  /* save jumplist */
  zathura_db_save_jumplist(zathura->database, path, zathura->jumplist.list);
  girara_list_iterator_free(zathura->jumplist.cur);
  zathura->jumplist.cur = NULL;
  girara_list_free(zathura->jumplist.list);
  zathura->jumplist.list = NULL;
  zathura->jumplist.size = 0;

947
  /* release render thread */
Pavel Borzenkov's avatar
Pavel Borzenkov committed
948
949
950
  render_free(zathura->sync.render_thread);
  zathura->sync.render_thread = NULL;

951
  /* remove widgets */
952
  gtk_container_foreach(GTK_CONTAINER(zathura->ui.page_widget), remove_page_from_table, (gpointer) 1);
Moritz Lipp's avatar
Moritz Lipp committed
953
954
  for (unsigned int i = 0; i < zathura_document_get_number_of_pages(zathura->document); i++) {
    g_object_unref(zathura->pages[i]);
955
    g_object_unref(zathura->pages[i]); // FIXME
Moritz Lipp's avatar
Moritz Lipp committed
956
  }
957
958
  free(zathura->pages);
  zathura->pages = NULL;
Moritz Lipp's avatar
Moritz Lipp committed
959

960
  /* remove document */
Moritz Lipp's avatar
Moritz Lipp committed
961
  zathura_document_free(zathura->document);
Pavel Borzenkov's avatar
Pavel Borzenkov committed
962
963
  zathura->document = NULL;

964
965
966
967
968
969
  /* remove index */
  if (zathura->ui.index != NULL) {
    g_object_ref_sink(zathura->ui.index);
    zathura->ui.index = NULL;
  }

970
  gtk_widget_hide(zathura->ui.page_widget);
971
972
973
974

  statusbar_page_number_update(zathura);

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

Moritz Lipp's avatar
Moritz Lipp committed
978
979
980
  /* update title */
  girara_set_window_title(zathura->ui.session, "zathura");

Moritz Lipp's avatar
Moritz Lipp committed
981
982
983
  return true;
}

984
static gboolean
Sebastian Ramacher's avatar
Sebastian Ramacher committed
985
986
page_set_delayed_impl(gpointer data)
{
987
988
989
990
991
992
993
994
995
996
  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)
{
997
998
  if (zathura == NULL || zathura->document == NULL ||
      (page_id >= zathura_document_get_number_of_pages(zathura->document))) {
999
1000
    return false;
  }
For faster browsing, not all history is shown. View entire blame