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

3
#define _DEFAULT_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>
10
#include <string.h>
Moritz Lipp's avatar
Moritz Lipp committed
11

12
13
14
15
16
#include <girara/datastructures.h>
#include <girara/utils.h>
#include <girara/session.h>
#include <girara/statusbar.h>
#include <girara/settings.h>
17
#include <girara/shortcuts.h>
Sebastian Ramacher's avatar
Sebastian Ramacher committed
18
#include <girara/template.h>
19
#include <glib/gstdio.h>
20
#include <glib/gi18n.h>
21

Moritz Lipp's avatar
Moritz Lipp committed
22
23
24
25
#ifdef G_OS_UNIX
#include <glib-unix.h>
#endif

26
#include "bookmarks.h"
Moritz Lipp's avatar
Moritz Lipp committed
27
28
#include "callbacks.h"
#include "config.h"
29
30
31
32
#ifdef WITH_SQLITE
#include "database-sqlite.h"
#endif
#include "database-plain.h"
33
#include "document.h"
Moritz Lipp's avatar
Moritz Lipp committed
34
#include "shortcuts.h"
Moritz Lipp's avatar
Moritz Lipp committed
35
#include "zathura.h"
Moritz Lipp's avatar
Moritz Lipp committed
36
#include "utils.h"
Moritz Lipp's avatar
Moritz Lipp committed
37
#include "marks.h"
Sebastian Ramacher's avatar
Sebastian Ramacher committed
38
#include "render.h"
Moritz Lipp's avatar
Moritz Lipp committed
39
#include "page.h"
40
#include "page-widget.h"
41
#include "plugin.h"
42
#include "adjustment.h"
Sebastian Ramacher's avatar
Sebastian Ramacher committed
43
#include "dbus-interface.h"
Sebastian Ramacher's avatar
Sebastian Ramacher committed
44
#include "css-definitions.h"
45
#include "synctex.h"
Moritz Lipp's avatar
Moritz Lipp committed
46

Moritz Lipp's avatar
Moritz Lipp committed
47
typedef struct zathura_document_info_s {
Moritz Lipp's avatar
Update    
Moritz Lipp committed
48
49
50
  zathura_t* zathura;
  const char* path;
  const char* password;
51
  int page_number;
Sebastian Ramacher's avatar
Sebastian Ramacher committed
52
  const char* mode;
53
  const char* synctex;
Moritz Lipp's avatar
Update    
Moritz Lipp committed
54
55
} zathura_document_info_t;

56

57
static gboolean document_info_open(gpointer data);
58
59
60
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
61

Moritz Lipp's avatar
Moritz Lipp committed
62
63
64
65
#ifdef G_OS_UNIX
static gboolean zathura_signal_sigterm(gpointer data);
#endif

Moritz Lipp's avatar
Moritz Lipp committed
66
/* function implementation */
Moritz Lipp's avatar
Moritz Lipp committed
67
zathura_t*
68
zathura_create(void)
69
{
70
71
72
73
  zathura_t* zathura = g_try_malloc0(sizeof(zathura_t));
  if (zathura == NULL) {
    return NULL;
  }
74

75
  /* global settings */
76
  zathura->global.search_direction = FORWARD;
77

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

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

Moritz Lipp's avatar
Moritz Lipp committed
89
90
91
92
93
#ifdef G_OS_UNIX
  /* signal handler */
  zathura->signals.sigterm = g_unix_signal_add(SIGTERM, zathura_signal_sigterm, zathura);
#endif

94
95
96
97
98
99
100
101
102
103
  zathura->ui.session->global.data = zathura;

  return zathura;

error_out:

  zathura_free(zathura);

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

105
106
107
108
109
bool
zathura_init(zathura_t* zathura)
{
  if (zathura == NULL) {
    return false;
110
111
  }

112
  /* create zathura (config/data) directory */
113
114
115
116
117
118
119
  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));
  }
120

121
122
123
  /* load plugins */
  zathura_plugin_manager_load(zathura->plugins.manager);

124
125
  /* configuration */
  config_load_default(zathura);
126
  config_load_files(zathura);
127

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

  /* girara events */
Moritz Lipp's avatar
Moritz Lipp committed
134
135
  zathura->ui.session->events.buffer_changed  = cb_buffer_changed;
  zathura->ui.session->events.unknown_command = cb_unknown_command;
136

137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
  /* zathura signals */
  zathura->signals.refresh_view = g_signal_new("refresh-view",
                                               GTK_TYPE_WIDGET,
                                               G_SIGNAL_RUN_LAST,
                                               0,
                                               NULL,
                                               NULL,
                                               g_cclosure_marshal_generic,
                                               G_TYPE_NONE,
                                               1,
                                               G_TYPE_POINTER);

  g_signal_connect(G_OBJECT(zathura->ui.session->gtk.view), "refresh-view",
                   G_CALLBACK(cb_refresh_view), zathura);

