zathura.c 43.4 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
  zathura_t* zathura;
Sebastian Ramacher's avatar
Sebastian Ramacher committed
49
50
  char* path;
  char* password;
51
  int page_number;
Sebastian Ramacher's avatar
Sebastian Ramacher committed
52
53
  char* mode;
  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);
Moritz Lipp's avatar
Moritz Lipp committed
58

Moritz Lipp's avatar
Moritz Lipp committed
59
60
61
62
#ifdef G_OS_UNIX
static gboolean zathura_signal_sigterm(gpointer data);
#endif

Sebastian Ramacher's avatar
Sebastian Ramacher committed
63
64
65
66
67
68
69
70
71
72
73
74
75
76
static void
free_document_info(zathura_document_info_t* document_info)
{
  if (document_info == NULL) {
    return;
  }

  g_free(document_info->path);
  g_free(document_info->password);
  g_free(document_info->mode);
  g_free(document_info->synctex);
  g_free(document_info);
}

Moritz Lipp's avatar
Moritz Lipp committed
77
/* function implementation */
Moritz Lipp's avatar
Moritz Lipp committed
78
zathura_t*
79
zathura_create(void)
80
{
81
82
83
84
  zathura_t* zathura = g_try_malloc0(sizeof(zathura_t));
  if (zathura == NULL) {
    return NULL;
  }
85

86
  /* global settings */
87
  zathura->global.search_direction = FORWARD;
88

89
  /* plugins */
Moritz Lipp's avatar
Moritz Lipp committed
90
91
  zathura->plugins.manager = zathura_plugin_manager_new();
  if (zathura->plugins.manager == NULL) {
92
    goto error_out;
Moritz Lipp's avatar
Moritz Lipp committed
93
  }
94

95
96
97
  /* UI */
  if ((zathura->ui.session = girara_session_create()) == NULL) {
    goto error_out;
Sebastian Ramacher's avatar
Sebastian Ramacher committed
98
99
  }

Moritz Lipp's avatar
Moritz Lipp committed
100
101
102
103
104
#ifdef G_OS_UNIX
  /* signal handler */
  zathura->signals.sigterm = g_unix_signal_add(SIGTERM, zathura_signal_sigterm, zathura);
#endif

105
106
107
108
109
110
111
112
113
114
  zathura->ui.session->global.data = zathura;

  return zathura;

error_out:

  zathura_free(zathura);

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

116
117
118
119
120
bool
zathura_init(zathura_t* zathura)
{
  if (zathura == NULL) {
    return false;
121
122
  }

123
  /* create zathura (config/data) directory */
124
125
126
127
128
129
130
  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));
  }
131

132
133
134
  /* load plugins */
  zathura_plugin_manager_load(zathura->plugins.manager);

135
136
  /* configuration */
  config_load_default(zathura);
137
  config_load_files(zathura);
138

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

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

148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
  /* 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);

163
  /* page view */
Moritz Lipp's avatar
Moritz Lipp committed
164
  zathura->ui.page_widget = gtk_grid_new();
165
166
  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
167
  if (zathura->ui.page_widget == NULL) {
168
169
170
    goto error_free;
  }

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

173
  GtkAdjustment* hadjustment = gtk_scrolled_window_get_hadjustment(
174
                 GTK_SCROLLED_WINDOW(zathura->ui.session->gtk.view));
175
176
177

  /* Connect hadjustment signals */
  g_signal_connect(G_OBJECT(hadjustment), "value-changed",
178
      G_CALLBACK(cb_view_hadjustment_value_changed), zathura);
179
180
181
182
  g_signal_connect(G_OBJECT(hadjustment), "changed",
      G_CALLBACK(cb_view_hadjustment_changed), zathura);

  GtkAdjustment* vadjustment = gtk_scrolled_window_get_vadjustment(
183
                 GTK_SCROLLED_WINDOW(zathura->ui.session->gtk.view));
184
185
186
187
188
189

  /* 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
190

191
  /* page view alignment */
192
193
  gtk_widget_set_halign(zathura->ui.page_widget, GTK_ALIGN_CENTER);
  gtk_widget_set_valign(zathura->ui.page_widget, GTK_ALIGN_CENTER);
194

195
196
197
198
  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);
199

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

202
  /* statusbar */
