zathura.c 39.1 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"
35
#include "adjustment.h"
Moritz Lipp's avatar
Moritz Lipp committed
36

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

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

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

55
static gboolean document_info_open(gpointer data);
56
57
58
59
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);
Moritz Lipp's avatar
Moritz Lipp committed
60

Moritz Lipp's avatar
Moritz Lipp committed
61
/* function implementation */
Moritz Lipp's avatar
Moritz Lipp committed
62
zathura_t*
63
zathura_create(void)
64
{
65
  zathura_t* zathura = g_malloc0(sizeof(zathura_t));
66

67
68
69
  /* global settings */
  zathura->global.recolor            = false;
  zathura->global.update_page_number = true;
70
  zathura->global.search_direction = FORWARD;
71

72
  /* plugins */
Moritz Lipp's avatar
Moritz Lipp committed
73
74
  zathura->plugins.manager = zathura_plugin_manager_new();
  if (zathura->plugins.manager == NULL) {
75
    goto error_out;
Moritz Lipp's avatar
Moritz Lipp committed
76
  }
77

78
79
80
  /* UI */
  if ((zathura->ui.session = girara_session_create()) == NULL) {
    goto error_out;
Sebastian Ramacher's avatar
Sebastian Ramacher committed
81
82
  }

83
84
85
86
87
88
89
90
91
92
  zathura->ui.session->global.data = zathura;

  return zathura;

error_out:

  zathura_free(zathura);

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

94
95
96
97
98
bool
zathura_init(zathura_t* zathura)
{
  if (zathura == NULL) {
    return false;
99
100
  }

101
  /* create zathura (config/data) directory */
102
103
104
105
106
107
108
  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));
  }
109

110
111
112
  /* load plugins */
  zathura_plugin_manager_load(zathura->plugins.manager);

113
114
115
  /* configuration */
  config_load_default(zathura);

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

  config_load_file(zathura, GLOBAL_RC);

131
132
133
  /* load local configuration files */
  char* configuration_file = g_build_filename(zathura->config.config_dir, ZATHURA_RC, NULL);
  config_load_file(zathura, configuration_file);
134
  g_free(configuration_file);
135

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

  /* girara events */
Moritz Lipp's avatar
Moritz Lipp committed
142
143
  zathura->ui.session->events.buffer_changed  = cb_buffer_changed;
  zathura->ui.session->events.unknown_command = cb_unknown_command;
144

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

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

159
160
161
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
  /* 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
190

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

198
199
200
201
202
203
204
205
#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


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

208
  /* statusbar */
Moritz Lipp's avatar
Moritz Lipp committed
209
210
  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
211
    goto error_free;
Moritz Lipp's avatar
Moritz Lipp committed
212
213
  }

Moritz Lipp's avatar
Moritz Lipp committed
214
215
  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
216
    goto error_free;
Moritz Lipp's avatar
Moritz Lipp committed
217
218
  }

Moritz Lipp's avatar
Moritz Lipp committed
219
  zathura->ui.statusbar.page_number = girara_statusbar_item_add(zathura->ui.session, FALSE, FALSE, FALSE, NULL);
Moritz Lipp's avatar
Moritz Lipp committed
220
  if (zathura->ui.statusbar.page_number == NULL) {
Moritz Lipp's avatar
Moritz Lipp committed
221
    goto error_free;
Moritz Lipp's avatar
Moritz Lipp committed
222
223
  }

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

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

229
230
231
  /* set page padding */
  int page_padding = 1;
  girara_setting_get(zathura->ui.session, "page-padding", &page_padding);
Moritz Lipp's avatar
Moritz Lipp committed
232

Moritz Lipp's avatar
Moritz Lipp committed
233
234
235
236
#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
237
238
  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
239
#endif
Moritz Lipp's avatar
Moritz Lipp committed
240

241
  /* database */
242
243
244
245
  char* database = NULL;
  girara_setting_get(zathura->ui.session, "database", &database);

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

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

266
  /* bookmarks */