152
  /* page view */
Moritz Lipp's avatar
Moritz Lipp committed
153
  zathura->ui.page_widget = gtk_grid_new();
154
155
  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
156
  if (zathura->ui.page_widget == NULL) {
157
158
159
    goto error_free;
  }

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

162
  GtkAdjustment* hadjustment = gtk_scrolled_window_get_hadjustment(
163
                 GTK_SCROLLED_WINDOW(zathura->ui.session->gtk.view));
164
165
166

  /* Connect hadjustment signals */
  g_signal_connect(G_OBJECT(hadjustment), "value-changed",
167
      G_CALLBACK(cb_view_hadjustment_value_changed), zathura);
168
169
170
171
  g_signal_connect(G_OBJECT(hadjustment), "changed",
      G_CALLBACK(cb_view_hadjustment_changed), zathura);

  GtkAdjustment* vadjustment = gtk_scrolled_window_get_vadjustment(
172
                 GTK_SCROLLED_WINDOW(zathura->ui.session->gtk.view));
173
174
175
176
177
178

  /* 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), "changed",
      G_CALLBACK(cb_view_vadjustment_changed), zathura);
Moritz Lipp's avatar
Moritz Lipp committed
179

180
  /* page view alignment */
181
182
  gtk_widget_set_halign(zathura->ui.page_widget, GTK_ALIGN_CENTER);
  gtk_widget_set_valign(zathura->ui.page_widget, GTK_ALIGN_CENTER);
183

184
185
186
187
  gtk_widget_set_hexpand_set(zathura->ui.page_widget, TRUE);
  gtk_widget_set_hexpand(zathura->ui.page_widget, FALSE);
  gtk_widget_set_vexpand_set(zathura->ui.page_widget, TRUE);
  gtk_widget_set_vexpand(zathura->ui.page_widget, FALSE);
188

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

191
  /* statusbar */
Moritz Lipp's avatar
Moritz Lipp committed
192
193
  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
194
    goto error_free;
Moritz Lipp's avatar
Moritz Lipp committed
195
196
  }

Moritz Lipp's avatar
Moritz Lipp committed
197
198
  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
199
    goto error_free;
Moritz Lipp's avatar
Moritz Lipp committed
200
201
  }

Moritz Lipp's avatar
Moritz Lipp committed
202
  zathura->ui.statusbar.page_number = girara_statusbar_item_add(zathura->ui.session, FALSE, FALSE, FALSE, NULL);
Moritz Lipp's avatar
Moritz Lipp committed
203
  if (zathura->ui.statusbar.page_number == NULL) {
Moritz Lipp's avatar
Moritz Lipp committed
204
    goto error_free;
Moritz Lipp's avatar
Moritz Lipp committed
205
206
  }

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

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

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

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

Sebastian Ramacher's avatar
Sebastian Ramacher committed
230
  if (zathura->database == NULL && g_strcmp0(database, "null") != 0) {
Sebastian Ramacher's avatar
Sebastian Ramacher committed
231
    girara_error("Unable to initialize database. Bookmarks won't be available.");
232
  } else {
Sebastian Ramacher's avatar
Sebastian Ramacher committed
233
    g_object_set(G_OBJECT(zathura->ui.session->command_history), "io", zathura->database, NULL);
234
  }
Sebastian Ramacher's avatar
Sebastian Ramacher committed
235
  g_free(database);
236

237
  /* bookmarks */
238
  zathura->bookmarks.bookmarks = girara_sorted_list_new2((girara_compare_function_t) zathura_bookmarks_compare,
Moritz Lipp's avatar
Moritz Lipp committed
239
                                 (girara_free_function_t) zathura_bookmark_free);
240

241
  /* jumplist */
242
243
  int jumplist_size = 20;
  girara_setting_get(zathura->ui.session, "jumplist-size", &jumplist_size);
244

245
246
  zathura->jumplist.max_size = jumplist_size < 0 ? 0 : jumplist_size;
  zathura->jumplist.list = NULL;
247
248
  zathura->jumplist.size = 0;
  zathura->jumplist.cur = NULL;
249