Moritz Lipp's avatar
Moritz Lipp committed
203
204
  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
205
    goto error_free;
Moritz Lipp's avatar
Moritz Lipp committed
206
207
  }

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

Moritz Lipp's avatar
Moritz Lipp committed
213
  zathura->ui.statusbar.page_number = girara_statusbar_item_add(zathura->ui.session, FALSE, FALSE, FALSE, NULL);
Moritz Lipp's avatar
Moritz Lipp committed
214
  if (zathura->ui.statusbar.page_number == NULL) {
Moritz Lipp's avatar
Moritz Lipp committed
215
    goto error_free;
Moritz Lipp's avatar
Moritz Lipp committed
216
217
  }

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

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

223
  /* database */
224
225
226
227
  char* database = NULL;
  girara_setting_get(zathura->ui.session, "database", &database);

  if (g_strcmp0(database, "plain") == 0) {
228
    girara_debug("Using plain database backend.");
229
230
231
    zathura->database = zathura_plaindatabase_new(zathura->config.data_dir);
#ifdef WITH_SQLITE
  } else if (g_strcmp0(database, "sqlite") == 0) {
232
    girara_debug("Using sqlite database backend.");
233
234
235
236
    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
237
  } else if (g_strcmp0(database, "null") != 0) {
238
239
240
    girara_error("Database backend '%s' is not supported.", database);
  }

Sebastian Ramacher's avatar
Sebastian Ramacher committed
241
  if (zathura->database == NULL && g_strcmp0(database, "null") != 0) {
Sebastian Ramacher's avatar
Sebastian Ramacher committed
242
    girara_error("Unable to initialize database. Bookmarks won't be available.");
243
  } else {
Sebastian Ramacher's avatar
Sebastian Ramacher committed
244
    g_object_set(G_OBJECT(zathura->ui.session->command_history), "io", zathura->database, NULL);
245
  }
Sebastian Ramacher's avatar
Sebastian Ramacher committed
246
  g_free(database);
247

248
  /* bookmarks */
249
  zathura->bookmarks.bookmarks = girara_sorted_list_new2((girara_compare_function_t) zathura_bookmarks_compare,
Moritz Lipp's avatar
Moritz Lipp committed
250
                                 (girara_free_function_t) zathura_bookmark_free);
251

252
  /* jumplist */
253
254
  int jumplist_size = 20;
  girara_setting_get(zathura->ui.session, "jumplist-size", &jumplist_size);
255

256
257
  zathura->jumplist.max_size = jumplist_size < 0 ? 0 : jumplist_size;
  zathura->jumplist.list = NULL;
258
259
  zathura->jumplist.size = 0;
  zathura->jumplist.cur = NULL;
260

Sebastian Ramacher's avatar
Sebastian Ramacher committed
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
  /* 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);

292
293
294
295
296
297
298
299
300
301
  /* Shortcut helpers */
  zathura->shortcut.mouse.x = 0;
  zathura->shortcut.mouse.y = 0;

  zathura->shortcut.toggle_page_mode.pages = 2;

  zathura->shortcut.toggle_presentation_mode.pages = 1;
  zathura->shortcut.toggle_presentation_mode.first_page_column_list = NULL;
  zathura->shortcut.toggle_presentation_mode.zoom = 1.0;

302
  /* Start D-Bus service */
Sebastian Ramacher's avatar
Sebastian Ramacher committed
303
304
305
306
  bool dbus = true;
  girara_setting_get(zathura->ui.session, "dbus-service", &dbus);
  if (dbus == true) {
    zathura->dbus = zathura_dbus_new(zathura);
307
308
  }

309
  return true;
Moritz Lipp's avatar
Moritz Lipp committed
310
311
312

error_free:

Moritz Lipp's avatar
Moritz Lipp committed
313
  if (zathura->ui.page_widget != NULL) {
314
    g_object_unref(zathura->ui.page_widget);
315
316
  }

317
  return false;
Moritz Lipp's avatar
Moritz Lipp committed
318
319
320
321
322
323
324
325
326
}

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

Moritz Lipp's avatar
Moritz Lipp committed
327
  document_close(zathura, false);
328

329
  /* stop D-Bus */