267
  zathura->bookmarks.bookmarks = girara_sorted_list_new2((girara_compare_function_t) zathura_bookmarks_compare,
Moritz Lipp's avatar
Moritz Lipp committed
268
                                 (girara_free_function_t) zathura_bookmark_free);
269

270
271
272
273
274
275
276
277
278
279
  /* 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);
280
281
282

  /* page cache */

283
284
285
  int cache_size = 0;
  girara_setting_get(zathura->ui.session, "page-cache-size", &cache_size);
  if (cache_size <= 0) {
286
287
    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;
288
289
  } else {
    zathura->page_cache.size = cache_size;
290
291
  }

292
293
294
  zathura->page_cache.cache = g_malloc(zathura->page_cache.size * sizeof(int));
  zathura_page_cache_invalidate_all(zathura);

295
  return true;
Moritz Lipp's avatar
Moritz Lipp committed
296
297
298

error_free:

Moritz Lipp's avatar
Moritz Lipp committed
299
  if (zathura->ui.page_widget != NULL) {
300
    g_object_unref(zathura->ui.page_widget);
301
302
  }

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

307
  return false;
Moritz Lipp's avatar
Moritz Lipp committed
308
309
310
311
312
313
314
315
316
}

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

Moritz Lipp's avatar
Moritz Lipp committed
317
  document_close(zathura, false);
318

Moritz Lipp's avatar
Moritz Lipp committed
319
320
321
322
  if (zathura->ui.session != NULL) {
    girara_session_destroy(zathura->ui.session);
  }

323
324
325
326
327
328
329
  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));
  }

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

336
337
338
  /* bookmarks */
  girara_list_free(zathura->bookmarks.bookmarks);

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

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

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

Moritz Lipp's avatar
Moritz Lipp committed
353
  /* free registered plugins */
Moritz Lipp's avatar
Moritz Lipp committed
354
  zathura_plugin_manager_free(zathura->plugins.manager);
355
356
357
358

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

360
361
362
363
364
365
366
367
368
  /* 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);
  }

369
370
  g_free(zathura->page_cache.cache);

371
  g_free(zathura);
372
373
}

374
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
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
423
    zathura_plugin_manager_add_dir(zathura->plugins.manager, path);
424
425
426
427
428
429
    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
430
    zathura_plugin_manager_add_dir(zathura->plugins.manager, path);
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
    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
454
void
455
zathura_set_synctex(zathura_t* zathura, bool value)
Moritz Lipp's avatar
Moritz Lipp committed
456
457
458
459
460
461
462
{
  g_return_if_fail(zathura != NULL);
  g_return_if_fail(zathura->ui.session != NULL);

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

463
464
465
466
467
468
469
470
void
zathura_set_argv(zathura_t* zathura, char** argv)
{
  g_return_if_fail(zathura != NULL);

  zathura->global.arguments = argv;
}

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

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

509
510
  close(handle);

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

  return file;
}

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

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

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

548
  g_free(document_info);
Moritz Lipp's avatar
Update    
Moritz Lipp committed
549
  return FALSE;
550
551
}

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

560
  gchar* file_uri = NULL;
561
562
  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
563

Moritz Lipp's avatar
Moritz Lipp committed
564
  if (document == NULL) {
565
566
567
    if (error == NULL ) {
      girara_notify(zathura->ui.session, GIRARA_ERROR, _("Unsupported file type. Please install the necessary plugin."), false, NULL, NULL, 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;
    }
584
    goto error_out;
Moritz Lipp's avatar
Moritz Lipp committed
585
586
  }

587
588
  const char* file_path        = zathura_document_get_path(document);
  unsigned int number_of_pages = zathura_document_get_number_of_pages(document);
589

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

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

Moritz Lipp's avatar
Moritz Lipp committed
600
  /* set page offset */
601
  zathura_document_set_page_offset(document, file_info.page_offset);
602

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

  /* check current page number */
612
613
614
615
616
617
  /* 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) {
618
619
    girara_warning("document info: '%s' has an invalid page number", file_path);
    zathura_document_set_current_page_number(document, 0);
620
  } else {
621
    zathura_document_set_current_page_number(document, page_number);
622
623
624
  }

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

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

  /* update statusbar */