Sebastian Ramacher's avatar
Sebastian Ramacher committed
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
  /* CSS for index mode */
  GiraraTemplate* csstemplate = girara_session_get_template(zathura->ui.session);

  static const char* index_settings[] = {
    "index-fg",
    "index-bg",
    "index-active-fg",
    "index-active-bg"
  };

  for (size_t s = 0; s < LENGTH(index_settings); ++s) {
    girara_template_add_variable(csstemplate, index_settings[s]);

    char* tmp_value = NULL;
    GdkRGBA rgba = { 0, 0, 0, 0 };
    girara_setting_get(zathura->ui.session, index_settings[s], &tmp_value);
    if (tmp_value != NULL) {
      gdk_rgba_parse(&rgba, tmp_value);
      g_free(tmp_value);
    }

    char* color = gdk_rgba_to_string(&rgba);
    girara_template_set_variable_value(csstemplate,
        index_settings[s], color);
    g_free(color);
  }

  char* css = g_strdup_printf("%s\n%s", girara_template_get_base(csstemplate), CSS_TEMPLATE_INDEX);
  girara_template_set_base(csstemplate, css);
  g_free(css);

281
  /* Start D-Bus service */
Sebastian Ramacher's avatar
Sebastian Ramacher committed
282
283
284
285
  bool dbus = true;
  girara_setting_get(zathura->ui.session, "dbus-service", &dbus);
  if (dbus == true) {
    zathura->dbus = zathura_dbus_new(zathura);
286
287
  }

288
  return true;
Moritz Lipp's avatar
Moritz Lipp committed
289
290
291

error_free:

Moritz Lipp's avatar
Moritz Lipp committed
292
  if (zathura->ui.page_widget != NULL) {
293
    g_object_unref(zathura->ui.page_widget);
294
295
  }

296
  return false;
Moritz Lipp's avatar
Moritz Lipp committed
297
298
299
300
301
302
303
304
305
}

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

Moritz Lipp's avatar
Moritz Lipp committed
306
  document_close(zathura, false);
307

308
  /* stop D-Bus */
Sebastian Ramacher's avatar
Sebastian Ramacher committed
309
310
311
  if (zathura->dbus != NULL) {
    g_object_unref(zathura->dbus);
    zathura->dbus = NULL;
312
313
  }

Moritz Lipp's avatar
Moritz Lipp committed
314
315
316
317
  if (zathura->ui.session != NULL) {
    girara_session_destroy(zathura->ui.session);
  }

318
319
320
321
322
323
  /* stdin support */
  if (zathura->stdin_support.file != NULL) {
    g_unlink(zathura->stdin_support.file);
    g_free(zathura->stdin_support.file);
  }

324
325
326
  /* bookmarks */
  girara_list_free(zathura->bookmarks.bookmarks);

327
  /* database */
Sebastian Ramacher's avatar
Sebastian Ramacher committed
328
329
330
  if (zathura->database != NULL) {
    g_object_unref(G_OBJECT(zathura->database));
  }
331

332
  /* free print settings */
Moritz Lipp's avatar
Moritz Lipp committed
333
  if (zathura->print.settings != NULL) {
334
335
336
337
338
339
    g_object_unref(zathura->print.settings);
  }

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

Moritz Lipp's avatar
Moritz Lipp committed
341
  /* free registered plugins */
Moritz Lipp's avatar
Moritz Lipp committed
342
  zathura_plugin_manager_free(zathura->plugins.manager);
343
344
345
346

  /* free config variables */
  g_free(zathura->config.config_dir);
  g_free(zathura->config.data_dir);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
347
  g_free(zathura->config.cache_dir);
Moritz Lipp's avatar
Moritz Lipp committed
348