Sebastian Ramacher's avatar
Sebastian Ramacher committed
330
331
332
  if (zathura->dbus != NULL) {
    g_object_unref(zathura->dbus);
    zathura->dbus = NULL;
333
334
  }

Moritz Lipp's avatar
Moritz Lipp committed
335
336
337
338
  if (zathura->ui.session != NULL) {
    girara_session_destroy(zathura->ui.session);
  }

339
340
341
  /* shortcut */
  g_free(zathura->shortcut.toggle_presentation_mode.first_page_column_list);

342
343
344
345
346
347
  /* stdin support */
  if (zathura->stdin_support.file != NULL) {
    g_unlink(zathura->stdin_support.file);
    g_free(zathura->stdin_support.file);
  }

348
349
350
  /* bookmarks */
  girara_list_free(zathura->bookmarks.bookmarks);

351
  /* database */
Sebastian Ramacher's avatar
Sebastian Ramacher committed
352
353
354
  if (zathura->database != NULL) {
    g_object_unref(G_OBJECT(zathura->database));
  }
355

356
  /* free print settings */
Moritz Lipp's avatar
Moritz Lipp committed
357
  if (zathura->print.settings != NULL) {
358
359
360
361
362
363
    g_object_unref(zathura->print.settings);
  }

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

Moritz Lipp's avatar
Moritz Lipp committed
365
  /* free registered plugins */
Moritz Lipp's avatar
Moritz Lipp committed
366
  zathura_plugin_manager_free(zathura->plugins.manager);
367
368
369
370

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

373
374
375
376
377
378
379
380
381
  /* 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);
  }

382
  g_free(zathura);
383
384
}

385
#ifdef GDK_WINDOWING_X11
386
387
388
389
390
391
392
void
zathura_set_xid(zathura_t* zathura, Window xid)
{
  g_return_if_fail(zathura != NULL);

  zathura->ui.session->gtk.embed = xid;
}
393
#endif
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411

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
412
413
  g_return_if_fail(zathura != NULL);

414
415
416
417
418
419
420
  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
421
}
422

Sebastian Ramacher's avatar
Sebastian Ramacher committed
423
424
425
void
zathura_set_cache_dir(zathura_t* zathura, const char* dir)
{
426
  g_return_if_fail(zathura != NULL);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
427
428
429
430
431
432
433
434

  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);
  }
435
436
437
438
439
440
441
442
443
444
445
}

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
446
    zathura_plugin_manager_add_dir(zathura->plugins.manager, path);
447
448
449
450
451
452
    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
453
    zathura_plugin_manager_add_dir(zathura->plugins.manager, path);
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
    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;
}

469
static gchar*
Sebastian Ramacher's avatar
Sebastian Ramacher committed
470
prepare_document_open_from_stdin(const char* path)
471
{
472
473
474
475
476
477
478
479
480
481
482
483
484
485
  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;
  }

486
487
488
  GError* error = NULL;
  gchar* file = NULL;
  gint handle = g_file_open_tmp("zathura.stdin.XXXXXX", &file, &error);
Moritz Lipp's avatar
Moritz Lipp committed
489
  if (handle == -1) {
490
491
492
493
    if (error != NULL) {
      girara_error("Can not create temporary file: %s", error->message);
      g_error_free(error);
    }
494
495
496
    return NULL;
  }

497
498
499
  // read and dump to temporary file
  if (infileno == -1) {
    girara_error("Can not read from file descriptor.");
500
501
502
503
504
505
506
507
    close(handle);
    g_unlink(file);
    g_free(file);
    return NULL;
  }

  char buffer[BUFSIZ];
  ssize_t count = 0;
508
  while ((count = read(infileno, buffer, BUFSIZ)) > 0) {
Moritz Lipp's avatar
Moritz Lipp committed
509
    if (write(handle, buffer, count) != count) {
510
511
512
513
514
515
516
      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
517

518
519
  close(handle);

Moritz Lipp's avatar
Moritz Lipp committed
520
  if (count != 0) {
521
    girara_error("Can not read from file descriptor.");
522
523
524
525
526
527
528
529
    g_unlink(file);
    g_free(file);
    return NULL;
  }

  return file;
}

Lukas K.'s avatar
Lukas K. committed
530
static gchar*
Sebastian Ramacher's avatar
Sebastian Ramacher committed
531
prepare_document_open_from_gfile(GFile* source)
Lukas K.'s avatar
Lukas K. committed
532
533
{
  gchar* file = NULL;
Sebastian Ramacher's avatar
Sebastian Ramacher committed
534
  GFileIOStream* iostream = NULL;
Lukas K.'s avatar
Lukas K. committed
535
536
537
  GError* error = NULL;

  GFile *tmpfile = g_file_new_tmp("zathura.gio.XXXXXX", &iostream, &error);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
538
  if (tmpfile == NULL) {
Lukas K.'s avatar
Lukas K. committed
539
540
541
542
543
544
545
546
    if (error != NULL) {
      girara_error("Can not create temporary file: %s", error->message);
      g_error_free(error);
    }
    return NULL;
  }

  gboolean rc = g_file_copy(source, tmpfile, G_FILE_COPY_OVERWRITE, NULL, NULL, NULL, &error);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
547
  if (rc == FALSE) {
Lukas K.'s avatar
Lukas K. committed
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
    if (error != NULL) {
      girara_error("Can not copy to temporary file: %s", error->message);
      g_error_free(error);
    }
    g_object_unref(iostream);
    g_object_unref(tmpfile);
    return NULL;
  }

  file = g_file_get_path(tmpfile);
  g_object_unref(iostream);
  g_object_unref(tmpfile);

  return file;
}

564
static gboolean
Moritz Lipp's avatar
Update    
Moritz Lipp committed
565
566
567
568
document_info_open(gpointer data)
{
  zathura_document_info_t* document_info = data;
  g_return_val_if_fail(document_info != NULL, FALSE);
Lukas K.'s avatar
Lukas K. committed
569
  char* uri = NULL;
Moritz Lipp's avatar
Update    
Moritz Lipp committed
570

571
  if (document_info->zathura != NULL && document_info->path != NULL) {
572
    char* file = NULL;
573
574
    if (g_strcmp0(document_info->path, "-") == 0 ||
        g_str_has_prefix(document_info->path, "/proc/self/fd/") == true) {
Sebastian Ramacher's avatar
Sebastian Ramacher committed
575
      file = prepare_document_open_from_stdin(document_info->path);
576
577
      if (file == NULL) {
        girara_notify(document_info->zathura->ui.session, GIRARA_ERROR,
578
                      _("Could not read file from stdin and write it to a temporary file."));
579
580
      } else {
        document_info->zathura->stdin_support.file = g_strdup(file);
581
582
      }
    } else {
Sebastian Ramacher's avatar
Sebastian Ramacher committed
583
584
585
586
      GFile* gf = g_file_new_for_commandline_arg(document_info->path);
      if (g_file_is_native(gf) == TRUE) {
        /* file was given as a native path */
        file = g_file_get_path(gf);