655
656
657
658
659
660
661
662
663
  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);
  }
664

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

  if (zathura->file_monitor.file == NULL) {
672
673
674
675
    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
676
677
678
  }

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

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

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

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

709
710
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
  /* 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);
  }

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

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

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

Moritz Lipp's avatar
Moritz Lipp committed
757
  /* threads */
Moritz Lipp's avatar
Moritz Lipp committed
758
  zathura->sync.render_thread = render_init(zathura);
Moritz Lipp's avatar
Moritz Lipp committed
759

Moritz Lipp's avatar
Moritz Lipp committed
760
  if (zathura->sync.render_thread == NULL) {
Moritz Lipp's avatar
Moritz Lipp committed
761
762
763
    goto error_free;
  }

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

768
  /* bookmarks */
769
  zathura_bookmarks_load(zathura, file_path);
770

Moritz Lipp's avatar
Moritz Lipp committed
771
  /* update title */
772
  basename_only = false;
773
774
775
776
777
778
779
780
  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
781

782
  g_free(file_uri);
Moritz Lipp's avatar
Moritz Lipp committed
783

784
  /* adjust window */
785
  girara_argument_t argument = { zathura_document_get_adjust_mode(document), NULL };
786
787
  sc_adjust_window(zathura->ui.session, &argument, NULL, 0);

788
789
  /* set position */
  if (file_info.position_x != 0 || file_info.position_y != 0) {
Moritz Lipp's avatar
Moritz Lipp committed
790
    position_set_delayed(zathura, file_info.position_x, file_info.position_y);
791
792
793
794
795
  } else {
    page_set_delayed(zathura, zathura_document_get_current_page_number(document));
    cb_view_vadjustment_value_changed(NULL, zathura);
  }

796
797
798
  /* Invalidate all current entries in the page cache */
  zathura_page_cache_invalidate_all(zathura);

Moritz Lipp's avatar
Moritz Lipp committed
799
  return true;
800
801
802

error_free:

Moritz Lipp's avatar
Moritz Lipp committed
803
804
805
806
  if (file_uri != NULL) {
    g_free(file_uri);
  }

807
808
809
810
811
  zathura_document_free(document);

error_out:

  return false;
Moritz Lipp's avatar
Moritz Lipp committed
812
813
}

814
void
815
816
document_open_idle(zathura_t* zathura, const char* path, const char* password,
                   int page_number)
817
818
819
820
821
822
823
{
  if (zathura == NULL || path == NULL) {
    return;
  }

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

824
825
826
827
  document_info->zathura     = zathura;
  document_info->path        = path;
  document_info->password    = password;
  document_info->page_number = page_number;
828
829
830
831

  gdk_threads_add_idle(document_info_open, document_info);
}

832
833
834
835
836
837
838
839
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);
840
841
842
843
844
845
846
847
848
  /* 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
849
  if ((overwrite == false) && g_file_test(file_path, G_FILE_TEST_EXISTS)) {
850
    girara_error("File already exists: %s. Use :write! to overwrite it.", file_path);
851
    g_free(file_path);
852
853
854
    return false;
  }

855
  zathura_error_t error = zathura_document_save_as(zathura->document, file_path);
856
  g_free(file_path);
857

858
  return (error == ZATHURA_ERROR_OK) ? true : false;
859
860
}

Pavel Borzenkov's avatar
Pavel Borzenkov committed
861
862
863
static void
remove_page_from_table(GtkWidget* page, gpointer permanent)
{
Moritz Lipp's avatar
Moritz Lipp committed
864
  if (permanent == false) {
Pavel Borzenkov's avatar
Pavel Borzenkov committed
865
866
867
    g_object_ref(G_OBJECT(page));
  }

868
  gtk_container_remove(GTK_CONTAINER(gtk_widget_get_parent(page)), page);
Pavel Borzenkov's avatar
Pavel Borzenkov committed
869
870
}

Moritz Lipp's avatar
Moritz Lipp committed
871
bool
Moritz Lipp's avatar
Moritz Lipp committed
872
document_close(zathura_t* zathura, bool keep_monitor)
Moritz Lipp's avatar
Moritz Lipp committed
873
{
Moritz Lipp's avatar
Moritz Lipp committed
874
  if (zathura == NULL || zathura->document == NULL) {
Moritz Lipp's avatar
Moritz Lipp committed
875
876
877
    return false;
  }

Moritz Lipp's avatar
Moritz Lipp committed
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
  /* 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
902
903
904
905
906
907
  /* remove marks */
  if (zathura->global.marks != NULL) {
    girara_list_free(zathura->global.marks);
    zathura->global.marks = NULL;
  }