349
350
351
352
353
354
355
356
357
  /* 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);
  }

358
  g_free(zathura);
359
360
}

361
#ifdef GDK_WINDOWING_X11
362
363
364
365
366
367
368
void
zathura_set_xid(zathura_t* zathura, Window xid)
{
  g_return_if_fail(zathura != NULL);

  zathura->ui.session->gtk.embed = xid;
}
369
#endif
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387

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)
{
Sebastian Ramacher's avatar
Sebastian Ramacher committed
388
389
  g_return_if_fail(zathura != NULL);

390
391
392
393
394
395
396
  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);
  }
Sebastian Ramacher's avatar
Sebastian Ramacher committed
397
}
398

Sebastian Ramacher's avatar
Sebastian Ramacher committed
399
400
401
void
zathura_set_cache_dir(zathura_t* zathura, const char* dir)
{
402
  g_return_if_fail(zathura != NULL);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
403
404
405
406
407
408
409
410

  if (dir != NULL) {
    zathura->config.cache_dir = g_strdup(dir);
  } else {
    gchar* path = girara_get_xdg_path(XDG_CACHE);
    zathura->config.cache_dir = g_build_filename(path, "zathura", NULL);
    g_free(path);
  }
411
412
413
414
415
416
417
418
419
420
421
}

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
422
    zathura_plugin_manager_add_dir(zathura->plugins.manager, path);
423
424
425
426
427
428
    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
429
    zathura_plugin_manager_add_dir(zathura->plugins.manager, path);
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
    GIRARA_LIST_FOREACH_END(paths, char*, iter, path);
    girara_list_free(paths);
#endif
  }

}

void
zathura_set_argv(zathura_t* zathura, char** argv)
{
  g_return_if_fail(zathura != NULL);

  zathura->global.arguments = argv;
}

445
static gchar*
446
prepare_document_open_from_stdin(zathura_t* zathura, const char* path)
447
448
449
{
  g_return_val_if_fail(zathura, NULL);

450
451
452
453
454
455
456
457
458
459
460
461
462
463
  int infileno = -1;
  if (g_strcmp0(path, "-") == 0) {
    infileno = fileno(stdin);
  } else if (g_str_has_prefix(path, "/proc/self/fd/") == true) {
    char* begin = g_strrstr(path, "/") + 1;
    gint64 temp = g_ascii_strtoll(begin, NULL, 0);
    if (temp > INT_MAX || temp < 0) {
      return NULL;
    }
    infileno = (int) temp;
  } else {
    return NULL;
  }

464
465
466
  GError* error = NULL;
  gchar* file = NULL;
  gint handle = g_file_open_tmp("zathura.stdin.XXXXXX", &file, &error);
Moritz Lipp's avatar
Moritz Lipp committed
467
  if (handle == -1) {
468
469
470
471
    if (error != NULL) {
      girara_error("Can not create temporary file: %s", error->message);
      g_error_free(error);
    }
472
473
474
    return NULL;
  }

475
476
477
  // read and dump to temporary file
  if (infileno == -1) {
    girara_error("Can not read from file descriptor.");
478
479
480
481
482
483
484
485
    close(handle);
    g_unlink(file);
    g_free(file);
    return NULL;
  }

  char buffer[BUFSIZ];
  ssize_t count = 0;
486
  while ((count = read(infileno, buffer, BUFSIZ)) > 0) {
Moritz Lipp's avatar
Moritz Lipp committed
487
    if (write(handle, buffer, count) != count) {
488
489
490
491
492
493
494
      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
495

496
497
  close(handle);

Moritz Lipp's avatar
Moritz Lipp committed
498
  if (count != 0) {
499
    girara_error("Can not read from file descriptor.");
500
501
502
503
504
505
506
507
    g_unlink(file);
    g_free(file);
    return NULL;
  }

  return file;
}

508
static gboolean
Moritz Lipp's avatar
Update    
Moritz Lipp committed
509
510
511
512
513
document_info_open(gpointer data)
{
  zathura_document_info_t* document_info = data;
  g_return_val_if_fail(document_info != NULL, FALSE);

514
  if (document_info->zathura != NULL && document_info->path != NULL) {
515
    char* file = NULL;
516
517
518
    if (g_strcmp0(document_info->path, "-") == 0 ||
        g_str_has_prefix(document_info->path, "/proc/self/fd/") == true) {
      file = prepare_document_open_from_stdin(document_info->zathura, document_info->path);
519
520
      if (file == NULL) {
        girara_notify(document_info->zathura->ui.session, GIRARA_ERROR,
521
                      _("Could not read file from stdin and write it to a temporary file."));
522
523
      } else {
        document_info->zathura->stdin_support.file = g_strdup(file);
524
525
526
527
528
529
      }
    } else {
      file = g_strdup(document_info->path);
    }

    if (file != NULL) {
530
531
532
533
534
535
536
      if (document_info->synctex != NULL) {
        document_open_synctex(document_info->zathura, file,
                              document_info->password, document_info->synctex);
      } else {
        document_open(document_info->zathura, file, document_info->password,
                      document_info->page_number);
      }
537
      g_free(file);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
538
539
540
541
542
543
544
545
546
547
548
549

      if (document_info->mode != NULL) {
        if (g_strcmp0(document_info->mode, "presentation") == 0) {
          sc_toggle_presentation(document_info->zathura->ui.session, NULL, NULL,
                                 0);
        } else if (g_strcmp0(document_info->mode, "fullscreen") == 0) {
          sc_toggle_fullscreen(document_info->zathura->ui.session, NULL, NULL,
                               0);
        } else {
          girara_error("Unknown mode: %s", document_info->mode);
        }
      }
550
    }
Moritz Lipp's avatar
Update    
Moritz Lipp committed
551
552
  }

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

557
static char*
558
get_formatted_filename(zathura_t* zathura, const char* file_path, bool statusbar)
559
560
{
  bool basename_only = false;
Sebastian Ramacher's avatar
Sebastian Ramacher committed
561
  if (statusbar == true) {
562
563
564
565
566
    girara_setting_get(zathura->ui.session, "window-title-basename", &basename_only);
  } else {
    girara_setting_get(zathura->ui.session, "statusbar-basename", &basename_only);
  }

567
568
  if (basename_only == false) {
    bool home_tilde = false;
569
570
571
572
573
574
    if (statusbar) {
      girara_setting_get(zathura->ui.session, "statusbar-home-tilde", &home_tilde);
    } else {
      girara_setting_get(zathura->ui.session, "window-title-home-tilde", &home_tilde);
    }

Sebastian Ramacher's avatar
Sebastian Ramacher committed
575
    const size_t file_path_len = file_path ? strlen(file_path) : 0;
576

Sebastian Ramacher's avatar
Sebastian Ramacher committed
577
578
579
    if (home_tilde == true) {
      char* home = girara_get_home_directory(NULL);
      const size_t home_len = home ? strlen(home) : 0;
580

Sidharth Kapur's avatar
Sidharth Kapur committed
581
582
583
584
      if (home_len > 1
          && file_path_len >= home_len
          && g_str_has_prefix(file_path, home)
          && (!file_path[home_len] || file_path[home_len] == '/')) {
Sebastian Ramacher's avatar
Sebastian Ramacher committed
585
        g_free(home);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
586
        return g_strdup_printf("~%s", &file_path[home_len]);
587
      } else {
Sebastian Ramacher's avatar
Sebastian Ramacher committed
588
        g_free(home);
589
        return g_strdup(file_path);
590
591
      }
    } else {
592
      return g_strdup(file_path);
593
594
    }
  } else {
595
    const char* basename = zathura_document_get_basename(zathura->document);
596
    return g_strdup(basename);
597
598
599
  }
}

600
601
602
603
604
605
606
607
608
609
static gboolean
document_open_password_dialog(gpointer data)
{
  zathura_password_dialog_info_t* password_dialog_info = data;

  girara_dialog(password_dialog_info->zathura->ui.session, _("Enter password:"), true, NULL,
                (girara_callback_inputbar_activate_t) cb_password_dialog, password_dialog_info);
  return FALSE;
}

Moritz Lipp's avatar
Moritz Lipp committed
610
bool
611
612
document_open(zathura_t* zathura, const char* path, const char* password,
              int page_number)
Moritz Lipp's avatar
Moritz Lipp committed
613
{
614
  if (zathura == NULL || zathura->plugins.manager == NULL || path == NULL) {
615
    goto error_out;
Moritz Lipp's avatar
Moritz Lipp committed
616
617
  }

618
  gchar* file_uri = NULL;
619
620
  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
621

Moritz Lipp's avatar
Moritz Lipp committed
622
  if (document == NULL) {
623
    if (error == ZATHURA_ERROR_INVALID_PASSWORD) {
624
      girara_debug("Invalid or no password.");
625
626
627
628
629
630
      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);
631
          gdk_threads_add_idle(document_open_password_dialog, password_dialog_info);
632
633
634
635
636
637
638
          goto error_out;
        } else {
          free(password_dialog_info);
        }
      }
      goto error_out;
    }
Moritz Lipp's avatar
Moritz Lipp committed
639
640
641
    if (error == ZATHURA_ERROR_OK ) {
      girara_notify(zathura->ui.session, GIRARA_ERROR, _("Unsupported file type. Please install the necessary plugin."));
    }
642
    goto error_out;
Moritz Lipp's avatar
Moritz Lipp committed
643
644
  }

645
646
  const char* file_path        = zathura_document_get_path(document);
  unsigned int number_of_pages = zathura_document_get_number_of_pages(document);
647

648
649
650
651
652
653
  if (number_of_pages == 0) {
    girara_notify(zathura->ui.session, GIRARA_WARNING,
        _("Document does not contain any pages"));
    goto error_free;
  }

654
  /* read history file */