Lukas K.'s avatar
Lukas K. committed
587
588
      }
      else {
589
        /* copy file with GIO */
Lukas K.'s avatar
Lukas K. committed
590
        uri = g_file_get_uri(gf);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
591
        file = prepare_document_open_from_gfile(gf);
592
593
594
        if (file == NULL) {
          girara_notify(document_info->zathura->ui.session, GIRARA_ERROR,
                        _("Could not read file from GIO and copy it to a temporary file."));
Lukas K.'s avatar
Lukas K. committed
595
        } else {
596
          document_info->zathura->stdin_support.file = g_strdup(file);
Lukas K.'s avatar
Lukas K. committed
597
598
599
        }
      }
      g_object_unref(gf);
600
601
602
    }

    if (file != NULL) {
603
      if (document_info->synctex != NULL) {
Lukas K.'s avatar
Lukas K. committed
604
        document_open_synctex(document_info->zathura, file, uri, 
605
606
                              document_info->password, document_info->synctex);
      } else {
Lukas K.'s avatar
Lukas K. committed
607
        document_open(document_info->zathura, file, uri, document_info->password,
608
609
                      document_info->page_number);
      }
610
      g_free(file);
Lukas K.'s avatar
Lukas K. committed
611
      g_free(uri);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
612
613
614
615
616
617
618
619
620
621
622
623

      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);
        }
      }
624
    }
Moritz Lipp's avatar
Update    
Moritz Lipp committed
625
626
  }