908
909
  /* store file information */
  const char* path = zathura_document_get_path(zathura->document);
910

911
  zathura_fileinfo_t file_info = { 0, 0, 1, 0, 1, 1, 0, 0 };
912
913
914
915
916
  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);

917
  girara_setting_get(zathura->ui.session, "pages-per-row", &(file_info.pages_per_row));
918
  girara_setting_get(zathura->ui.session, "first-page-column", &(file_info.first_page_column));
919
920
921
922
923
924
925
926
927
928

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

931
  /* release render thread */
Pavel Borzenkov's avatar
Pavel Borzenkov committed
932
933
934
  render_free(zathura->sync.render_thread);
  zathura->sync.render_thread = NULL;

935
  /* remove widgets */
936
  gtk_container_foreach(GTK_CONTAINER(zathura->ui.page_widget), remove_page_from_table, (gpointer) 1);
Moritz Lipp's avatar
Moritz Lipp committed
937
938
  for (unsigned int i = 0; i < zathura_document_get_number_of_pages(zathura->document); i++) {
    g_object_unref(zathura->pages[i]);
939
    g_object_unref(zathura->pages[i]); // FIXME
Moritz Lipp's avatar
Moritz Lipp committed
940
  }
941
942
  free(zathura->pages);
  zathura->pages = NULL;
Moritz Lipp's avatar
Moritz Lipp committed
943

944
  /* remove document */
Moritz Lipp's avatar
Moritz Lipp committed
945
  zathura_document_free(zathura->document);
Pavel Borzenkov's avatar
Pavel Borzenkov committed
946
947
  zathura->document = NULL;

948
949
950
951
952
953
  /* remove index */
  if (zathura->ui.index != NULL) {
    g_object_ref_sink(zathura->ui.index);
    zathura->ui.index = NULL;
  }

954
  gtk_widget_hide(zathura->ui.page_widget);
955
956
957
958

  statusbar_page_number_update(zathura);

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

Moritz Lipp's avatar
Moritz Lipp committed
962
963
964
  /* update title */
  girara_set_window_title(zathura->ui.session, "zathura");

Moritz Lipp's avatar
Moritz Lipp committed
965
966
967
  return true;
}

968
static gboolean
Sebastian Ramacher's avatar
Sebastian Ramacher committed
969
970
page_set_delayed_impl(gpointer data)
{
971
972
973
974
975
976
977
978
979
980
  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)
{
981
982
  if (zathura == NULL || zathura->document == NULL ||
      (page_id >= zathura_document_get_number_of_pages(zathura->document))) {
983
984
985
986
987
988
    return false;
  }

  page_set_delayed_t* p = g_malloc(sizeof(page_set_delayed_t));
  p->zathura = zathura;
  p->page = page_id;
989
  gdk_threads_add_idle(page_set_delayed_impl, p);
990
991
992
  return true;
}

Moritz Lipp's avatar
Moritz Lipp committed
993
bool
Moritz Lipp's avatar
Moritz Lipp committed
994
page_set(zathura_t* zathura, unsigned int page_id)
Moritz Lipp's avatar
Moritz Lipp committed
995
{
996
  if (zathura == NULL || zathura->document == NULL) {
Moritz Lipp's avatar
Moritz Lipp committed
997
    goto error_out;
Moritz Lipp's avatar
Moritz Lipp committed
998
999
  }

1000
  /* render page */
For faster browsing, not all history is shown. View entire blame