655
  char first_page_column_list_default[] = "1:2";
656
657
658
659
660
661
  zathura_fileinfo_t file_info = {
    .current_page = 0,
    .page_offset = 0,
    .scale = 1,
    .rotation = 0,
    .pages_per_row = 0,
662
    .first_page_column_list = first_page_column_list_default,
663
664
665
    .position_x = 0,
    .position_y = 0
  };
666
667
668
669
  bool known_file = false;
  if (zathura->database != NULL) {
    known_file = zathura_db_get_fileinfo(zathura->database, file_path, &file_info);
  }
670

Moritz Lipp's avatar
Moritz Lipp committed
671
  /* set page offset */
672
  zathura_document_set_page_offset(document, file_info.page_offset);
673

674
  /* check for valid scale value */
675
676
677
  if (file_info.scale <= DBL_EPSILON) {
    file_info.scale = 1;
  }
Moritz Lipp's avatar
Moritz Lipp committed
678
679
  zathura_document_set_scale(document,
      zathura_correct_scale_value(zathura->ui.session, file_info.scale));
680
681

  /* check current page number */
682
683
684
685
686
687
  /* 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) {
688
689
    girara_warning("document info: '%s' has an invalid page number", file_path);
    zathura_document_set_current_page_number(document, 0);
690
  } else {
691
    zathura_document_set_current_page_number(document, page_number);
692
693
694
  }

  /* check for valid rotation */