Sebastian Ramacher's avatar
Sebastian Ramacher committed
627
  free_document_info(document_info);
Moritz Lipp's avatar
Update    
Moritz Lipp committed
628
  return FALSE;
629
630
}

Lukas K.'s avatar
Lukas K. committed
631
632
char*
get_formatted_filename(zathura_t* zathura, bool statusbar)
633
634
{
  bool basename_only = false;
Lukas K.'s avatar
Lukas K. committed
635
  const char* file_path = zathura_document_get_uri(zathura->document);
636
  if (file_path == NULL) {
Lukas K.'s avatar
Lukas K. committed
637
638
    file_path = zathura_document_get_path(zathura->document);
  }
Sebastian Ramacher's avatar
Sebastian Ramacher committed
639
  if (statusbar == true) {
640
    girara_setting_get(zathura->ui.session, "statusbar-basename", &basename_only);
Lukas K.'s avatar
Lukas K. committed
641
642
  } else {
    girara_setting_get(zathura->ui.session, "window-title-basename", &basename_only);
643
644
  }

645
646
  if (basename_only == false) {
    bool home_tilde = false;
647
648
649
650
651
652
    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
653
    const size_t file_path_len = file_path ? strlen(file_path) : 0;
654

Sebastian Ramacher's avatar
Sebastian Ramacher committed
655
656
657
    if (home_tilde == true) {
      char* home = girara_get_home_directory(NULL);
      const size_t home_len = home ? strlen(home) : 0;
658

Sidharth Kapur's avatar
Sidharth Kapur committed
659
660
661
662
      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
663
        g_free(home);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
664
        return g_strdup_printf("~%s", &file_path[home_len]);
665
      } else {
Sebastian Ramacher's avatar
Sebastian Ramacher committed
666
        g_free(home);
667
        return g_strdup(file_path);
668
669
      }
    } else {
670
      return g_strdup(file_path);
671
672
    }
  } else {
673
    const char* basename = zathura_document_get_basename(zathura->document);
674
    return g_strdup(basename);
675
676
677
  }
}

678
679
680
681
682
683
684
685
686
687
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
688
bool
Lukas K.'s avatar
Lukas K. committed
689
document_open(zathura_t* zathura, const char* path, const char* uri, const char* password,
690
              int page_number)
Moritz Lipp's avatar
Moritz Lipp committed
691
{
692
  if (zathura == NULL || zathura->plugins.manager == NULL || path == NULL) {
693
    goto error_out;
Moritz Lipp's avatar
Moritz Lipp committed
694
695
  }

696
  gchar* file_uri = NULL;
697
  zathura_error_t error = ZATHURA_ERROR_OK;
Lukas K.'s avatar
Lukas K. committed
698
  zathura_document_t* document = zathura_document_open(zathura->plugins.manager, path, uri, password, &error);
Moritz Lipp's avatar
Moritz Lipp committed
699

Moritz Lipp's avatar
Moritz Lipp committed
700
  if (document == NULL) {
701
    if (error == ZATHURA_ERROR_INVALID_PASSWORD) {
702
      girara_debug("Invalid or no password.");
703
704
705
      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;
Sebastian Ramacher's avatar
Sebastian Ramacher committed
706
        password_dialog_info->path = g_strdup(path);
Lukas K.'s avatar
Lukas K. committed
707
        password_dialog_info->uri = g_strdup(uri);
708

Sebastian Ramacher's avatar
Sebastian Ramacher committed
709
        if (password_dialog_info->path != NULL) {
710
          gdk_threads_add_idle(document_open_password_dialog, password_dialog_info);
711
712
713
714
715
716
717
          goto error_out;
        } else {
          free(password_dialog_info);
        }
      }
      goto error_out;
    }
Moritz Lipp's avatar
Moritz Lipp committed
718
719
720
    if (error == ZATHURA_ERROR_OK ) {
      girara_notify(zathura->ui.session, GIRARA_ERROR, _("Unsupported file type. Please install the necessary plugin."));
    }
721
    goto error_out;
Moritz Lipp's avatar
Moritz Lipp committed
722
  }
Lukas K.'s avatar
Lukas K. committed
723
724
  
  zathura->document = document;
Moritz Lipp's avatar
Moritz Lipp committed
725