695
  if (file_info.rotation % 90 != 0) {
696
697
698
    girara_warning("document info: '%s' has an invalid rotation", file_path);
    zathura_document_set_rotation(document, 0);
  } else {
699
    zathura_document_set_rotation(document, file_info.rotation % 360);
700
701
702
703
704
705
706
707
708
709
710
  }

  /* 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";
711
  if (known_file == false && girara_setting_get(zathura->ui.session, "adjust-open", &(adjust_open)) == true) {
712
713
714
715
716
717
718
719
    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);
720
721
  } else {
    zathura_document_set_adjust_mode(document, ZATHURA_ADJUST_NONE);
722
723
  }

724
725
726
727
728
  /* initialize bisect state */
  zathura->bisect.start = 0;
  zathura->bisect.last_jump = zathura_document_get_current_page_number(document);
  zathura->bisect.end = number_of_pages - 1;

729
  /* update statusbar */
730
731
732
  char* filename = get_formatted_filename(zathura, file_path, true);
  girara_statusbar_item_set_text(zathura->ui.session, zathura->ui.statusbar.file, filename);
  g_free(filename);
733

Moritz Lipp's avatar
Moritz Lipp committed
734
  /* install file monitor */
735
  file_uri = g_filename_to_uri(file_path, NULL, NULL);
Moritz Lipp's avatar
Moritz Lipp committed
736
737
738
739
740
  if (file_uri == NULL) {
    goto error_free;
  }

  if (zathura->file_monitor.file == NULL) {
741
742
743
744
    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
745
746
747
  }

  if (zathura->file_monitor.monitor == NULL) {
748
749
750
751
752
    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
753
754
755
  }

  if (zathura->file_monitor.file_path == NULL) {
756
    zathura->file_monitor.file_path = g_strdup(file_path);
757
758
759
    if (zathura->file_monitor.file_path == NULL) {
      goto error_free;
    }
Moritz Lipp's avatar
Moritz Lipp committed
760
761
  }

762
  if (password != NULL) {
763
    g_free(zathura->file_monitor.password);
764
    zathura->file_monitor.password = g_strdup(password);
Moritz Lipp's avatar
Moritz Lipp committed
765
766
767
768
769
    if (zathura->file_monitor.password == NULL) {
      goto error_free;
    }
  }

Moritz Lipp's avatar
Moritz Lipp committed
770
771
772
773
774
775
  /* 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
776
  zathura->document = document;
Moritz Lipp's avatar
Moritz Lipp committed
777

778
779
780
781
782
783
784
785
786
  /* page cache size */
  int cache_size = 0;
  girara_setting_get(zathura->ui.session, "page-cache-size", &cache_size);
  if (cache_size <= 0) {
    girara_warning("page-cache-size is not positive, using %d instead",
        ZATHURA_PAGE_CACHE_DEFAULT_SIZE);
    cache_size = ZATHURA_PAGE_CACHE_DEFAULT_SIZE;
  }

787
  /* threads */
788
  zathura->sync.render_thread = zathura_renderer_new(cache_size);
789
790
791
792
793

  if (zathura->sync.render_thread == NULL) {
    goto error_free;
  }

Sebastian Ramacher's avatar
Sebastian Ramacher committed
794
  /* set up recolor info in ZathuraRenderer */
795
796
797
798
799
800
801
802
803
  char* recolor_dark = NULL;
  char* recolor_light = NULL;
  girara_setting_get(zathura->ui.session, "recolor-darkcolor", &recolor_dark);
  girara_setting_get(zathura->ui.session, "recolor-lightcolor", &recolor_light);
  zathura_renderer_set_recolor_colors_str(zathura->sync.render_thread,
      recolor_light, recolor_dark);
  g_free(recolor_dark);
  g_free(recolor_light);

804
805
806
807
808
  bool recolor = false;
  girara_setting_get(zathura->ui.session, "recolor", &recolor);
  zathura_renderer_enable_recolor(zathura->sync.render_thread, recolor);
  girara_setting_get(zathura->ui.session, "recolor-keephue", &recolor);
  zathura_renderer_enable_recolor_hue(zathura->sync.render_thread, recolor);