726
727
  const char* file_path        = zathura_document_get_path(document);
  unsigned int number_of_pages = zathura_document_get_number_of_pages(document);
728

729
730
731
732
733
734
  if (number_of_pages == 0) {
    girara_notify(zathura->ui.session, GIRARA_WARNING,
        _("Document does not contain any pages"));
    goto error_free;
  }

735
  /* read history file */
736
737
738
739
740
741
  zathura_fileinfo_t file_info = {
    .current_page = 0,
    .page_offset = 0,
    .scale = 1,
    .rotation = 0,
    .pages_per_row = 0,
Sebastian Ramacher's avatar
Sebastian Ramacher committed
742
    .first_page_column_list = NULL,
743
744
745
    .position_x = 0,
    .position_y = 0
  };
746
747
748
749
  bool known_file = false;
  if (zathura->database != NULL) {
    known_file = zathura_db_get_fileinfo(zathura->database, file_path, &file_info);
  }
750

Moritz Lipp's avatar
Moritz Lipp committed
751
  /* set page offset */
752
  zathura_document_set_page_offset(document, file_info.page_offset);
753

754
  /* check for valid scale value */
755
756
757
  if (file_info.scale <= DBL_EPSILON) {
    file_info.scale = 1;
  }
Moritz Lipp's avatar
Moritz Lipp committed
758
759
  zathura_document_set_scale(document,
      zathura_correct_scale_value(zathura->ui.session, file_info.scale));
760
761

  /* check current page number */
762
763
764
765
766
767
  /* 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) {
768
769
    girara_warning("document info: '%s' has an invalid page number", file_path);
    zathura_document_set_current_page_number(document, 0);
770
  } else {
771
    zathura_document_set_current_page_number(document, page_number);
772
773
774
  }

  /* check for valid rotation */
775
  if (file_info.rotation % 90 != 0) {
776
777
778
    girara_warning("document info: '%s' has an invalid rotation", file_path);
    zathura_document_set_rotation(document, 0);
  } else {
779
    zathura_document_set_rotation(document, file_info.rotation % 360);
780
781
782
783
784
785
786
787
788
789
790
  }

  /* 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";
791
  if (known_file == false && girara_setting_get(zathura->ui.session, "adjust-open", &(adjust_open)) == true) {
792
793
794
795
796
797
798
799
    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);
800
801
  } else {
    zathura_document_set_adjust_mode(document, ZATHURA_ADJUST_NONE);
802
803
  }

804
805
806
807
808
  /* 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;

809
  /* update statusbar */
Lukas K.'s avatar
Lukas K. committed
810
  char* filename = get_formatted_filename(zathura, true);
811
812
  girara_statusbar_item_set_text(zathura->ui.session, zathura->ui.statusbar.file, filename);
  g_free(filename);
813

Moritz Lipp's avatar
Moritz Lipp committed
814
  /* install file monitor */
815
  file_uri = g_filename_to_uri(file_path, NULL, NULL);
Moritz Lipp's avatar
Moritz Lipp committed
816
817
818
819
820
  if (file_uri == NULL) {
    goto error_free;
  }

  if (zathura->file_monitor.file == NULL) {
821
822
823
824
    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
825
826
827
  }

  if (zathura->file_monitor.monitor == NULL) {
828
829
830
831
832
    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
833
834
835
  }

  if (zathura->file_monitor.file_path == NULL) {
836
    zathura->file_monitor.file_path = g_strdup(file_path);
837
838
839
    if (zathura->file_monitor.file_path == NULL) {
      goto error_free;
    }
Moritz Lipp's avatar
Moritz Lipp committed
840
841
  }

842
  if (password != NULL) {
843
    g_free(zathura->file_monitor.password);
844
    zathura->file_monitor.password = g_strdup(password);
Moritz Lipp's avatar
Moritz Lipp committed
845
846
847
848
849
    if (zathura->file_monitor.password == NULL) {
      goto error_free;
    }
  }

Moritz Lipp's avatar
Moritz Lipp committed
850
851
852
853
854
855
  /* create marks list */
  zathura->global.marks = girara_list_new2((girara_free_function_t) mark_free);
  if (zathura->global.marks == NULL) {
    goto error_free;
  }

856
857
858
859
860
861
862
863
864
  /* 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;
  }

865
  /* threads */