809
810
  girara_setting_get(zathura->ui.session, "recolor-reverse-video", &recolor);
  zathura_renderer_enable_recolor_reverse_video(zathura->sync.render_thread, recolor);
811

812
813
814
815
816
817
  /* get view port size */
  GtkAdjustment* hadjustment = gtk_scrolled_window_get_hadjustment(
                 GTK_SCROLLED_WINDOW(zathura->ui.session->gtk.view));
  GtkAdjustment* vadjustment = gtk_scrolled_window_get_vadjustment(
                 GTK_SCROLLED_WINDOW(zathura->ui.session->gtk.view));

818
  const unsigned int view_width = (unsigned int)floor(gtk_adjustment_get_page_size(hadjustment));
819
  zathura_document_set_viewport_width(zathura->document, view_width);
820
  const unsigned int view_height = (unsigned int)floor(gtk_adjustment_get_page_size(vadjustment));
821
822
  zathura_document_set_viewport_height(zathura->document, view_height);

823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
  /* 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;
    }

akbjker's avatar
akbjker committed
840
    g_object_ref(page_widget);
841
842
    zathura->pages[page_id] = page_widget;

843
844
845
    gtk_widget_set_halign(page_widget, GTK_ALIGN_CENTER);
    gtk_widget_set_valign(page_widget, GTK_ALIGN_CENTER);

846
847
848
    g_signal_connect(G_OBJECT(page_widget), "text-selected",
        G_CALLBACK(cb_page_widget_text_selected), zathura);
    g_signal_connect(G_OBJECT(page_widget), "image-selected",
849
        G_CALLBACK(cb_page_widget_image_selected), zathura);
850
851
852
853
    g_signal_connect(G_OBJECT(page_widget), "enter-link",
        G_CALLBACK(cb_page_widget_link), (gpointer) true);
    g_signal_connect(G_OBJECT(page_widget), "leave-link",
        G_CALLBACK(cb_page_widget_link), (gpointer) false);
854
855
    g_signal_connect(G_OBJECT(page_widget), "scaled-button-release",
        G_CALLBACK(cb_page_widget_scaled_button_release), zathura);
856
857
  }

858
  /* view mode */
859
860
  unsigned int pages_per_row = 1;
  unsigned int first_page_column = 1;
861
  char* first_page_column_list = first_page_column_list_default;
862
863
864
865
  unsigned int page_padding = 1;

  girara_setting_get(zathura->ui.session, "page-padding", &page_padding);

866
867
868
869
870
  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
871

872
873
874
  /* read first_page_column list */
  if (strcmp(file_info.first_page_column_list, "")) {
    first_page_column_list = file_info.first_page_column_list;
875
876
877
878
  } else {
    girara_setting_get(zathura->ui.session, "first-page-column", &first_page_column);
  }

Moritz Lipp's avatar
Moritz Lipp committed
879
  girara_setting_set(zathura->ui.session, "pages-per-row", &pages_per_row);
880
  girara_setting_set(zathura->ui.session, "first-page-column", &first_page_column);
881
882
883

  page_widget_set_mode(zathura, page_padding, pages_per_row, first_page_column);
  zathura_document_set_page_layout(zathura->document, page_padding, pages_per_row, first_page_column);
Moritz Lipp's avatar
Moritz Lipp committed
884

885
  girara_set_view(zathura->ui.session, zathura->ui.page_widget);
Moritz Lipp's avatar
Moritz Lipp committed
886

Moritz Lipp's avatar
Moritz Lipp committed
887

888
  /* bookmarks */
889
890
  if (zathura->database != NULL) {
    if (zathura_bookmarks_load(zathura, file_path) == false) {
891
      girara_debug("Failed to load bookmarks.");
892
    }
893

894
895
896
897
    /* jumplist */
    if (zathura_jumplist_load(zathura, file_path) == false) {
      zathura->jumplist.list = girara_list_new2(g_free);
    }
898
899
  }

Moritz Lipp's avatar
Moritz Lipp committed
900
  /* update title */
901
902
903
  char* formatted_filename = get_formatted_filename(zathura, file_path, false);
  girara_set_window_title(zathura->ui.session, formatted_filename);
  g_free(formatted_filename);
Moritz Lipp's avatar
Moritz Lipp committed
904

905
  g_free(file_uri);
Moritz Lipp's avatar
Moritz Lipp committed
906

907
  /* adjust_view */
908
  adjust_view(zathura);
909
910
911
912
913
914
915
916
917
  for (unsigned int page_id = 0; page_id < number_of_pages; page_id++) {
    /* set widget size */
    zathura_page_t* page = zathura_document_get_page(document, page_id);
    unsigned int page_height = 0;
    unsigned int page_width  = 0;

    /* adjust_view calls render_all in some cases and render_all calls
     * gtk_widget_set_size_request. To be sure that it's really called, do it
     * here once again. */
Sebastian Ramacher's avatar
Sebastian Ramacher committed
918
919
    const double height = zathura_page_get_height(page);
    const double width = zathura_page_get_width(page);
920
921
922
923
924
925
    page_calc_height_width(zathura->document, height, width, &page_height, &page_width, true);
    gtk_widget_set_size_request(zathura->pages[page_id], page_width, page_height);

    /* show widget */
    gtk_widget_show(zathura->pages[page_id]);
  }
926

927
  /* Set page */
928
  page_set(zathura, zathura_document_get_current_page_number(document));
929
930

  /* Set position (only if restoring from history file) */
Sebastian Ramacher's avatar
CS    
Sebastian Ramacher committed
931
932
933
  if (file_info.current_page == zathura_document_get_current_page_number(document) &&
      (file_info.position_x != 0 || file_info.position_y != 0)) {
    position_set(zathura, file_info.position_x, file_info.position_y);
934
935
  }

936
937
  update_visible_pages(zathura);

Moritz Lipp's avatar
Moritz Lipp committed
938
  return true;
939
940
941

error_free:

Moritz Lipp's avatar
Moritz Lipp committed
942
943
944
945
  if (file_uri != NULL) {
    g_free(file_uri);
  }

946
947
948
949
950
  zathura_document_free(document);

error_out:

  return false;
Moritz Lipp's avatar
Moritz Lipp committed
951
952
}

953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
bool
document_open_synctex(zathura_t* zathura, const char* path,
                      const char* password, const char* synctex)
{
  bool ret = document_open(zathura, path, password,
                           ZATHURA_PAGE_NUMBER_UNSPECIFIED);
  if (ret == false) {
    return false;
  }
  if (synctex == NULL) {
    return true;
  }

  int line = 0;
  int column = 0;
  char* input_file = NULL;
  if (synctex_parse_input(synctex, &input_file, &line, &column) == false) {
    return false;
  }

  ret = synctex_view(zathura, input_file, line, column);
  g_free(input_file);
  return ret;
}

978
void
979
document_open_idle(zathura_t* zathura, const char* path, const char* password,
980
                   int page_number, const char* mode, const char* synctex)
981
982
983
984
985
{
  if (zathura == NULL || path == NULL) {
    return;
  }

986
987
988
989
  zathura_document_info_t* document_info = g_try_malloc0(sizeof(zathura_document_info_t));
  if (document_info == NULL) {
    return;
  }
990

991
992
993
994
  document_info->zathura     = zathura;
  document_info->path        = path;
  document_info->password    = password;
  document_info->page_number = page_number;
Sebastian Ramacher's avatar
Sebastian Ramacher committed
995
  document_info->mode        = mode;
996
  document_info->synctex     = synctex;
997
998
999
1000

  gdk_threads_add_idle(document_info_open, document_info);
}

1001
1002
1003
1004
1005
1006
1007
1008
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);
1009
1010
1011
1012
1013
1014
1015
1016
1017
  /* 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
1018
  if ((overwrite == false) && g_file_test(file_path, G_FILE_TEST_EXISTS)) {
1019
    girara_error("File already exists: %s. Use :write! to overwrite it.", file_path);
1020
    g_free(file_path);
1021
1022
1023
    return false;
  }

1024
  zathura_error_t error = zathura_document_save_as(zathura->document, file_path);
1025
  g_free(file_path);
1026

1027
  return (error == ZATHURA_ERROR_OK) ? true : false;
1028
1029
}

Pavel Borzenkov's avatar
Pavel Borzenkov committed
1030
static void
1031
remove_page_from_table(GtkWidget* page, gpointer UNUSED(permanent))
Pavel Borzenkov's avatar
Pavel Borzenkov committed
1032
{
1033
  gtk_container_remove(GTK_CONTAINER(gtk_widget_get_parent(page)), page);
Pavel Borzenkov's avatar
Pavel Borzenkov committed
1034
1035
}

Moritz Lipp's avatar
Moritz Lipp committed
1036
bool
Moritz Lipp's avatar
Moritz Lipp committed
1037
document_close(zathura_t* zathura, bool keep_monitor)
Moritz Lipp's avatar
Moritz Lipp committed
1038
{
Moritz Lipp's avatar
Moritz Lipp committed
1039
  if (zathura == NULL || zathura->document == NULL) {
Moritz Lipp's avatar
Moritz Lipp committed
1040
1041
1042
    return false;
  }