866
  zathura->sync.render_thread = zathura_renderer_new(cache_size);
867
868
869
870
871

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

Sebastian Ramacher's avatar
Sebastian Ramacher committed
872
  /* set up recolor info in ZathuraRenderer */
873
874
875
876
877
878
879
880
881
  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);

882
883
884
885
886
  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);
887
888
  girara_setting_get(zathura->ui.session, "recolor-reverse-video", &recolor);
  zathura_renderer_enable_recolor_reverse_video(zathura->sync.render_thread, recolor);
889

890
891
892
893
894
895
  /* 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));

896
  const unsigned int view_width = (unsigned int)floor(gtk_adjustment_get_page_size(hadjustment));
897
  zathura_document_set_viewport_width(zathura->document, view_width);
898
  const unsigned int view_height = (unsigned int)floor(gtk_adjustment_get_page_size(vadjustment));
899
900
  zathura_document_set_viewport_height(zathura->document, view_height);

901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
  /* 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
918
    g_object_ref(page_widget);
919
920
    zathura->pages[page_id] = page_widget;

921
922
923
    gtk_widget_set_halign(page_widget, GTK_ALIGN_CENTER);
    gtk_widget_set_valign(page_widget, GTK_ALIGN_CENTER);

924
925
926
    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",
927
        G_CALLBACK(cb_page_widget_image_selected), zathura);
928
929
930
931
    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);
932
933
    g_signal_connect(G_OBJECT(page_widget), "scaled-button-release",
        G_CALLBACK(cb_page_widget_scaled_button_release), zathura);
934
935
  }

936
  /* view mode */
937
  unsigned int pages_per_row = 1;
Sebastian Ramacher's avatar
Sebastian Ramacher committed
938
  char* first_page_column_list = NULL;
939
940
941
942
  unsigned int page_padding = 1;

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

943
944
945
946
947
  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
948

949
  /* read first_page_column list */
Sebastian Ramacher's avatar
Sebastian Ramacher committed
950
  if (file_info.first_page_column_list != NULL && strcmp(file_info.first_page_column_list, "")) {
951
    first_page_column_list = file_info.first_page_column_list;
Sebastian Ramacher's avatar
Sebastian Ramacher committed
952
    file_info.first_page_column_list = NULL;
953
  } else {
954
    girara_setting_get(zathura->ui.session, "first-page-column", &first_page_column_list);
955
956
  }

957
958
959
  /* find value for first_page_column */
  unsigned int first_page_column = find_first_page_column(first_page_column_list, pages_per_row);

Moritz Lipp's avatar
Moritz Lipp committed
960
  girara_setting_set(zathura->ui.session, "pages-per-row", &pages_per_row);
961
  girara_setting_set(zathura->ui.session, "first-page-column", first_page_column_list);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
962
  g_free(file_info.first_page_column_list);
963
  g_free(first_page_column_list);
964
965
966

  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
967

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

Moritz Lipp's avatar
Moritz Lipp committed
970

971
  /* bookmarks */
972
973
  if (zathura->database != NULL) {
    if (zathura_bookmarks_load(zathura, file_path) == false) {
974
      girara_debug("Failed to load bookmarks.");
975
    }
976

977
978
979
980
    /* jumplist */
    if (zathura_jumplist_load(zathura, file_path) == false) {
      zathura->jumplist.list = girara_list_new2(g_free);
    }
981
982
  }

Moritz Lipp's avatar
Moritz Lipp committed
983
  /* update title */
Lukas K.'s avatar
Lukas K. committed
984
  char* formatted_filename = get_formatted_filename(zathura, false);
985
986
  girara_set_window_title(zathura->ui.session, formatted_filename);
  g_free(formatted_filename);
Moritz Lipp's avatar
Moritz Lipp committed
987

988
  g_free(file_uri);
Moritz Lipp's avatar
Moritz Lipp committed
989

990
  /* adjust_view */
991
  adjust_view(zathura);
992
993
994
995
996
997
998
999
1000
  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
1001
1002
    const double height = zathura_page_get_height(page);
    const double width = zathura_page_get_width(page);
1003
1004
1005
1006
1007
1008
    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]);
  }
1009

1010
  /* Set page */
1011
  page_set(zathura, zathura_document_get_current_page_number(document));
1012
1013